From e9fe8c6496f2e5333c985406b16138e76a04e301 Mon Sep 17 00:00:00 2001 From: codewithgun Date: Fri, 17 Jan 2025 20:16:16 +0800 Subject: [PATCH 1/2] fix: remove unused dependency --- ts-client/package.json | 4 +- ts-client/src/dlmm/helpers/index.ts | 47 ++- ts-client/src/dlmm/helpers/weight.ts | 357 ++---------------- ts-client/src/dlmm/index.ts | 356 +++++++++-------- .../src/test/calculate_distribution.test.ts | 302 +-------------- 5 files changed, 270 insertions(+), 796 deletions(-) diff --git a/ts-client/package.json b/ts-client/package.json index f881b84d..20f3a7c0 100644 --- a/ts-client/package.json +++ b/ts-client/package.json @@ -35,14 +35,12 @@ "dependencies": { "@coral-xyz/anchor": "^0.28.0", "@coral-xyz/borsh": "^0.28.0", - "@solana-developers/helpers": "^2.5.6", "@solana/buffer-layout": "^4.0.1", "@solana/spl-token": "^0.4.6", "@solana/web3.js": "^1.91.6", "bn.js": "^5.2.1", "decimal.js": "^10.4.2", - "express": "^4.19.2", - "gaussian": "^1.3.0" + "express": "^4.19.2" }, "keywords": [], "author": "McSam", diff --git a/ts-client/src/dlmm/helpers/index.ts b/ts-client/src/dlmm/helpers/index.ts index bb674bb6..37baada4 100644 --- a/ts-client/src/dlmm/helpers/index.ts +++ b/ts-client/src/dlmm/helpers/index.ts @@ -13,15 +13,17 @@ import { } from "@solana/spl-token"; import { SCALE_OFFSET } from "../constants"; import { + AddressLookupTableAccount, ComputeBudgetProgram, Connection, PublicKey, SystemProgram, TransactionInstruction, + TransactionMessage, + VersionedTransaction, } from "@solana/web3.js"; import { Bin, ClmmProgram, GetOrCreateATAResponse } from "../types"; import { Rounding, mulShr, shlDiv } from "./math"; -import { getSimulationComputeUnits } from "@solana-developers/helpers"; import { MAX_CU_BUFFER, MIN_CU_BUFFER } from "./computeUnit"; export * from "./derive"; @@ -38,11 +40,7 @@ export function chunks(array: T[], size: number): T[][] { ); } -export function range( - min: number, - max: number, - mapfn: (i: number) => T -) { +export function range(min: number, max: number, mapfn: (i: number) => T) { const length = max - min + 1; return Array.from({ length }, (_, i) => mapfn(min + i)); } @@ -209,6 +207,43 @@ export async function chunkedGetMultipleAccountInfos( return accountInfos; } +export const getSimulationComputeUnits = async ( + connection: Connection, + instructions: Array, + payer: PublicKey, + lookupTables: Array | [] +): Promise => { + const testInstructions = [ + // Set an arbitrarily high number in simulation + // so we can be sure the transaction will succeed + // and get the real compute units used + ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }), + ...instructions, + ]; + + const testTransaction = new VersionedTransaction( + new TransactionMessage({ + instructions: testInstructions, + payerKey: payer, + // RecentBlockhash can by any public key during simulation + // since 'replaceRecentBlockhash' is set to 'true' below + recentBlockhash: PublicKey.default.toString(), + }).compileToV0Message(lookupTables) + ); + + const rpcResponse = await connection.simulateTransaction(testTransaction, { + replaceRecentBlockhash: true, + sigVerify: false, + }); + + if (rpcResponse?.value?.err) { + const logs = rpcResponse.value.logs?.join("\n • ") || "No logs available"; + throw new Error(`Transaction simulation failed:\n •${logs}`); + } + + return rpcResponse.value.unitsConsumed || null; +}; + /** * Gets the estimated compute unit usage with a buffer. * @param connection A Solana connection object. diff --git a/ts-client/src/dlmm/helpers/weight.ts b/ts-client/src/dlmm/helpers/weight.ts index a2c396d5..14e6164e 100644 --- a/ts-client/src/dlmm/helpers/weight.ts +++ b/ts-client/src/dlmm/helpers/weight.ts @@ -1,78 +1,17 @@ import { BN } from "@coral-xyz/anchor"; -import gaussian, { Gaussian } from "gaussian"; import { BASIS_POINT_MAX } from "../constants"; import Decimal from "decimal.js"; -import { toAmountAskSide, toAmountBidSide, toAmountBothSide } from "./weightToAmounts"; +import { + toAmountAskSide, + toAmountBidSide, + toAmountBothSide, +} from "./weightToAmounts"; export function getPriceOfBinByBinId(binId: number, binStep: number): Decimal { const binStepNum = new Decimal(binStep).div(new Decimal(BASIS_POINT_MAX)); return new Decimal(1).add(new Decimal(binStepNum)).pow(new Decimal(binId)); } -/// Build a gaussian distribution from the bins, with active bin as the mean. -function buildGaussianFromBins(activeBin: number, binIds: number[]) { - const smallestBin = Math.min(...binIds); - const largestBin = Math.max(...binIds); - - // Define the Gaussian distribution. The mean will be active bin when active bin is within the bin ids. Else, use left or right most bin id as the mean. - let mean = 0; - const isAroundActiveBin = binIds.find((bid) => bid == activeBin); - // The liquidity will be distributed surrounding active bin - if (isAroundActiveBin) { - mean = activeBin; - } - // The liquidity will be distributed to the right side of the active bin. - else if (activeBin < smallestBin) { - mean = smallestBin; - } - // The liquidity will be distributed to the left side of the active bin. - else { - mean = largestBin; - } - - const TWO_STANDARD_DEVIATION = 4; - const stdDev = (largestBin - smallestBin) / TWO_STANDARD_DEVIATION; - const variance = Math.max(stdDev ** 2, 1); - - return gaussian(mean, variance); -} - -/// Find the probability of the bin id over the gaussian. The probability ranged from 0 - 1 and will be used as liquidity allocation for that particular bin. -function generateBinLiquidityAllocation( - gaussian: Gaussian, - binIds: number[], - invert: boolean -) { - const allocations = binIds.map((bid) => - invert ? 1 / gaussian.pdf(bid) : gaussian.pdf(bid) - ); - const totalAllocations = allocations.reduce((acc, v) => acc + v, 0); - // Gaussian impossible to cover 100%, normalized it to have total of 100% - return allocations.map((a) => a / totalAllocations); -} - -/// Convert liquidity allocation from 0..1 to 0..10000 bps unit. The sum of allocations must be 1. Return BPS and the loss after conversion. -function computeAllocationBps(allocations: number[]): { - bpsAllocations: BN[]; - pLoss: BN; -} { - let totalAllocation = new BN(0); - const bpsAllocations: BN[] = []; - - for (const allocation of allocations) { - const allocBps = new BN(allocation * 10000); - bpsAllocations.push(allocBps); - totalAllocation = totalAllocation.add(allocBps); - } - - const pLoss = new BN(10000).sub(totalAllocation); - return { - bpsAllocations, - pLoss, - }; -} -/** private */ - export function toWeightDistribution( amountX: BN, amountY: BN, @@ -137,15 +76,15 @@ export function calculateSpotDistribution( const distributions = binIds[0] < activeBin ? binIds.map((binId) => ({ - binId, - xAmountBpsOfTotal: new BN(0), - yAmountBpsOfTotal: dist, - })) + binId, + xAmountBpsOfTotal: new BN(0), + yAmountBpsOfTotal: dist, + })) : binIds.map((binId) => ({ - binId, - xAmountBpsOfTotal: dist, - yAmountBpsOfTotal: new BN(0), - })); + binId, + xAmountBpsOfTotal: dist, + yAmountBpsOfTotal: new BN(0), + })); // Add the loss to the left most bin if (binIds[0] < activeBin) { @@ -202,268 +141,12 @@ export function calculateSpotDistribution( }); } -export function calculateBidAskDistribution( - activeBin: number, - binIds: number[] -): { - binId: number; - xAmountBpsOfTotal: BN; - yAmountBpsOfTotal: BN; -}[] { - const smallestBin = Math.min(...binIds); - const largestBin = Math.max(...binIds); - - const rightOnly = activeBin < smallestBin; - const leftOnly = activeBin > largestBin; - - const gaussian = buildGaussianFromBins(activeBin, binIds); - const allocations = generateBinLiquidityAllocation(gaussian, binIds, true); - - // To the right of active bin, liquidity distribution consists of only token X. - if (rightOnly) { - const { bpsAllocations, pLoss } = computeAllocationBps(allocations); - const binDistributions = binIds.map((bid, idx) => ({ - binId: bid, - xAmountBpsOfTotal: bpsAllocations[idx], - yAmountBpsOfTotal: new BN(0), - })); - const idx = binDistributions.length - 1; - binDistributions[idx].xAmountBpsOfTotal = - binDistributions[idx].xAmountBpsOfTotal.add(pLoss); - return binDistributions; - } - - // To the left of active bin, liquidity distribution consists of only token Y. - if (leftOnly) { - const { bpsAllocations, pLoss } = computeAllocationBps(allocations); - const binDistributions = binIds.map((bid, idx) => ({ - binId: bid, - xAmountBpsOfTotal: new BN(0), - yAmountBpsOfTotal: bpsAllocations[idx], - })); - binDistributions[0].yAmountBpsOfTotal = - binDistributions[0].yAmountBpsOfTotal.add(pLoss); - return binDistributions; - } - - // Find total X, and Y bps allocations for normalization. - const [totalXAllocation, totalYAllocation] = allocations.reduce( - ([xAcc, yAcc], allocation, idx) => { - const binId = binIds[idx]; - if (binId > activeBin) { - return [xAcc + allocation, yAcc]; - } else if (binId < activeBin) { - return [xAcc, yAcc + allocation]; - } else { - const half = allocation / 2; - return [xAcc + half, yAcc + half]; - } - }, - [0, 0] - ); - - // Normalize and convert to BPS - const [normXAllocations, normYAllocations] = allocations.reduce<[BN[], BN[]]>( - ([xAllocations, yAllocations], allocation, idx) => { - const binId = binIds[idx]; - if (binId > activeBin) { - const distX = new BN((allocation * 10000) / totalXAllocation); - xAllocations.push(distX); - } - if (binId < activeBin) { - const distY = new BN((allocation * 10000) / totalYAllocation); - yAllocations.push(distY); - } - if (binId == activeBin) { - const half = allocation / 2; - const distX = new BN((half * 10000) / totalXAllocation); - const distY = new BN((half * 10000) / totalYAllocation); - xAllocations.push(distX); - yAllocations.push(distY); - } - return [xAllocations, yAllocations]; - }, - [[], []] - ); - - const totalXNormAllocations = normXAllocations.reduce( - (acc, v) => acc.add(v), - new BN(0) - ); - const totalYNormAllocations = normYAllocations.reduce( - (acc, v) => acc.add(v), - new BN(0) - ); - - const xPLoss = new BN(10000).sub(totalXNormAllocations); - const yPLoss = new BN(10000).sub(totalYNormAllocations); - - const distributions = binIds.map((binId) => { - if (binId === activeBin) { - return { - binId, - xAmountBpsOfTotal: normXAllocations.shift(), - yAmountBpsOfTotal: normYAllocations.shift(), - }; - } - - if (binId > activeBin) { - return { - binId, - xAmountBpsOfTotal: normXAllocations.shift(), - yAmountBpsOfTotal: new BN(0), - }; - } - - if (binId < activeBin) { - return { - binId, - xAmountBpsOfTotal: new BN(0), - yAmountBpsOfTotal: normYAllocations.shift(), - }; - } - }); - - if (!yPLoss.isZero()) { - distributions[0].yAmountBpsOfTotal = - distributions[0].yAmountBpsOfTotal.add(yPLoss); - } - - if (!xPLoss.isZero()) { - const last = distributions.length - 1; - distributions[last].xAmountBpsOfTotal = - distributions[last].xAmountBpsOfTotal.add(xPLoss); - } - - return distributions; -} - -export function calculateNormalDistribution( - activeBin: number, - binIds: number[] -): { - binId: number; - xAmountBpsOfTotal: BN; - yAmountBpsOfTotal: BN; -}[] { - const smallestBin = Math.min(...binIds); - const largestBin = Math.max(...binIds); - - const rightOnly = activeBin < smallestBin; - const leftOnly = activeBin > largestBin; - - const gaussian = buildGaussianFromBins(activeBin, binIds); - const allocations = generateBinLiquidityAllocation(gaussian, binIds, false); - - // To the right of active bin, liquidity distribution consists of only token X. - if (rightOnly) { - const { bpsAllocations, pLoss } = computeAllocationBps(allocations); - const binDistributions = binIds.map((bid, idx) => ({ - binId: bid, - xAmountBpsOfTotal: bpsAllocations[idx], - yAmountBpsOfTotal: new BN(0), - })); - // When contains only X token, bin closest to active bin will be index 0. - // Add back the precision loss - binDistributions[0].xAmountBpsOfTotal = - binDistributions[0].xAmountBpsOfTotal.add(pLoss); - return binDistributions; - } - - // To the left of active bin, liquidity distribution consists of only token Y. - if (leftOnly) { - const { bpsAllocations, pLoss } = computeAllocationBps(allocations); - const binDistributions = binIds.map((bid, idx) => ({ - binId: bid, - xAmountBpsOfTotal: new BN(0), - yAmountBpsOfTotal: bpsAllocations[idx], - })); - // When contains only Y token, bin closest to active bin will be last index. - // Add back the precision loss - const idx = binDistributions.length - 1; - binDistributions[idx].yAmountBpsOfTotal = - binDistributions[idx].yAmountBpsOfTotal.add(pLoss); - return binDistributions; - } - - // The liquidity distribution consists of token X and Y. Allocations from gaussian only says how much liquidity percentage per bin over the full bin range. - // Normalize liquidity allocation percentage into X - 100%, Y - 100%. - - // Find total X, and Y bps allocations for normalization. - const [totalXAllocation, totalYAllocation] = allocations.reduce( - ([xAcc, yAcc], allocation, idx) => { - const binId = binIds[idx]; - if (binId > activeBin) { - return [xAcc + allocation, yAcc]; - } else if (binId < activeBin) { - return [xAcc, yAcc + allocation]; - } else { - const half = allocation / 2; - return [xAcc + half, yAcc + half]; - } - }, - [0, 0] - ); - - // Normalize and convert to BPS - const [normXAllocations, normYAllocations] = allocations.reduce( - ([xAllocations, yAllocations], allocation, idx) => { - const binId = binIds[idx]; - if (binId > activeBin) { - const distX = new BN((allocation * 10000) / totalXAllocation); - xAllocations.push(distX); - } - if (binId < activeBin) { - const distY = new BN((allocation * 10000) / totalYAllocation); - yAllocations.push(distY); - } - return [xAllocations, yAllocations]; - }, - [[], []] - ); - - const normXActiveBinAllocation = normXAllocations.reduce( - (maxBps, bps) => maxBps.sub(bps), - new BN(10_000) - ); - const normYActiveBinAllocation = normYAllocations.reduce( - (maxBps, bps) => maxBps.sub(bps), - new BN(10_000) - ); - - return binIds.map((binId) => { - if (binId === activeBin) { - return { - binId, - xAmountBpsOfTotal: normXActiveBinAllocation, - yAmountBpsOfTotal: normYActiveBinAllocation, - }; - } - - if (binId > activeBin) { - return { - binId, - xAmountBpsOfTotal: normXAllocations.shift(), - yAmountBpsOfTotal: new BN(0), - }; - } - - if (binId < activeBin) { - return { - binId, - xAmountBpsOfTotal: new BN(0), - yAmountBpsOfTotal: normYAllocations.shift(), - }; - } - }); -} - export function fromWeightDistributionToAmountOneSide( amount: BN, distributions: { binId: number; weight: number }[], binStep: number, activeId: number, - depositForY: boolean, + depositForY: boolean ): { binId: number; amount: BN }[] { if (depositForY) { return toAmountBidSide(activeId, amount, distributions); @@ -513,5 +196,13 @@ export function fromWeightDistributionToAmount( }; }); } - return toAmountBothSide(activeId, binStep, amountX, amountY, amountXInActiveBin, amountYInActiveBin, distributions); -} \ No newline at end of file + return toAmountBothSide( + activeId, + binStep, + amountX, + amountY, + amountXInActiveBin, + amountYInActiveBin, + distributions + ); +} diff --git a/ts-client/src/dlmm/index.ts b/ts-client/src/dlmm/index.ts index e5a1198c..eed2089b 100644 --- a/ts-client/src/dlmm/index.ts +++ b/ts-client/src/dlmm/index.ts @@ -154,7 +154,7 @@ export class DLMM { public tokenY: TokenReserve, public clock: Clock, private opt?: Opt - ) { } + ) {} /** Static public method */ @@ -466,7 +466,7 @@ export class DLMM { reserveAndTokenMintAccountsInfo[reservePublicKeys.length + index * 2]; const tokenYMintAccountInfo = reserveAndTokenMintAccountsInfo[ - reservePublicKeys.length + index * 2 + 1 + reservePublicKeys.length + index * 2 + 1 ]; if (!reserveXAccountInfo || !reserveYAccountInfo) @@ -600,15 +600,8 @@ export class DLMM { const positionBinArraysMapV2 = new Map(); - for ( - let i = 0; - i < binArrayPubkeyArrayV2.length; - i++ - ) { - const binArrayPubkey = - binArrayPubkeyArrayV2[ - i - ]; + for (let i = 0; i < binArrayPubkeyArrayV2.length; i++) { + const binArrayPubkey = binArrayPubkeyArrayV2[i]; const binArrayAccInfoBufferV2 = binArraysAccInfo[i]; if (binArrayAccInfoBufferV2) { const binArrayAccInfo = program.coder.accounts.decode( @@ -621,15 +614,11 @@ export class DLMM { const lbPairArraysMapV2 = new Map(); for ( - let i = - binArrayPubkeyArrayV2.length; + let i = binArrayPubkeyArrayV2.length; i < binArraysAccInfo.length; i++ ) { - const lbPairPubkey = - lbPairArrayV2[ - i - binArrayPubkeyArrayV2.length - ]; + const lbPairPubkey = lbPairArrayV2[i - binArrayPubkeyArrayV2.length]; const lbPairAccInfoBufferV2 = binArraysAccInfo[i]; if (!lbPairAccInfoBufferV2) throw new Error(`LB Pair account ${lbPairPubkey.toBase58()} not found`); @@ -664,10 +653,8 @@ export class DLMM { >(); lbPairArrayV2.forEach((lbPair, idx) => { const index = idx * 4; - const reserveAccBufferXV2 = - reserveAccountsInfo[index]; - const reserveAccBufferYV2 = - reserveAccountsInfo[index + 1]; + const reserveAccBufferXV2 = reserveAccountsInfo[index]; + const reserveAccBufferYV2 = reserveAccountsInfo[index + 1]; if (!reserveAccBufferXV2 || !reserveAccBufferYV2) throw new Error( `Reserve account for LB Pair ${lbPair.toBase58()} not found` @@ -680,10 +667,8 @@ export class DLMM { reserveY: reserveAccY.amount, }); - const mintXBufferV2 = - reserveAccountsInfo[index + 2]; - const mintYBufferV2 = - reserveAccountsInfo[index + 3]; + const mintXBufferV2 = reserveAccountsInfo[index + 2]; + const mintYBufferV2 = reserveAccountsInfo[index + 3]; if (!mintXBufferV2 || !mintYBufferV2) throw new Error( `Mint account for LB Pair ${lbPair.toBase58()} not found` @@ -758,32 +743,35 @@ export class DLMM { amount: reserveYBalance, decimal: quoteTokenDecimal, }; - const positionData = !!lowerBinArray && !!upperBinArray ? await DLMM.processPosition( - program, - PositionVersion.V2, - lbPairAcc, - onChainTimestamp, - account, - baseTokenDecimal, - quoteTokenDecimal, - lowerBinArray, - upperBinArray, - feeOwner - ) : { - totalXAmount: '0', - totalYAmount: '0', - positionBinData: [], - lastUpdatedAt: new BN(0), - upperBinId, - lowerBinId, - feeX: new BN(0), - feeY: new BN(0), - rewardOne: new BN(0), - rewardTwo: new BN(0), - feeOwner, - totalClaimedFeeXAmount: new BN(0), - totalClaimedFeeYAmount: new BN(0), - }; + const positionData = + !!lowerBinArray && !!upperBinArray + ? await DLMM.processPosition( + program, + PositionVersion.V2, + lbPairAcc, + onChainTimestamp, + account, + baseTokenDecimal, + quoteTokenDecimal, + lowerBinArray, + upperBinArray, + feeOwner + ) + : { + totalXAmount: "0", + totalYAmount: "0", + positionBinData: [], + lastUpdatedAt: new BN(0), + upperBinId, + lowerBinId, + feeX: new BN(0), + feeY: new BN(0), + rewardOne: new BN(0), + rewardTwo: new BN(0), + feeOwner, + totalClaimedFeeXAmount: new BN(0), + totalClaimedFeeYAmount: new BN(0), + }; if (positionData) { positionsMap.set(lbPair.toBase58(), { @@ -1458,20 +1446,20 @@ export class DLMM { const promiseResults = await Promise.all([ this.getActiveBin(), userPubKey && - this.program.account.positionV2.all([ - { - memcmp: { - bytes: bs58.encode(userPubKey.toBuffer()), - offset: 8 + 32, + this.program.account.positionV2.all([ + { + memcmp: { + bytes: bs58.encode(userPubKey.toBuffer()), + offset: 8 + 32, + }, }, - }, - { - memcmp: { - bytes: bs58.encode(this.pubkey.toBuffer()), - offset: 8, + { + memcmp: { + bytes: bs58.encode(this.pubkey.toBuffer()), + offset: 8, + }, }, - }, - ]), + ]), ]); const [activeBin, positionsV2] = promiseResults; @@ -1515,11 +1503,7 @@ export class DLMM { const lbPairAndBinArrays = await chunkedGetMultipleAccountInfos( this.program.provider.connection, - [ - this.pubkey, - SYSVAR_CLOCK_PUBKEY, - ...binArrayPubkeyArrayV2, - ] + [this.pubkey, SYSVAR_CLOCK_PUBKEY, ...binArrayPubkeyArrayV2] ); const [lbPairAccInfo, clockAccInfo, ...binArraysAccInfo] = @@ -1527,8 +1511,7 @@ export class DLMM { const positionBinArraysMapV2 = new Map(); for (let i = 0; i < binArraysAccInfo.length; i++) { - const binArrayPubkey = - binArrayPubkeyArrayV2[i]; + const binArrayPubkey = binArrayPubkeyArrayV2[i]; const binArrayAccBufferV2 = binArraysAccInfo[i]; if (!binArrayAccBufferV2) throw new Error( @@ -1685,9 +1668,13 @@ export class DLMM { * - `version`: The version of the position (in this case, `Position.V2`) */ public async getPosition(positionPubKey: PublicKey): Promise { - const positionAccountInfo = await this.program.account.positionV2.fetch(positionPubKey); + const positionAccountInfo = await this.program.account.positionV2.fetch( + positionPubKey + ); if (!positionAccountInfo) { - throw new Error(`Position account ${positionPubKey.toBase58()} not found`); + throw new Error( + `Position account ${positionPubKey.toBase58()} not found` + ); } const { lowerBinId, upperBinId, feeOwner } = positionAccountInfo; @@ -1704,21 +1691,19 @@ export class DLMM { this.program.programId ); - const [clockAccInfo, lowerBinArrayAccInfo, upperBinArrayAccInfo] = await chunkedGetMultipleAccountInfos( - this.program.provider.connection, - [ + const [clockAccInfo, lowerBinArrayAccInfo, upperBinArrayAccInfo] = + await chunkedGetMultipleAccountInfos(this.program.provider.connection, [ SYSVAR_CLOCK_PUBKEY, lowerBinArrayPubKey, upperBinArrayPubKey, - ] - ); + ]); if (!lowerBinArrayAccInfo || !upperBinArrayAccInfo) { return { publicKey: positionPubKey, positionData: { - totalXAmount: '0', - totalYAmount: '0', + totalXAmount: "0", + totalYAmount: "0", positionBinData: [], lastUpdatedAt: new BN(0), upperBinId, @@ -1732,7 +1717,7 @@ export class DLMM { totalClaimedFeeYAmount: new BN(0), }, version: PositionVersion.V2, - } + }; } const onChainTimestamp = new BN( @@ -1742,11 +1727,11 @@ export class DLMM { const lowerBinArray = this.program.coder.accounts.decode( "binArray", lowerBinArrayAccInfo.data - ) + ); const upperBinArray = this.program.coder.accounts.decode( "binArray", upperBinArrayAccInfo.data - ) + ); return { publicKey: positionPubKey, @@ -1763,7 +1748,7 @@ export class DLMM { feeOwner ), version: PositionVersion.V2, - } + }; } /** @@ -2700,11 +2685,17 @@ export class DLMM { }): Promise { const lowerBinIdToRemove = Math.min(...binIds); const upperBinIdToRemove = Math.max(...binIds); - const { lbPair, owner, feeOwner, lowerBinId: positionLowerBinId, liquidityShares } = await this.program.account.positionV2.fetch(position); + const { + lbPair, + owner, + feeOwner, + lowerBinId: positionLowerBinId, + liquidityShares, + } = await this.program.account.positionV2.fetch(position); - if (liquidityShares.every((share) => share.isZero())) { - throw new Error("No liquidity to remove"); - } + if (liquidityShares.every((share) => share.isZero())) { + throw new Error("No liquidity to remove"); + } const lowerBinArrayIndex = binIdToBinArrayIndex(new BN(positionLowerBinId)); const upperBinArrayIndex = lowerBinArrayIndex.add(new BN(1)); @@ -2856,7 +2847,11 @@ export class DLMM { : null; const removeLiquidityTx = await this.program.methods - .removeLiquidityByRange(lowerBinIdToRemove, upperBinIdToRemove, bps.toNumber()) + .removeLiquidityByRange( + lowerBinIdToRemove, + upperBinIdToRemove, + bps.toNumber() + ) .accounts({ position, lbPair, @@ -3252,7 +3247,7 @@ export class DLMM { const minOutAmount = actualOutAmount .mul(new BN(BASIS_POINT_MAX).sub(allowedSlippage)) .div(new BN(BASIS_POINT_MAX)); - + const endPrice = getPriceOfBinByBinId( lastFilledActiveBinId.toNumber(), this.lbPair.binStep @@ -3544,7 +3539,6 @@ export class DLMM { closeWrappedSOLIx && postInstructions.push(closeWrappedSOLIx); } - // TODO: needs some refinement in case binArray not yet initialized const binArrays: AccountMeta[] = binArraysPubkey.map((pubkey) => { return { @@ -4187,22 +4181,22 @@ export class DLMM { } /** - * The `seedLiquidity` function create multiple grouped instructions. The grouped instructions will be either [initialize bin array + initialize position instructions] or [deposit instruction] combination. - * @param - * - `payer`: The public key of the tx payer. - * - `base`: Base key - * - `seedAmount`: Token X lamport amount to be seeded to the pool. - * - `price`: TokenX/TokenY Price in UI format - * - `roundingUp`: Whether to round up the price - * - `positionOwner`: The owner of the position - * - `feeOwner`: Position fee owner - * - `operator`: Operator of the position. Operator able to manage the position on behalf of the position owner. However, liquidity withdrawal issue by the operator can only send to the position owner. - * - `lockReleasePoint`: The lock release point of the position. - * - `shouldSeedPositionOwner` (optional): Whether to send 1 lamport amount of token X to the position owner to prove ownership. - * - * The returned instructions need to be executed sequentially if it was separated into multiple transactions. - * @returns {Promise} - */ + * The `seedLiquidity` function create multiple grouped instructions. The grouped instructions will be either [initialize bin array + initialize position instructions] or [deposit instruction] combination. + * @param + * - `payer`: The public key of the tx payer. + * - `base`: Base key + * - `seedAmount`: Token X lamport amount to be seeded to the pool. + * - `price`: TokenX/TokenY Price in UI format + * - `roundingUp`: Whether to round up the price + * - `positionOwner`: The owner of the position + * - `feeOwner`: Position fee owner + * - `operator`: Operator of the position. Operator able to manage the position on behalf of the position owner. However, liquidity withdrawal issue by the operator can only send to the position owner. + * - `lockReleasePoint`: The lock release point of the position. + * - `shouldSeedPositionOwner` (optional): Whether to send 1 lamport amount of token X to the position owner to prove ownership. + * + * The returned instructions need to be executed sequentially if it was separated into multiple transactions. + * @returns {Promise} + */ public async seedLiquiditySingleBin( payer: PublicKey, base: PublicKey, @@ -4230,9 +4224,23 @@ export class DLMM { const lowerBinArrayIndex = binIdToBinArrayIndex(binId); const upperBinArrayIndex = lowerBinArrayIndex.add(new BN(1)); - const [lowerBinArray] = deriveBinArray(this.pubkey, lowerBinArrayIndex, this.program.programId); - const [upperBinArray] = deriveBinArray(this.pubkey, upperBinArrayIndex, this.program.programId); - const [positionPda] = derivePosition(this.pubkey, base, binId, new BN(1), this.program.programId); + const [lowerBinArray] = deriveBinArray( + this.pubkey, + lowerBinArrayIndex, + this.program.programId + ); + const [upperBinArray] = deriveBinArray( + this.pubkey, + upperBinArrayIndex, + this.program.programId + ); + const [positionPda] = derivePosition( + this.pubkey, + base, + binId, + new BN(1), + this.program.programId + ); const preInstructions = []; @@ -4273,11 +4281,16 @@ export class DLMM { if (isOverflowDefaultBinArrayBitmap(lowerBinArrayIndex)) { const bitmapExtensionAccount = accounts[3]; if (!bitmapExtensionAccount) { - preInstructions.push(await this.program.methods.initializeBinArrayBitmapExtension().accounts({ - binArrayBitmapExtension, - funder: payer, - lbPair: this.pubkey - }).instruction()); + preInstructions.push( + await this.program.methods + .initializeBinArrayBitmapExtension() + .accounts({ + binArrayBitmapExtension, + funder: payer, + lbPair: this.pubkey, + }) + .instruction() + ); } } else { binArrayBitmapExtension = this.program.programId; @@ -4295,20 +4308,39 @@ export class DLMM { ); if (shouldSeedPositionOwner) { - const positionOwnerTokenXAccount = await this.program.provider.connection.getAccountInfo(positionOwnerTokenX); + const positionOwnerTokenXAccount = + await this.program.provider.connection.getAccountInfo( + positionOwnerTokenX + ); if (positionOwnerTokenXAccount) { const account = AccountLayout.decode(positionOwnerTokenXAccount.data); if (account.amount == BigInt(0)) { // send 1 lamport to position owner token X to prove ownership - const transferIx = createTransferInstruction(operatorTokenX, positionOwnerTokenX, payer, 1); + const transferIx = createTransferInstruction( + operatorTokenX, + positionOwnerTokenX, + payer, + 1 + ); preInstructions.push(transferIx); } } else { - const createPositionOwnerTokenXIx = createAssociatedTokenAccountInstruction(payer, positionOwnerTokenX, positionOwner, this.lbPair.tokenXMint); + const createPositionOwnerTokenXIx = + createAssociatedTokenAccountInstruction( + payer, + positionOwnerTokenX, + positionOwner, + this.lbPair.tokenXMint + ); preInstructions.push(createPositionOwnerTokenXIx); // send 1 lamport to position owner token X to prove ownership - const transferIx = createTransferInstruction(operatorTokenX, positionOwnerTokenX, payer, 1); + const transferIx = createTransferInstruction( + operatorTokenX, + positionOwnerTokenX, + payer, + 1 + ); preInstructions.push(transferIx); } } @@ -4378,22 +4410,25 @@ export class DLMM { binLiquidityDist: [binLiquidityDist], }; - const depositLiquidityIx = await this.program.methods.addLiquidity(addLiquidityParams).accounts({ - position: positionPda, - lbPair: this.pubkey, - binArrayBitmapExtension, - userTokenX, - userTokenY, - reserveX: this.lbPair.reserveX, - reserveY: this.lbPair.reserveY, - tokenXMint: this.lbPair.tokenXMint, - tokenYMint: this.lbPair.tokenYMint, - binArrayLower: lowerBinArray, - binArrayUpper: upperBinArray, - sender: operator, - tokenXProgram: TOKEN_PROGRAM_ID, - tokenYProgram: TOKEN_PROGRAM_ID, - }).instruction(); + const depositLiquidityIx = await this.program.methods + .addLiquidity(addLiquidityParams) + .accounts({ + position: positionPda, + lbPair: this.pubkey, + binArrayBitmapExtension, + userTokenX, + userTokenY, + reserveX: this.lbPair.reserveX, + reserveY: this.lbPair.reserveY, + tokenXMint: this.lbPair.tokenXMint, + tokenYMint: this.lbPair.tokenYMint, + binArrayLower: lowerBinArray, + binArrayUpper: upperBinArray, + sender: operator, + tokenXProgram: TOKEN_PROGRAM_ID, + tokenYProgram: TOKEN_PROGRAM_ID, + }) + .instruction(); return [...preInstructions, depositLiquidityIx]; } @@ -4987,7 +5022,7 @@ export class DLMM { const activationPoint = !this.lbPair.preActivationSwapAddress.equals(PublicKey.default) && - this.lbPair.preActivationSwapAddress.equals(swapInitiator) + this.lbPair.preActivationSwapAddress.equals(swapInitiator) ? preActivationSwapPoint : this.lbPair.activationPoint; @@ -5426,33 +5461,42 @@ export class DLMM { const binArrayPubkeys = range( lowerBinArrayIndex.toNumber() + lowerBinArrayIndexOffset, upperBinArrayIndex.toNumber() + upperBinArrayIndexOffset, - i => deriveBinArray(lbPairPubKey, new BN(i), this.program.programId)[0] + (i) => deriveBinArray(lbPairPubKey, new BN(i), this.program.programId)[0] ); - const fetchedBinArrays = binArrayPubkeys.length !== 0 ? - await this.program.account.binArray.fetchMultiple(binArrayPubkeys) : []; + const fetchedBinArrays = + binArrayPubkeys.length !== 0 + ? await this.program.account.binArray.fetchMultiple(binArrayPubkeys) + : []; const binArrays = [ ...(hasCachedLowerBinArray ? [lowerBinArray] : []), ...fetchedBinArrays, - ...((hasCachedUpperBinArray && !isSingleBinArray) ? [upperBinArray] : []) + ...(hasCachedUpperBinArray && !isSingleBinArray ? [upperBinArray] : []), ]; - const binsById = new Map(binArrays - .filter(x => x != null) - .flatMap(({ bins, index }) => { - const [lowerBinId] = getBinArrayLowerUpperBinId(index); - return bins.map((b, i) => [lowerBinId.toNumber() + i, b] as [number, Bin]); - })); - const version = binArrays.find(binArray => binArray != null)?.version ?? 1; + const binsById = new Map( + binArrays + .filter((x) => x != null) + .flatMap(({ bins, index }) => { + const [lowerBinId] = getBinArrayLowerUpperBinId(index); + return bins.map( + (b, i) => [lowerBinId.toNumber() + i, b] as [number, Bin] + ); + }) + ); + const version = + binArrays.find((binArray) => binArray != null)?.version ?? 1; - return Array.from(enumerateBins( - binsById, - lowerBinId, - upperBinId, - this.lbPair.binStep, - baseTokenDecimal, - quoteTokenDecimal, - version, - )); + return Array.from( + enumerateBins( + binsById, + lowerBinId, + upperBinId, + this.lbPair.binStep, + baseTokenDecimal, + quoteTokenDecimal, + version + ) + ); } private async binArraysToBeCreate( @@ -5549,7 +5593,7 @@ export class DLMM { if (elapsed < sParameter.decayPeriod) { const decayedVolatilityReference = Math.floor( (vParameter.volatilityAccumulator * sParameter.reductionFactor) / - BASIS_POINT_MAX + BASIS_POINT_MAX ); vParameter.volatilityReference = decayedVolatilityReference; } else { diff --git a/ts-client/src/test/calculate_distribution.test.ts b/ts-client/src/test/calculate_distribution.test.ts index 04472d06..7071b6b9 100644 --- a/ts-client/src/test/calculate_distribution.test.ts +++ b/ts-client/src/test/calculate_distribution.test.ts @@ -1,17 +1,12 @@ import { BN } from "@coral-xyz/anchor"; +import babar from "babar"; +import Decimal from "decimal.js"; import { - calculateBidAskDistribution, - calculateNormalDistribution, calculateSpotDistribution, - toWeightDistribution, - fromWeightDistributionToAmount, getPriceOfBinByBinId, - fromWeightDistributionToAmountOneSide, toAmountsOneSideByStrategy, + toWeightDistribution, } from "../dlmm/helpers"; -import { StrategyType } from "../dlmm/types"; -import babar from "babar"; -import Decimal from "decimal.js"; import { compressBinAmount, distributeAmountToCompressedBinsByRatio, @@ -19,6 +14,7 @@ import { getC, getPositionCount, } from "../dlmm/helpers/math"; +import { StrategyType } from "../dlmm/types"; interface Distribution { binId: number; @@ -50,44 +46,6 @@ function debugDistributionChart(distributions: Distribution[]) { } describe("calculate_distribution", () => { - describe("consists of only 1 bin id", () => { - describe("when the deposit bin at the left of the active bin", () => { - const binIds = [-10000]; - const activeBin = -3333; - - const distributions = calculateNormalDistribution(activeBin, binIds); - - expect(distributions.length).toBe(1); - expect(distributions[0].binId).toBe(binIds[0]); - expect(distributions[0].xAmountBpsOfTotal.toNumber()).toBe(0); - expect(distributions[0].yAmountBpsOfTotal.toNumber()).toBe(10000); - }); - - describe("when the deposit bin at the right of the active bin", () => { - const binIds = [-2222]; - const activeBin = -3333; - - const distributions = calculateNormalDistribution(activeBin, binIds); - - expect(distributions.length).toBe(1); - expect(distributions[0].binId).toBe(binIds[0]); - expect(distributions[0].xAmountBpsOfTotal.toNumber()).toBe(10000); - expect(distributions[0].yAmountBpsOfTotal.toNumber()).toBe(0); - }); - - describe("when the deposit bin is the active bin", () => { - const binIds = [-3333]; - const activeBin = -3333; - - const distributions = calculateNormalDistribution(activeBin, binIds); - - expect(distributions.length).toBe(1); - expect(distributions[0].binId).toBe(binIds[0]); - expect(distributions[0].xAmountBpsOfTotal.toNumber()).toBe(10000); - expect(distributions[0].yAmountBpsOfTotal.toNumber()).toBe(10000); - }); - }); - describe("spot distribution", () => { test("should return correct distribution with equal delta", () => { const binIds = [1, 2, 3, 4, 5]; @@ -336,131 +294,6 @@ describe("calculate_distribution", () => { } } } - - test("should return correct distribution with liquidity concentrated around right side of the active bin", () => { - const binIds = [ - 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, 5515, 5516, - 5517, 5518, 5519, 5520, 5521, - ]; - const activeBin = 5518; - - const distributions = calculateNormalDistribution(activeBin, binIds); - - expect(distributions.length).toBe(binIds.length); - - const xTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.xAmountBpsOfTotal.toNumber(), - 0 - ); - const yTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.yAmountBpsOfTotal.toNumber(), - 0 - ); - - expect(xTokenTotalBps).toBe(10_000); - expect(yTokenTotalBps).toBe(10_000); - - debugDistributionChart(distributions); - assertDistributionAroundActiveBin(activeBin, distributions); - }); - - test("should return correct distribution with liquidity concentrated around left side of the active bin", () => { - const binIds = [ - 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, 5515, 5516, - 5517, 5518, 5519, 5520, 5521, - ]; - const activeBin = 5508; - - const distributions = calculateNormalDistribution(activeBin, binIds); - - expect(distributions.length).toBe(binIds.length); - - const xTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.xAmountBpsOfTotal.toNumber(), - 0 - ); - const yTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.yAmountBpsOfTotal.toNumber(), - 0 - ); - - expect(xTokenTotalBps).toBe(10_000); - expect(yTokenTotalBps).toBe(10_000); - - debugDistributionChart(distributions); - assertDistributionAroundActiveBin(activeBin, distributions); - }); - - test("should return correct distribution with liquidity concentrated around the active bin", () => { - const binIds = [ - 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, 5515, 5516, - 5517, 5518, 5519, 5520, 5521, - ]; - const activeBin = 5513; - - const distributions = calculateNormalDistribution(activeBin, binIds); - - const xTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.xAmountBpsOfTotal.toNumber(), - 0 - ); - const yTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.yAmountBpsOfTotal.toNumber(), - 0 - ); - - expect(xTokenTotalBps).toBe(10_000); - expect(yTokenTotalBps).toBe(10_000); - - debugDistributionChart(distributions); - assertDistributionAroundActiveBin(activeBin, distributions); - }); - - test("should return correct distribution with liquidity to far right of the active bin", () => { - const binIds = [5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513]; - const activeBin = 3000; - - const distributions = calculateNormalDistribution(activeBin, binIds); - expect(distributions.length).toBe(binIds.length); - - const xTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.xAmountBpsOfTotal.toNumber(), - 0 - ); - const yTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.yAmountBpsOfTotal.toNumber(), - 0 - ); - - expect(xTokenTotalBps).toBe(10_000); - expect(yTokenTotalBps).toBe(0); - - debugDistributionChart(distributions); - assertDistributionAroundActiveBin(activeBin, distributions); - }); - - test("should return correct distribution with liquidity to far left of the active bin", () => { - const binIds = [5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513]; - const activeBin = 8000; - - const distributions = calculateNormalDistribution(activeBin, binIds); - expect(distributions.length).toBe(binIds.length); - - const xTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.xAmountBpsOfTotal.toNumber(), - 0 - ); - const yTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.yAmountBpsOfTotal.toNumber(), - 0 - ); - - expect(xTokenTotalBps).toBe(0); - expect(yTokenTotalBps).toBe(10_000); - - debugDistributionChart(distributions); - assertDistributionAroundActiveBin(activeBin, distributions); - }); }); describe("bid ask distribution", () => { @@ -497,133 +330,6 @@ describe("calculate_distribution", () => { } } - test("should return correct distribution with liquidity concentrated around right side of the active bin", () => { - const binIds = [ - 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, 5515, 5516, - 5517, 5518, 5519, 5520, 5521, - ]; - const activeBin = 5518; - - const distributions = calculateBidAskDistribution(activeBin, binIds); - - expect(distributions.length).toBe(binIds.length); - - const xTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.xAmountBpsOfTotal.toNumber(), - 0 - ); - const yTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.yAmountBpsOfTotal.toNumber(), - 0 - ); - - expect(xTokenTotalBps).toBe(10_000); - expect(yTokenTotalBps).toBe(10_000); - - debugDistributionChart(distributions); - assertDistributionAroundActiveBin(activeBin, distributions); - }); - - test("should return correct distribution with liquidity concentrated around left side of the active bin", () => { - const binIds = [ - 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, 5515, 5516, - 5517, 5518, 5519, 5520, 5521, - ]; - const activeBin = 5508; - - const distributions = calculateBidAskDistribution(activeBin, binIds); - - expect(distributions.length).toBe(binIds.length); - - const xTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.xAmountBpsOfTotal.toNumber(), - 0 - ); - const yTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.yAmountBpsOfTotal.toNumber(), - 0 - ); - - expect(xTokenTotalBps).toBe(10_000); - expect(yTokenTotalBps).toBe(10_000); - - debugDistributionChart(distributions); - assertDistributionAroundActiveBin(activeBin, distributions); - }); - - test("should return correct distribution with liquidity concentrated around the active bin", () => { - const binIds = [ - 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, 5515, 5516, - 5517, 5518, 5519, 5520, 5521, - ]; - const activeBin = 5513; - - const distributions = calculateBidAskDistribution(activeBin, binIds); - - expect(distributions.length).toBe(binIds.length); - - const xTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.xAmountBpsOfTotal.toNumber(), - 0 - ); - const yTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.yAmountBpsOfTotal.toNumber(), - 0 - ); - - expect(xTokenTotalBps).toBe(10_000); - expect(yTokenTotalBps).toBe(10_000); - - debugDistributionChart(distributions); - assertDistributionAroundActiveBin(activeBin, distributions); - }); - - test("should return correct distribution with liquidity to far right of the active bin", () => { - const binIds = [5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513]; - const activeBin = 3000; - - const distributions = calculateBidAskDistribution(activeBin, binIds); - expect(distributions.length).toBe(binIds.length); - - const xTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.xAmountBpsOfTotal.toNumber(), - 0 - ); - const yTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.yAmountBpsOfTotal.toNumber(), - 0 - ); - - expect(xTokenTotalBps).toBe(10_000); - expect(yTokenTotalBps).toBe(0); - - debugDistributionChart(distributions); - assertDistributionAroundActiveBin(activeBin, distributions); - }); - - test("should return correct distribution with liquidity to far left of the active bin", () => { - const binIds = [5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513]; - const activeBin = 8000; - - const distributions = calculateBidAskDistribution(activeBin, binIds); - expect(distributions.length).toBe(binIds.length); - - const xTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.xAmountBpsOfTotal.toNumber(), - 0 - ); - const yTokenTotalBps = distributions.reduce( - (acc, d) => acc + d.yAmountBpsOfTotal.toNumber(), - 0 - ); - - expect(xTokenTotalBps).toBe(0); - expect(yTokenTotalBps).toBe(10_000); - - debugDistributionChart(distributions); - assertDistributionAroundActiveBin(activeBin, distributions); - }); - test("to weight distribution", () => { const binIds = [ -3563, -3562, -3561, -3560, -3559, -3558, -3557, -3556, -3555, From 19241573ba746ae250ed3a252770e5f77ef037ec Mon Sep 17 00:00:00 2001 From: codewithgun Date: Fri, 17 Jan 2025 20:17:58 +0800 Subject: [PATCH 2/2] chore: update version and changelog --- CHANGELOG.md | 10 ++++++++-- ts-client/package.json | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 286ff4bb..83c4decb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,11 +19,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security +## @meteora-ag/dlmm [1.3.9] - PR #150 + +### Removed + +- Remove non-solana official libs and unused dependency + ## @meteora-ag/dlmm [1.3.8] - PR #144 ### Fixed -- Fix `getOrCreateATAInstruction` to use `createAssociatedTokenAccountIdempotentInstruction` +- Fix `getOrCreateATAInstruction` to use `createAssociatedTokenAccountIdempotentInstruction` ## @meteora-ag/dlmm [1.3.7] - PR #143 @@ -36,7 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Refactored; remove `position(V1)` interaction from SDK -- Throw error in `removeLiquidity` function if position doesn't have any liquidity +- Throw error in `removeLiquidity` function if position doesn't have any liquidity ### Fixed diff --git a/ts-client/package.json b/ts-client/package.json index 20f3a7c0..5c8c845f 100644 --- a/ts-client/package.json +++ b/ts-client/package.json @@ -1,6 +1,6 @@ { "name": "@meteora-ag/dlmm", - "version": "1.3.8", + "version": "1.3.9", "description": "", "main": "./dist/index.js", "module": "./dist/index.mjs",