Skip to content

Comments

[cl_vault] added range management#23

Open
0x-minato wants to merge 13 commits intomainfrom
ariyan/multi-range-cl-vault
Open

[cl_vault] added range management#23
0x-minato wants to merge 13 commits intomainfrom
ariyan/multi-range-cl-vault

Conversation

@0x-minato
Copy link
Collaborator

No description provided.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the concentrated liquidity vault from a single-pool architecture to support multiple managed pools. The vault can now handle liquidity across multiple Ekubo pools simultaneously, with proportional deposit/withdrawal logic across all pools.

Key changes:

  • Introduces ManagedPool, SqrtValues, and InitValues structs to manage multiple pools
  • Converts storage from single pool variables to vectors of pools with associated data
  • Updates all core functions (deposit, withdraw, rebalance, etc.) to operate across multiple pools

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
src/strategies/cl_vault/interface.cairo Adds new data structures for multi-pool support and updates function signatures to accept pool parameters
src/strategies/cl_vault/cl_vault.cairo Implements multi-pool logic with loops over managed pools, proportional deposit/withdrawal calculations, and per-pool fee handling

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

);
return MyPosition {
liquidity: userPosition.liquidity, amount0: amt0.into(), amount1: amt1.into()
liquidity: userPosition.liquidity, amount0: total_amt0.into(), amount1: total_amt0.into()
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The amount1 field is incorrectly set to total_amt0 instead of total_amt1. This will return the wrong amount for token1.

Suggested change
liquidity: userPosition.liquidity, amount0: total_amt0.into(), amount1: total_amt0.into()
liquidity: userPosition.liquidity, amount0: total_amt0.into(), amount1: total_amt1.into()

Copilot uses AI. Check for mistakes.
Comment on lines 260 to 287
while i != self.managed_pools.len() {
let pool = self.managed_pools[i].read();
self.handle_fees(pool);
let pool_liq = self._convert_to_assets(shares, pool);

let old_liq = self.get_position(pool).liquidity;

let (amt0, amt1) = self._withdraw_position(pool_liq, pool);
total_amt0 += amt0.into();
total_amt1 += amt1.into();

let current_liq = self.get_position(pool).liquidity;

if current_liq == 0 {
self.managed_pools[i].write(
ManagedPool {
pool_key: pool.pool_key,
bounds: pool.bounds,
nft_id: 0
}
);
}

// withdraw
let (amt0, amt1) = self._withdraw_position(userPosition.liquidity.try_into().unwrap());
assert(
(old_liq - current_liq).into() == userPosition.liquidity,
'invalid liquidity removed'
);
}
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion on line 284 compares per-pool liquidity change against total userPosition.liquidity, which will fail for multi-pool scenarios. The assertion should compare (old_liq - current_liq).into() == pool_liq or accumulate liquidity changes across pools before asserting. Also, the loop is missing an increment statement i += 1; at the end.

Copilot uses AI. Check for mistakes.
Comment on lines 1006 to 1007
let deposit_amt0 = (amount0 * (*range_amount0).into()) / total_amount0.into();
let deposit_amt1 = (amount1 * (*range_amount1).into()) / total_amount1.into();
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Division by zero is possible if total_amount0 or total_amount1 is zero (e.g., when all pools are empty or unbalanced). Add checks to handle the case when totals are zero before performing division.

Suggested change
let deposit_amt0 = (amount0 * (*range_amount0).into()) / total_amount0.into();
let deposit_amt1 = (amount1 * (*range_amount1).into()) / total_amount1.into();
let deposit_amt0 = if total_amount0 != 0 {
(amount0 * (*range_amount0).into()) / total_amount0.into()
} else {
0
};
let deposit_amt1 = if total_amount1 != 0 {
(amount1 * (*range_amount1).into()) / total_amount1.into()
} else {
0
};

Copilot uses AI. Check for mistakes.
Comment on lines 1009 to 1010
let user_new_liq = (*range_liq * deposit_amt0.try_into().unwrap()) / *range_amount0;
let mut range_shares = 0;
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Division by zero is possible if *range_amount0 is zero. Add a check to handle empty pool ranges before performing this calculation.

Suggested change
let user_new_liq = (*range_liq * deposit_amt0.try_into().unwrap()) / *range_amount0;
let mut range_shares = 0;
let user_new_liq = if *range_amount0 != 0 {

Copilot uses AI. Check for mistakes.
// implement arrakis ratio for shares
let init_values = self.init_values.read();
let shares_from_token0 = if init_values.init0 != 0 {
deposit_amt0 * 1000000000000000000_u256 / init_values.init0 }
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magic number 1000000000000000000_u256 (1e18) should be defined as a named constant to improve code readability and maintainability.

Copilot uses AI. Check for mistakes.
pub bounds: Bounds,
pub nft_id: u64
}
// todo : add sqrt values if possible
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Inconsistent spacing around colon. Should be // todo: add sqrt values if possible or // TODO: add sqrt values if possible for consistency with typical comment formatting.

Suggested change
// todo : add sqrt values if possible
// TODO: add sqrt values if possible

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@akiraonstarknet akiraonstarknet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

}

shares += range_shares;
total_assets += self._convert_to_assets(range_shares, pool);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of total_assets, call as total_liquidity. Also, no point in adding liquidity. there are not comparable. So, dont do this. just return shares from this function.

@akiraonstarknet
Copy link
Contributor

akiraonstarknet commented Dec 8, 2025

@0x-minato

  • add_pool() has no access control (gov role)

  • remove_pool() has no access control (gov role)

  • get_amount_delta() is unnecessarily mutable (ref self) with no access control

  • remove_pool logic is broken. Add following test cases

  • add get_managed_pools_len and get_managed_pool(index) functions

  • rename bounds as new_bounds to keep it clear

#[derive(Drop, Copy, Serde, starknet::Store, starknet::Event)]
pub struct RangeInstruction {
    pub liquidity_mint: u128,
    pub liquidity_burn: u128,
    pub pool_key: PoolKey,
    pub bounds: Bounds,
}
  • in withdraw function, in event being emited, amount1 used amount0.
   return MyPosition {
                liquidity: liquidities, amount0: total_amt0.into(), amount1: total_amt0.into()
            };
  • will fail, if range_amt0 is zero
    let user_new_liq = (*range_liq * deposit_amt0.try_into().unwrap()) / *range_amt0;

  • when withdrawing 100% of liquidity, in test, add a condition that checks if the earlier ekubo NFT is burnt or not.

  • [constructor] No validation of InitValues in constructor (could be zero)

  • [constructor] No bounds checking on fee_bps (could exceed 10000)

  • [constructor] No validation that managed_pools array is non-empty

  • [constructor] Missing validation that all pools use same token0/token1 pair. must be done in add_pool too.

  • [Akira] think of a way to avoid using rewards_share -> May be its ok without it bcz backend can decide when to and how much of the unused to handle, enabling softer increase in liquidity instead of a big liquidity addition to avoid inflation attacks

  • Add pool_key validation to rebalance_pool
    Add this check:

fn rebalance_pool(ref self: ContractState, rebalance_params: RebalanceParams) {
    // Add validation loop
    let mut i = 0;
    while i != self.managed_pools.len() {
        let stored_pool = self.managed_pools[i].read();
        let input_pool_key = rebalance_params.rebal.at(i).pool_key; // Add pool_key to RangeInstruction
        
        assert(stored_pool.pool_key.token0 == input_pool_key.token0, 'pool_key mismatch token0');
        assert(stored_pool.pool_key.token1 == input_pool_key.token1, 'pool_key mismatch token1');
        assert(stored_pool.pool_key.fee == input_pool_key.fee, 'pool_key mismatch fee');
        assert(stored_pool.pool_key.tick_spacing == input_pool_key.tick_spacing, 'pool_key mismatch tick_spacing');
        assert(stored_pool.pool_key.extension == input_pool_key.extension, 'pool_key mismatch extension');
        
        i += 1;
    }
    
    // ... rest of rebalance logic
}
  • (Akira) -> _ekubo_deposit may need some slippage protections. Check once.
  • [Akira] -> Think of emergency withdraw mechanisms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants