Skip to content
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Remove `update_whitelisted_wallet`

## @meteora-ag/dlmm [1.2.4] - PR #119

### Fixed

- Refactor `getBins` to work with any bin ranges

## @meteora-ag/dlmm [1.2.3] - PR #112

### Fixed
Expand Down
20 changes: 20 additions & 0 deletions ts-client/src/dlmm/helpers/binArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
BinArray,
BinArrayAccount,
BinArrayBitmapExtension,
BinLiquidity,
BitmapType,
LbPair,
} from "../types";
Expand Down Expand Up @@ -364,3 +365,22 @@ export function getBinArraysRequiredByPositionRange(
index,
}));
}

export function* enumerateBins(
binsById: Map<number, Bin>,
lowerBinId: number,
upperBinId: number,
binStep: number,
baseTokenDecimal: number,
quoteTokenDecimal: number,
version: number
) {
for (let currentBinId = lowerBinId; currentBinId <= upperBinId; currentBinId++) {
const bin = binsById.get(currentBinId);
if (bin != null) {
yield BinLiquidity.fromBin(bin, currentBinId, binStep, baseTokenDecimal, quoteTokenDecimal, version);
} else {
yield BinLiquidity.empty(currentBinId, binStep, baseTokenDecimal, quoteTokenDecimal, version);
}
}
}
9 changes: 9 additions & 0 deletions ts-client/src/dlmm/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ export function chunks<T>(array: T[], size: number): T[][] {
);
}

export function range<T>(
min: number,
max: number,
mapfn: (i: number) => T
) {
const length = max - min + 1;
return Array.from({ length }, (_, i) => mapfn(min + i));
}

export async function chunkedFetchMultiplePoolAccount(
program: ClmmProgram,
pks: PublicKey[],
Expand Down
159 changes: 42 additions & 117 deletions ts-client/src/dlmm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ import {
toWeightDistribution,
unwrapSOLInstruction,
wrapSOLInstruction,
enumerateBins,
range,
} from "./helpers";
import {
Rounding,
Expand Down Expand Up @@ -1620,17 +1622,17 @@ export class DLMM {
public async getBinsBetweenLowerAndUpperBound(
lowerBinId: number,
upperBinId: number,
lowerBinArrays?: BinArray,
upperBinArrays?: BinArray
lowerBinArray?: BinArray,
upperBinArray?: BinArray
): Promise<{ activeBin: number; bins: BinLiquidity[] }> {
const bins = await this.getBins(
this.pubkey,
lowerBinId,
upperBinId,
this.tokenX.decimal,
this.tokenY.decimal,
lowerBinArrays,
upperBinArrays
lowerBinArray,
upperBinArray
);

return { activeBin: this.lbPair.activeId, bins };
Expand Down Expand Up @@ -5674,126 +5676,49 @@ export class DLMM {
upperBinId: number,
baseTokenDecimal: number,
quoteTokenDecimal: number,
lowerBinArrays?: BinArray,
upperBinArrays?: BinArray
Comment thread
00xSam marked this conversation as resolved.
): Promise<BinLiquidity[]> {
lowerBinArray?: BinArray,
upperBinArray?: BinArray
) {
const lowerBinArrayIndex = binIdToBinArrayIndex(new BN(lowerBinId));
const upperBinArrayIndex = binIdToBinArrayIndex(new BN(upperBinId));

let bins: BinLiquidity[] = [];
if (lowerBinArrayIndex.eq(upperBinArrayIndex)) {
const [binArrayPubKey] = deriveBinArray(
lbPairPubKey,
lowerBinArrayIndex,
this.program.programId
);
const binArray =
lowerBinArrays ??
(await this.program.account.binArray.fetch(binArrayPubKey).catch(() => {
const [lowerBinId, upperBinId] =
getBinArrayLowerUpperBinId(lowerBinArrayIndex);

const binArrayBins: Bin[] = [];
for (let i = lowerBinId.toNumber(); i <= upperBinId.toNumber(); i++) {
binArrayBins.push({
amountX: new BN(0),
amountY: new BN(0),
liquiditySupply: new BN(0),
rewardPerTokenStored: [new BN(0), new BN(0)],
amountXIn: new BN(0),
amountYIn: new BN(0),
feeAmountXPerTokenStored: new BN(0),
feeAmountYPerTokenStored: new BN(0),
price: new BN(0),
});
}

return {
bins: binArrayBins,
index: lowerBinArrayIndex,
version: 1,
};
}));

const [lowerBinIdForBinArray] = getBinArrayLowerUpperBinId(
binArray.index
);

binArray.bins.forEach((bin, idx) => {
const binId = lowerBinIdForBinArray.toNumber() + idx;

if (binId >= lowerBinId && binId <= upperBinId) {
const pricePerLamport = getPriceOfBinByBinId(
binId,
this.lbPair.binStep
).toString();
bins.push({
binId,
xAmount: bin.amountX,
yAmount: bin.amountY,
supply: bin.liquiditySupply,
price: pricePerLamport,
version: binArray.version,
pricePerToken: new Decimal(pricePerLamport)
.mul(new Decimal(10 ** (baseTokenDecimal - quoteTokenDecimal)))
.toString(),
});
}
});
} else {
const [lowerBinArrayPubKey] = deriveBinArray(
lbPairPubKey,
lowerBinArrayIndex,
this.program.programId
);
const [upperBinArrayPubKey] = deriveBinArray(
lbPairPubKey,
upperBinArrayIndex,
this.program.programId
);
const hasCachedLowerBinArray = lowerBinArray != null;
const hasCachedUpperBinArray = upperBinArray != null;
const isSingleBinArray = lowerBinArrayIndex.eq(upperBinArrayIndex);

const binArrays = await (async () => {
if (!lowerBinArrays || !upperBinArrays) {
return (
await this.program.account.binArray.fetchMultiple([
lowerBinArrayPubKey,
upperBinArrayPubKey,
])
).filter((binArray) => binArray !== null);
}
const lowerBinArrayIndexOffset = hasCachedLowerBinArray ? 1 : 0;
const upperBinArrayIndexOffset = hasCachedUpperBinArray ? -1 : 0;

return [lowerBinArrays, upperBinArrays];
})();
const binArrayPubkeys = range(
lowerBinArrayIndex.toNumber() + lowerBinArrayIndexOffset,
upperBinArrayIndex.toNumber() + upperBinArrayIndexOffset,
i => deriveBinArray(lbPairPubKey, new BN(i), this.program.programId)[0]
);
Comment thread
00xSam marked this conversation as resolved.
const fetchedBinArrays = binArrayPubkeys.length !== 0 ?
await this.program.account.binArray.fetchMultiple(binArrayPubkeys) : [];
const binArrays = [
...(hasCachedLowerBinArray ? [lowerBinArray] : []),
...fetchedBinArrays,
...((hasCachedUpperBinArray && !isSingleBinArray) ? [upperBinArray] : [])
];

binArrays.forEach((binArray) => {
if (!binArray) return;
const [lowerBinIdForBinArray] = getBinArrayLowerUpperBinId(
binArray.index
);
binArray.bins.forEach((bin, idx) => {
const binId = lowerBinIdForBinArray.toNumber() + idx;
if (binId >= lowerBinId && binId <= upperBinId) {
const pricePerLamport = getPriceOfBinByBinId(
binId,
this.lbPair.binStep
).toString();
bins.push({
binId,
xAmount: bin.amountX,
yAmount: bin.amountY,
supply: bin.liquiditySupply,
price: pricePerLamport,
version: binArray.version,
pricePerToken: new Decimal(pricePerLamport)
.mul(new Decimal(10 ** (baseTokenDecimal - quoteTokenDecimal)))
.toString(),
});
}
});
});
}
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.length !== 0 ? binArrays[0].version : 1;

return bins;
return Array.from(enumerateBins(
binsById,
lowerBinId,
upperBinId,
this.lbPair.binStep,
baseTokenDecimal,
quoteTokenDecimal,
version,
));
}

private async binArraysToBeCreate(
Expand Down
52 changes: 52 additions & 0 deletions ts-client/src/dlmm/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ProgramAccount,
} from "@coral-xyz/anchor";
import { LbClmm } from "../idl";
import { getPriceOfBinByBinId } from "../helpers";
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
import Decimal from "decimal.js";
import { u64, i64, struct } from "@coral-xyz/borsh";
Expand Down Expand Up @@ -189,6 +190,57 @@ export interface BinLiquidity {
pricePerToken: string;
}

export module BinLiquidity {
export function fromBin(
bin: Bin,
binId: number,
binStep: number,
baseTokenDecimal: number,
quoteTokenDecimal: number,
version: number
): BinLiquidity {
const pricePerLamport = getPriceOfBinByBinId(
binId,
binStep
).toString();
return {
binId,
xAmount: bin.amountX,
yAmount: bin.amountY,
supply: bin.liquiditySupply,
price: pricePerLamport,
version,
pricePerToken: new Decimal(pricePerLamport)
.mul(new Decimal(10 ** (baseTokenDecimal - quoteTokenDecimal)))
.toString(),
};
}

export function empty(
binId: number,
binStep: number,
baseTokenDecimal: number,
quoteTokenDecimal: number,
version: number
): BinLiquidity {
const pricePerLamport = getPriceOfBinByBinId(
binId,
binStep
).toString();
return {
binId,
xAmount: new BN(0),
yAmount: new BN(0),
supply: new BN(0),
price: pricePerLamport,
version,
pricePerToken: new Decimal(pricePerLamport)
.mul(new Decimal(10 ** (baseTokenDecimal - quoteTokenDecimal)))
.toString(),
};
}
}

export interface SwapQuote {
consumedInAmount: BN;
outAmount: BN;
Expand Down