diff --git a/programs/zap/src/math/price_math.rs b/programs/zap/src/math/price_math.rs index 3abadf4..158ad51 100644 --- a/programs/zap/src/math/price_math.rs +++ b/programs/zap/src/math/price_math.rs @@ -20,7 +20,7 @@ pub fn get_price_from_id(active_id: i32, bin_step: u16) -> Result { // get price base factor pub fn get_price_base_factor(bin_step: u16) -> Result { - // Make bin_step into Q64x64, and divided by BASIS_POINT_MAX. If bin_step = 1, we get 0.0001 in Q64x64 + // Make bin_step into Q64x64, and divided by MAX_BASIS_POINT. If bin_step = 1, we get 0.0001 in Q64x64 let bps = u128::from(bin_step) .safe_shl(SCALE_OFFSET.into())? .safe_div(MAX_BASIS_POINT as u128)?; diff --git a/programs/zap/src/utils/dlmm_utils.rs b/programs/zap/src/utils/dlmm_utils.rs index 9df0718..f9234a9 100644 --- a/programs/zap/src/utils/dlmm_utils.rs +++ b/programs/zap/src/utils/dlmm_utils.rs @@ -308,10 +308,7 @@ impl StrategyHandler for CurveHandler { if min_delta_id == max_delta_id { let bin_id = active_id.safe_add(min_delta_id)?; - let pm = U256::from(get_price_from_id(bin_id, bin_step)?); - let x0 = U256::from(amount_x).safe_mul(pm)?.safe_shr(64)?; - let x0: i128 = x0.try_into().map_err(|_| ZapError::TypeCastFailed)?; - return Ok((x0, 0)); + return find_x0_and_delta_x_single_bin(bin_id, bin_step, amount_x); } let mut b = U256::ZERO; @@ -405,14 +402,11 @@ impl StrategyHandler for BidAskHandler { // C = (m1 * p(m1) + ... + m2 * p(m2)) // delta_x = sum(amounts) / (C-B) // note: in bid ask strategy: x0 <= 0 and delta_x >= 0 + // except for the case min_delta_id == max_delta_id, x0 > 0 and delta_x = 0 (using the same result from curve strategy) if min_delta_id == max_delta_id { let bin_id = active_id.safe_add(min_delta_id)?; - let pm = get_price_from_id(bin_id.neg(), bin_step)?; - let denominator = U256::from(min_delta_id).safe_mul(U256::from(pm))?; - let delta_x = U256::from(amount_x).safe_shl(64)?.safe_div(denominator)?; - let delta_x: i128 = delta_x.try_into().map_err(|_| ZapError::TypeCastFailed)?; - return Ok((0, delta_x)); + return find_x0_and_delta_x_single_bin(bin_id, bin_step, amount_x); } let mut b = U256::ZERO; @@ -443,3 +437,14 @@ impl StrategyHandler for BidAskHandler { Ok((x0, delta_x)) } } + +fn find_x0_and_delta_x_single_bin( + bin_id: i32, + bin_step: u16, + amount_x: u64, +) -> Result<(i128, i128)> { + let pm = U256::from(get_price_from_id(bin_id, bin_step)?); + let x0 = U256::from(amount_x).safe_mul(pm)?.safe_shr(64)?; + let x0: i128 = x0.try_into().map_err(|_| ZapError::TypeCastFailed)?; + return Ok((x0, 0)); +} diff --git a/tests/test_zapin/zapin_dlmm_initialize_position.test.ts b/tests/test_zapin/zapin_dlmm_initialize_position.test.ts index dcaa3a0..89b5f05 100644 --- a/tests/test_zapin/zapin_dlmm_initialize_position.test.ts +++ b/tests/test_zapin/zapin_dlmm_initialize_position.test.ts @@ -233,6 +233,66 @@ describe("Zapin DLMM with initialize position", () => { }); }); + it("Zapin dlmm single bin with Curve strategy", async () => { + await initializeBinArrayBitmapExtension(svm, lbPair, admin); + const position = await createDlmmPosition(svm, user, lbPair, lowerBinId); + + const amountTokenA = new BN(LAMPORTS_PER_SOL); + const amountSwap = amountTokenA.divn(2); + + const binArrays = getBinArrayAccountMetaByBinRange( + lbPair, + new BN(lowerBinId), + new BN(upperBinId) + ); + + await zapInDlmmFullFlow({ + svm, + user, + lbPair, + position, + inputTokenMint: tokenXMint, + outputTokenMint: tokenYMint, + totalAmount: amountTokenA, + amountSwap, + minDeltaId: 0, + maxDeltaId: 0, + strategy: StrategyType.Curve, + binArrays, + remainingAccountInfo: { slices: [] }, + }); + }); + + it("Zapin dlmm single bin with Bid/ask strategy", async () => { + await initializeBinArrayBitmapExtension(svm, lbPair, admin); + const position = await createDlmmPosition(svm, user, lbPair, lowerBinId); + + const amountTokenA = new BN(LAMPORTS_PER_SOL); + const amountSwap = amountTokenA.divn(2); + + const binArrays = getBinArrayAccountMetaByBinRange( + lbPair, + new BN(lowerBinId), + new BN(upperBinId) + ); + + await zapInDlmmFullFlow({ + svm, + user, + lbPair, + position, + inputTokenMint: tokenXMint, + outputTokenMint: tokenYMint, + totalAmount: amountTokenA, + amountSwap, + minDeltaId: 0, + maxDeltaId: 0, + strategy: StrategyType.BidAsk, + binArrays, + remainingAccountInfo: { slices: [] }, + }); + }); + it("Zapin dlmm without bin array bitmap extension", async () => { const position = await createDlmmPosition(svm, user, lbPair, lowerBinId); const amountTokenA = new BN(LAMPORTS_PER_SOL); @@ -370,5 +430,12 @@ async function zapInDlmmFullFlow(params: { expect(result).instanceOf(TransactionMetadata); let liquidities = getPositionTotalLiquidityAllBin(svm, position); - console.log(babar(liquidities)); + if (liquidities.length > 1) { + // there is a bug in babar that we could draw single bin + console.log(babar(liquidities)); + } else { + console.log("Liquidity distribution (bin_index, liquidity)"); + console.log(liquidities); + } + }