Skip to content

Commit

Permalink
feat: getBinArrayAroundActiveBin for swap quote
Browse files Browse the repository at this point in the history
  • Loading branch information
McSam94 committed Jan 17, 2024
1 parent a9a3cb6 commit f0866e7
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 114 deletions.
3 changes: 2 additions & 1 deletion ts-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ try {
```ts
const swapAmount = new BN(100);
// Swap quote
const binArrays = await dlmmPool.getBinArrays();
const binArrays = await dlmmPool.getBinArrayAroundActiveBin();
const swapQuote = await dlmmPool.swapQuote(
swapAmount,
true,
Expand Down Expand Up @@ -206,6 +206,7 @@ try {
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `refetchStates` | Update onchain state of DLMM instance. It's recommend to call this before interact with the program (Deposit/ Withdraw/ Swap) | `Promise<void>` |
| `getBinArrays` | Retrieves List of Bin Arrays | `Promise<BinArrayAccount[]>` |
| `getBinArrayAroundActiveBin` | Retrieves List of Bin Arrays around Active Bin | `Promise<BinArrayAccount[]>` |
| `getFeeInfo` | Retrieves LbPair's fee info including `base fee`, `protocol fee` & `max fee` | `FeeInfo` |
| `getDynamicFee` | Retrieves LbPair's dynamic fee | `Decimal` |
| `getBinsAroundActiveBin` | retrieves a specified number of bins to the left and right of the active bin and returns them along with the active bin ID. | `Promise<{ activeBin: number; bins: BinLiquidity[] }>` |
Expand Down
93 changes: 93 additions & 0 deletions ts-client/src/dlmm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import {
deriveOracle,
derivePresetParameter,
computeBudgetIx,
findNextBinArrayIndexWithLiquidity,
} from "./helpers";
import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";
import Decimal from "decimal.js";
Expand Down Expand Up @@ -929,6 +930,98 @@ export class DLMM {
]);
}

/**
* The function `getBinArrayAroundActiveBin` retrieves a specified number of `BinArrayAccount`
* objects from the blockchain, based on the active bin and its surrounding bin arrays.
* @param [count=2] - The `count` parameter is the number of bin arrays to retrieve on left and right respectively. By default, it
* is set to 2.
* @returns an array of `BinArrayAccount` objects.
*/
public async getBinArrayAroundActiveBin(
count = 2
): Promise<BinArrayAccount[]> {
await this.refetchStates();

const binArraysPubkey = new Set<string>();

let shouldStopRight = false;
let activeIdToLoop = this.lbPair.activeId;

while (!shouldStopRight) {
const binArrayIndex = findNextBinArrayIndexWithLiquidity(
false,
new BN(activeIdToLoop),
this.lbPair,
this.binArrayBitmapExtension?.account ?? null
);
if (binArrayIndex === null) shouldStopRight = true;
else {
const [binArrayPubKey] = deriveBinArray(
this.pubkey,
binArrayIndex,
this.program.programId
);
binArraysPubkey.add(binArrayPubKey.toBase58());

const [, upperBinId] = getBinArrayLowerUpperBinId(binArrayIndex);
activeIdToLoop = upperBinId.toNumber() + 1;
}

if (binArraysPubkey.size === count) shouldStopRight = true;
}

let shouldStopLeft = false;
activeIdToLoop = this.lbPair.activeId;

while (!shouldStopLeft) {
const binArrayIndex = findNextBinArrayIndexWithLiquidity(
true,
new BN(activeIdToLoop),
this.lbPair,
this.binArrayBitmapExtension?.account ?? null
);
if (binArrayIndex === null) shouldStopLeft = true;
else {
const [binArrayPubKey] = deriveBinArray(
this.pubkey,
binArrayIndex,
this.program.programId
);
binArraysPubkey.add(binArrayPubKey.toBase58());

const [lowerBinId] = getBinArrayLowerUpperBinId(binArrayIndex);
activeIdToLoop = lowerBinId.toNumber() - 1;
}

if (binArraysPubkey.size === count * 2) shouldStopLeft = true;
}

const accountsToFetch = Array.from(binArraysPubkey).map(
(pubkey) => new PublicKey(pubkey)
);

const binArraysAccInfoBuffer = await chunkedGetMultipleAccountInfos(
this.program.provider.connection,
accountsToFetch
);

const binArrays: BinArrayAccount[] = await Promise.all(
binArraysAccInfoBuffer.map(async (accInfo, idx) => {
const account: BinArray = this.program.coder.accounts.decode(
"binArray",
accInfo.data
);
const publicKey = accountsToFetch[idx];
return {
account,
publicKey,
};
})
);

return binArrays;
}

/**
* The function `getFeeInfo` calculates and returns the base fee rate percentage, maximum fee rate
* percentage, and protocol fee percentage.
Expand Down
226 changes: 113 additions & 113 deletions ts-client/src/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,122 +28,122 @@ async function main() {
const activeBin = await dlmmPool.getActiveBin();
console.log("🚀 ~ activeBin:", activeBin);

const TOTAL_RANGE_INTERVAL = 10; // 10 bins on each side of the active bin
const bins = [activeBin.binId]; // Make sure bins is less than 70, as currently only support up to 70 bins for 1 position
for (
let i = activeBin.binId;
i < activeBin.binId + TOTAL_RANGE_INTERVAL / 2;
i++
) {
const rightNextBinId = i + 1;
const leftPrevBinId = activeBin.binId - (rightNextBinId - activeBin.binId);
bins.push(rightNextBinId);
bins.unshift(leftPrevBinId);
}

const activeBinPricePerToken = dlmmPool.fromPricePerLamport(
Number(activeBin.price)
);
const totalXAmount = new BN(100);
const totalYAmount = totalXAmount.mul(new BN(Number(activeBinPricePerToken)));

// Get spot distribution
const spotXYAmountDistribution = calculateSpotDistribution(
activeBin.binId,
bins
);

// Create Position
const newPosition = new Keypair();
const createPositionTx =
await dlmmPool.initializePositionAndAddLiquidityByWeight({
positionPubKey: newPosition.publicKey,
lbPairPubKey: dlmmPool.pubkey,
user: user.publicKey,
totalXAmount,
totalYAmount,
xYAmountDistribution: spotXYAmountDistribution,
});

try {
for (let tx of Array.isArray(createPositionTx)
? createPositionTx
: [createPositionTx]) {
const createPositionTxHash = await sendAndConfirmTransaction(
connection,
tx,
[user, newPosition]
);
console.log("🚀 ~ createPositionTxHash:", createPositionTxHash);
}
} catch (error) {
console.log("🚀 ~ error:", JSON.parse(JSON.stringify(error)));
}

// Get position state
const { userPositions } = await dlmmPool.getPositionsByUserAndLbPair(
user.publicKey
);
console.log("🚀 ~ userPositions:", userPositions);

// Add Liquidity to existing position
const addLiquidityTx = await dlmmPool.addLiquidityByWeight({
positionPubKey: userPositions[0].publicKey,
lbPairPubKey: dlmmPool.pubkey,
user: user.publicKey,
totalXAmount,
totalYAmount,
xYAmountDistribution: spotXYAmountDistribution,
});

try {
for (let tx of Array.isArray(addLiquidityTx)
? addLiquidityTx
: [addLiquidityTx]) {
const addLiquidityTxHash = await sendAndConfirmTransaction(
connection,
tx,
[user, newPosition]
);
console.log("🚀 ~ addLiquidityTxHash:", addLiquidityTxHash);
}
} catch (error) {
console.log("🚀 ~ error:", JSON.parse(JSON.stringify(error)));
}

// Remove Liquidity
const binIdsToRemove = userPositions[0].positionData.positionBinData.map(
(bin) => bin.binId
);
const removeLiquidityTx = await dlmmPool.removeLiquidity({
position: userPositions[0].publicKey,
user: user.publicKey,
binIds: binIdsToRemove,
liquiditiesBpsToRemove: new Array(binIdsToRemove.length).fill(
new BN(100 * 100)
), // 100% (range from 0 to 100)
shouldClaimAndClose: true, // should claim swap fee and close position together
});

try {
for (let tx of Array.isArray(removeLiquidityTx)
? removeLiquidityTx
: [removeLiquidityTx]) {
const removeLiquidityTxHash = await sendAndConfirmTransaction(
connection,
tx,
[user, newPosition],
{ skipPreflight: false, preflightCommitment: "singleGossip" }
);
console.log("🚀 ~ removeLiquidityTxHash:", removeLiquidityTxHash);
}
} catch (error) {
console.log("🚀 ~ error:", JSON.parse(JSON.stringify(error)));
}
// const TOTAL_RANGE_INTERVAL = 10; // 10 bins on each side of the active bin
// const bins = [activeBin.binId]; // Make sure bins is less than 70, as currently only support up to 70 bins for 1 position
// for (
// let i = activeBin.binId;
// i < activeBin.binId + TOTAL_RANGE_INTERVAL / 2;
// i++
// ) {
// const rightNextBinId = i + 1;
// const leftPrevBinId = activeBin.binId - (rightNextBinId - activeBin.binId);
// bins.push(rightNextBinId);
// bins.unshift(leftPrevBinId);
// }

// const activeBinPricePerToken = dlmmPool.fromPricePerLamport(
// Number(activeBin.price)
// );
// const totalXAmount = new BN(100);
// const totalYAmount = totalXAmount.mul(new BN(Number(activeBinPricePerToken)));

// // Get spot distribution
// const spotXYAmountDistribution = calculateSpotDistribution(
// activeBin.binId,
// bins
// );

// // Create Position
// const newPosition = new Keypair();
// const createPositionTx =
// await dlmmPool.initializePositionAndAddLiquidityByWeight({
// positionPubKey: newPosition.publicKey,
// lbPairPubKey: dlmmPool.pubkey,
// user: user.publicKey,
// totalXAmount,
// totalYAmount,
// xYAmountDistribution: spotXYAmountDistribution,
// });

// try {
// for (let tx of Array.isArray(createPositionTx)
// ? createPositionTx
// : [createPositionTx]) {
// const createPositionTxHash = await sendAndConfirmTransaction(
// connection,
// tx,
// [user, newPosition]
// );
// console.log("🚀 ~ createPositionTxHash:", createPositionTxHash);
// }
// } catch (error) {
// console.log("🚀 ~ error:", JSON.parse(JSON.stringify(error)));
// }

// // Get position state
// const { userPositions } = await dlmmPool.getPositionsByUserAndLbPair(
// user.publicKey
// );
// console.log("🚀 ~ userPositions:", userPositions);

// // Add Liquidity to existing position
// const addLiquidityTx = await dlmmPool.addLiquidityByWeight({
// positionPubKey: userPositions[0].publicKey,
// lbPairPubKey: dlmmPool.pubkey,
// user: user.publicKey,
// totalXAmount,
// totalYAmount,
// xYAmountDistribution: spotXYAmountDistribution,
// });

// try {
// for (let tx of Array.isArray(addLiquidityTx)
// ? addLiquidityTx
// : [addLiquidityTx]) {
// const addLiquidityTxHash = await sendAndConfirmTransaction(
// connection,
// tx,
// [user, newPosition]
// );
// console.log("🚀 ~ addLiquidityTxHash:", addLiquidityTxHash);
// }
// } catch (error) {
// console.log("🚀 ~ error:", JSON.parse(JSON.stringify(error)));
// }

// // Remove Liquidity
// const binIdsToRemove = userPositions[0].positionData.positionBinData.map(
// (bin) => bin.binId
// );
// const removeLiquidityTx = await dlmmPool.removeLiquidity({
// position: userPositions[0].publicKey,
// user: user.publicKey,
// binIds: binIdsToRemove,
// liquiditiesBpsToRemove: new Array(binIdsToRemove.length).fill(
// new BN(100 * 100)
// ), // 100% (range from 0 to 100)
// shouldClaimAndClose: true, // should claim swap fee and close position together
// });

// try {
// for (let tx of Array.isArray(removeLiquidityTx)
// ? removeLiquidityTx
// : [removeLiquidityTx]) {
// const removeLiquidityTxHash = await sendAndConfirmTransaction(
// connection,
// tx,
// [user, newPosition],
// { skipPreflight: false, preflightCommitment: "singleGossip" }
// );
// console.log("🚀 ~ removeLiquidityTxHash:", removeLiquidityTxHash);
// }
// } catch (error) {
// console.log("🚀 ~ error:", JSON.parse(JSON.stringify(error)));
// }

const swapAmount = new BN(100);
// Swap quote
const binArrays = await dlmmPool.getBinArrays();
const binArrays = await dlmmPool.getBinArrayAroundActiveBin();
const swapQuote = await dlmmPool.swapQuote(
swapAmount,
true,
Expand Down

0 comments on commit f0866e7

Please sign in to comment.