Skip to content

Commit

Permalink
[Caviarnine v1 Adapter v1]: Naming and comments around ticks and bins.
Browse files Browse the repository at this point in the history
  • Loading branch information
0xOmarA committed Feb 15, 2024
1 parent 794a18c commit 791dd20
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 64 deletions.
106 changes: 53 additions & 53 deletions packages/caviarnine-v1-adapter-v1/src/bin_selector.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
//! A module implementing the logic for the selection of the bins to contribute
//! A module implementing the logic for the selection of the ticks to contribute
//! liquidity to based on the current active bin, the bin span, and the maximum
//! number of allowed bins.
//! number of allowed ticks.

#![allow(clippy::arithmetic_side_effects)]

/// Ticks are in the range [0, 54000].
const MAXIMUM_TICK_VALUE: usize = 54000;

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SelectedBins {
pub struct SelectedTicks {
pub active_bin: u32,
pub lower_bins: Vec<u32>,
pub higher_bins: Vec<u32>,
pub lower_ticks: Vec<u32>,
pub higher_ticks: Vec<u32>,
}

impl SelectedBins {
/// Selects the bins that the positions should be made out of.
impl SelectedTicks {
/// Selects the ticks that the positions should be made out of.
///
/// Given the pool's active bin, bin span, and the preferred number of bins
/// we wish to have, this function determines which bins the liquidity
/// Given the pool's active bin, bin span, and the preferred number of ticks
/// we wish to have, this function determines which ticks the liquidity
/// should go to without determining the exact formation of the liquidity
/// (e.g., flat or triangle). This function is specifically suited to handle
/// edge cases when selecting the bin that could lead to failures, and
/// handles such cases in a correct graceful manner.
///
/// In simple cases where the active bin is in the middle of the bin range
/// and the bin span is small enough, the selection of the lower and higher
/// bins is simple enough: preferred number of bins (divided by 2) to the
/// left and the same on the right. This gives the caller the number of bins
/// ticks is simple enough: preferred number of ticks (divided by 2) to the
/// left and the same on the right. This gives the caller the number of ticks
/// that they wish to have on the left and the right of the active bin.
///
/// There are cases however where the number of lower bins can't be equal to
/// the number of higher bins. Specifically, cases when the active bin's
/// There are cases however where the number of lower ticks can't be equal to
/// the number of higher ticks. Specifically, cases when the active bin's
/// value is too small or too large or cases when the bin span is too large.
/// In such cases, this function attempts to compensate the other side. As
/// an example, if we wish to have 10 bins and can only have 2 lower bins,
/// then the higher bins will have 8, thus achieving the preferred number of
/// lower and higher bins specified by the caller. A similar thing happens
/// an example, if we wish to have 10 ticks and can only have 2 lower ticks,
/// then the higher ticks will have 8, thus achieving the preferred number of
/// lower and higher ticks specified by the caller. A similar thing happens
/// if the current active bin is too close to the maximum.
///
/// There are cases when the proffered number of bins can not be achieved by
/// There are cases when the proffered number of ticks can not be achieved by
/// the function, specifically, cases when the bin span is too large that
/// any bin to the right or the left will be outside of the range is allowed
/// bins. In such cases, this function returns a number of left and right
/// bins that is less than the desired.
/// ticks. In such cases, this function returns a number of left and right
/// ticks that is less than the desired.
///
/// # Examples
///
Expand All @@ -54,100 +54,100 @@ impl SelectedBins {
///
/// * `active_bin`: 100
/// * `bin_span`: 10
/// * `preferred_total_number_of_higher_and_lower_bins`: 4
/// * `preferred_total_number_of_higher_and_lower_ticks`: 4
///
/// This function will return the following:
///
/// * `active_bin`: 100
/// * `lower_bins`: [90, 80]
/// * `higher_bins`: [110, 120]
/// * `lower_ticks`: [90, 80]
/// * `higher_ticks`: [110, 120]
///
/// ## Example 2: Left Skew
///
/// * `active_bin`: 20
/// * `bin_span`: 10
/// * `preferred_total_number_of_higher_and_lower_bins`: 6
/// * `preferred_total_number_of_higher_and_lower_ticks`: 6
///
/// This function will return the following:
///
/// * `active_bin`: 20
/// * `lower_bins`: [10, 0]
/// * `higher_bins`: [30, 40, 50, 60]
/// * `lower_ticks`: [10, 0]
/// * `higher_ticks`: [30, 40, 50, 60]
///
/// At this currently active bin, there can only exist 2 bins on the lower
/// side. Thus, these bins are selected and left's remaining share of the
/// bins is given to the right. This results in a total of 6 bins.
/// At this currently active bin, there can only exist 2 ticks on the lower
/// side. Thus, these ticks are selected and left's remaining share of the
/// ticks is given to the right. This results in a total of 6 ticks.
///
/// ## Example 3: Right Skew
///
/// * `active_bin`: 53980
/// * `bin_span`: 10
/// * `preferred_total_number_of_higher_and_lower_bins`: 6
/// * `preferred_total_number_of_higher_and_lower_ticks`: 6
///
/// This function will return the following:
///
/// * `active_bin`: 53980
/// * `lower_bins`: [53970, 53960, 53950, 53940]
/// * `higher_bins`: [53990, 54000]
/// * `lower_ticks`: [53970, 53960, 53950, 53940]
/// * `higher_ticks`: [53990, 54000]
///
/// At this currently active bin, there can only exist 2 bins on the higher
/// side. Thus, these bins are selected and right's remaining share of the
/// bins is given to the left. This results in a total of 6 bins.
/// At this currently active bin, there can only exist 2 ticks on the higher
/// side. Thus, these ticks are selected and right's remaining share of the
/// ticks is given to the left. This results in a total of 6 ticks.
///
/// Example 4: Bin Size too large
///
/// * `active_bin`: 27000
/// * `bin_span`: 54000
/// * `preferred_total_number_of_higher_and_lower_bins`: 6
/// * `preferred_total_number_of_higher_and_lower_ticks`: 6
///
/// This function will return the following:
///
/// * `active_bin`: 27000
/// * `lower_bins`: []
/// * `higher_bins`: []
/// * `lower_ticks`: []
/// * `higher_ticks`: []
///
/// Given this pool's bin span, we can not get any bins that are lower or
/// Given this pool's bin span, we can not get any ticks that are lower or
/// higher and so we return just the active bin and return no lower or
/// higher bins.
/// higher ticks.
///
/// Example 4: Bin Size too large with a skew
///
/// * `active_bin`: 54000
/// * `bin_span`: 30000
/// * `preferred_total_number_of_higher_and_lower_bins`: 6
/// * `preferred_total_number_of_higher_and_lower_ticks`: 6
///
/// This function will return the following:
///
/// * `active_bin`: 54000
/// * `lower_bins`: [24000]
/// * `higher_bins`: []
/// * `lower_ticks`: [24000]
/// * `higher_ticks`: []
///
/// # Arguments:
///
/// * `active_bin`: [`u32`] - The pool's currently active bin.
/// * `bin_span`: [`u32`] - The span between each bin and another or the
/// distance between them.
/// * `preferred_total_number_of_higher_and_lower_bins`: [`u32`] - The total
/// number of bins the caller wishes to have on the right and the left
/// * `preferred_total_number_of_higher_and_lower_ticks`: [`u32`] - The total
/// number of ticks the caller wishes to have on the right and the left
/// (summed). As detailed above, this may or may not be achieved depending
/// on the pool's current bin and bin span.
///
/// # Returns:
///
/// [`SelectedBins`] - A struct with the bins that have been selected by
/// [`SelectedTicks`] - A struct with the ticks that have been selected by
/// this function.
pub fn select(
active_bin: u32,
bin_span: u32,
preferred_total_number_of_higher_and_lower_bins: u32,
preferred_total_number_of_higher_and_lower_ticks: u32,
) -> Self {
let mut selected_bins = Self {
let mut selected_ticks = Self {
active_bin,
higher_bins: vec![],
lower_bins: vec![],
higher_ticks: vec![],
lower_ticks: vec![],
};

let mut remaining = preferred_total_number_of_higher_and_lower_bins;
let mut remaining = preferred_total_number_of_higher_and_lower_ticks;

let mut forward_counter =
BoundedU32::<0, MAXIMUM_TICK_VALUE>(active_bin);
Expand All @@ -160,14 +160,14 @@ impl SelectedBins {

if forward_counter.checked_add_assign(bin_span).is_some() {
remaining -= 1;
selected_bins.higher_bins.push(forward_counter.0);
selected_ticks.higher_ticks.push(forward_counter.0);
forward_counter_incremented = true;
}
if remaining > 0
&& backward_counter.checked_sub_assign(bin_span).is_some()
{
remaining -= 1;
selected_bins.lower_bins.push(backward_counter.0);
selected_ticks.lower_ticks.push(backward_counter.0);
backward_counter_decremented = true;
}

Expand All @@ -176,7 +176,7 @@ impl SelectedBins {
}
}

selected_bins
selected_ticks
}
}

Expand Down
43 changes: 32 additions & 11 deletions packages/caviarnine-v1-adapter-v1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,22 @@ macro_rules! pool {
}

/// The total number of bins that we will be using on the left and the right
/// excluding the one in the middle.
/// excluding the one in the middle. This number, in addition to the bin span
/// of the pool determines how much upside and downside we're covering. The
/// upside and downside we should cover is a business decision and its 20x up
/// and down. To calculate how much bins are needed (on each side) we can do
/// the following:
///
/// ```math
/// bins_required = ceil(log(value = multiplier, base = 1.0005) / (2 * bin_span))
/// ```
///
/// In the case of a bin span of 50, the amount of bins we want to contribute to
/// on each side is 60 bins (60L and 60R). Therefore, the amount of bins to
/// contribute to is dependent on the bin span of the pool. However, in this
/// implementation we assume pools of a fixed bin span of 50 since we can't find
/// the number of bins required in Scrypto due to a missing implementation of a
/// function for computing the log.
pub const PREFERRED_TOTAL_NUMBER_OF_HIGHER_AND_LOWER_BINS: u32 = 60 * 2;

#[blueprint_with_traits]
Expand Down Expand Up @@ -163,20 +178,26 @@ pub mod adapter {

// Select the bins that we will contribute to.
let active_bin = pool.get_active_tick().expect(NO_ACTIVE_BIN_ERROR);
let SelectedBins {
higher_bins,
lower_bins,
let SelectedTicks {
higher_ticks,
lower_ticks,
..
} = SelectedBins::select(
} = SelectedTicks::select(
active_bin,
bin_span,
PREFERRED_TOTAL_NUMBER_OF_HIGHER_AND_LOWER_BINS,
);

// This comment was quickly going out of sync with the constant that
// is defined above of the number of bins to contribute to. Thus, to
// make this simple, let's say that the number of bins to contribute
// to is defined as `m` such that we're contributing to `m` bins to
// the left and `m` to the right.
//
// Determine the amount of resources that we will add to each of the
// bins. We have 20 on the left and 20 on the right. But, we also
// bins. We have m on the left and m on the right. But, we also
// have the active bin that is composed of both x and y. So, this
// be like contributing to 20.x and 20.y bins where x = 1-y. X here
// be like contributing to m.x and m.y bins where x = 1-y. X here
// is the ratio of resources x in the active bin.
let (amount_in_active_bin_x, amount_in_active_bin_y) =
pool.get_active_amounts().expect(NO_ACTIVE_AMOUNTS_ERROR);
Expand Down Expand Up @@ -204,11 +225,11 @@ pub mod adapter {
// positions plus the ratio of y in the active bin since the pool
// starting from the current price and downward is composed just of
// y.
let position_amount_x = Decimal::from(higher_bins.len() as u32)
let position_amount_x = Decimal::from(higher_ticks.len() as u32)
.checked_add(ratio_in_active_bin_x)
.and_then(|value| amount_x.checked_div(value))
.expect(OVERFLOW_ERROR);
let position_amount_y = Decimal::from(lower_bins.len() as u32)
let position_amount_y = Decimal::from(lower_ticks.len() as u32)
.checked_add(ratio_in_active_bin_y)
.and_then(|value| amount_y.checked_div(value))
.expect(OVERFLOW_ERROR);
Expand All @@ -235,12 +256,12 @@ pub mod adapter {
.expect(OVERFLOW_ERROR),
)];
positions.extend(
lower_bins
lower_ticks
.iter()
.map(|bin_id| (*bin_id, dec!(0), position_amount_y)),
);
positions.extend(
higher_bins
higher_ticks
.iter()
.map(|bin_id| (*bin_id, position_amount_x, dec!(0))),
);
Expand Down

0 comments on commit 791dd20

Please sign in to comment.