jup-lend-sdk 0.2.0

SDK for Jupiter lending protocol
Documentation
use super::{
    errors::ErrorCodes,
    ticks::{
        MIN_TICK, TICK_HAS_DEBT_ARRAY_SIZE, TICK_HAS_DEBT_CHILDREN_SIZE,
        TICK_HAS_DEBT_CHILDREN_SIZE_IN_BITS, TOTAL_INDICES_NEEDED, get_first_tick_for_array_index,
    },
};
use crate::{
    math::{casting::Cast, safe_math::SafeMath},
    programs::vaults::accounts::TickHasDebtArray,
};
use anchor_lang::Result;

impl TickHasDebtArray {
    pub fn has_bits(&self, map_id: usize) -> bool {
        self.tick_has_debt[map_id]
            .children_bits
            .iter()
            .any(|&x| x != 0)
    }

    // Create a mask for the current byte that will:
    // - Keep all bits lower than bit_index (these are smaller ticks)
    // - Clear the current bit and all higher bits (these are the current and larger ticks)
    pub fn clear_bits(
        &mut self,
        map_index: usize,
        byte_index: usize,
        bit_index: usize,
    ) -> Result<()> {
        // Create a working copy of the current map's bitmap
        let mut bitmap: [u8; TICK_HAS_DEBT_CHILDREN_SIZE] =
            self.tick_has_debt[map_index].children_bits;

        // Clear the current tick's bit and all higher bits in the current byte
        if bit_index > 0 {
            // Create mask to keep only bits lower than current bit
            let mask = (1 << bit_index) - 1;
            bitmap[byte_index] &= mask;
        } else {
            // If bit_index is 0, clear the entire byte
            bitmap[byte_index] = 0;
        }

        // Clear all bytes with higher indices (representing higher ticks)
        for i in (byte_index + 1)..TICK_HAS_DEBT_CHILDREN_SIZE {
            bitmap[i] = 0;
        }

        self.tick_has_debt[map_index].children_bits = bitmap;

        Ok(())
    }

    pub fn fetch_next_top_tick(&mut self, mut map_index: usize) -> Result<(i32, bool)> {
        // Search for the next tick with debt
        loop {
            if self.has_bits(map_index.cast()?) {
                let (next_tick, has_next_tick) = self.get_next_tick(map_index.cast()?)?;

                if has_next_tick {
                    return Ok((next_tick, true));
                }
            }

            // No bits found in current map, move to the previous map (lower ticks)
            if map_index == 0 {
                if self.index == 0 {
                    // No more maps to check in this array
                    return Ok((MIN_TICK, true));
                } else {
                    return Ok((MIN_TICK, false));
                }
            }

            map_index -= 1;
        }
    }

    fn get_most_significant_bit(&self, map_id: usize, byte_idx: usize) -> u32 {
        let bits: u8 = self.tick_has_debt[map_id].children_bits[byte_idx];

        // Find most significant bit in the byte
        bits.leading_zeros()
    }

    fn get_next_tick(&self, map_index: usize) -> Result<(i32, bool)> {
        for byte_idx in (0..TICK_HAS_DEBT_CHILDREN_SIZE).rev() {
            if self.tick_has_debt[map_index].children_bits[byte_idx] != 0 {
                // Find the highest set bit in this byte
                let leading_zeros = self.get_most_significant_bit(map_index, byte_idx);
                // 7 - leading_zeros ensures we count from the left of the byte
                let bit_pos = 7 - leading_zeros as usize;

                // Calculate the tick within the map (0-255)
                let tick_within_map = byte_idx * 8 + bit_pos;

                // Calculate the actual tick value
                let map_first_tick = self.get_first_tick_for_map_index(map_index)?;
                return Ok((map_first_tick + tick_within_map as i32, true));
            }
        }

        Ok((MIN_TICK, false))
    }

    fn get_first_tick_for_map_index(&self, map_index: usize) -> Result<i32> {
        get_first_tick_for_map_in_array(self.index, map_index.cast()?)
    }
}

/// Get the first tick for a given map_index within a specific TickHasDebtArray
pub fn get_first_tick_for_map_in_array(array_index: u8, map_index: u8) -> Result<i32> {
    if array_index >= TOTAL_INDICES_NEEDED as u8 || map_index >= TICK_HAS_DEBT_ARRAY_SIZE as u8 {
        return Err(ErrorCodes::VaultTickHasDebtOutOfRange.into());
    }

    // Each array covers 2048 ticks, each map within array covers 256 ticks
    let array_first_tick = get_first_tick_for_array_index(array_index)?;

    let map_first_tick = array_first_tick
        .safe_add((map_index as i32).safe_mul(TICK_HAS_DEBT_CHILDREN_SIZE_IN_BITS as i32)?)?;

    Ok(map_first_tick)
}