Skip to content

Commit

Permalink
[Caviarnine v1 Adapter v1]: Calculate the active tick from the price.
Browse files Browse the repository at this point in the history
  • Loading branch information
0xOmarA committed Feb 20, 2024
1 parent 89967e7 commit 2e2f167
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/caviarnine-v1-adapter-v1/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ radix-engine-interface = { workspace = true }
transaction = { workspace = true, optional = true }

scrypto-interface = { path = "../../libraries/scrypto-interface" }
scrypto-math = { path = "../../libraries/scrypto-math" }
ports-interface = { path = "../../libraries/ports-interface" }
common = { path = "../../libraries/common" }

Expand Down
37 changes: 28 additions & 9 deletions packages/caviarnine-v1-adapter-v1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ pub mod adapter {
pub fn liquidity_receipt_data(
// Does not depend on state, this is kept in case this is required
// in the future for whatever reason.
&self,
&mut self,
global_id: NonFungibleGlobalId,
) -> LiquidityReceipt<CaviarnineV1AdapterSpecificInformation> {
// Read the non-fungible data.
Expand Down Expand Up @@ -164,6 +164,23 @@ pub mod adapter {
}
}

pub fn price_and_active_tick(
&mut self,
pool_address: ComponentAddress,
pool_information: Option<PoolInformation>,
) -> Option<(Decimal, u32)> {
let pool_information = pool_information
.unwrap_or(self.get_pool_information(pool_address));
let pool = pool!(pool_address);
let price = pool.get_price()?;
let active_tick = spot_to_tick(price)
.and_then(|value| value.checked_div(pool_information.bin_span))
.and_then(|value| {
value.checked_mul(pool_information.bin_span)
})?;
Some((price, active_tick))
}

fn get_pool_information(
&mut self,
pool_address: ComponentAddress,
Expand All @@ -188,7 +205,7 @@ pub mod adapter {

// Split the two buckets into bucket_x and bucket_y in the same way
// that they're defined in the pool itself.
let PoolInformation {
let pool_information @ PoolInformation {
bin_span,
resources:
ResourceIndexedData {
Expand Down Expand Up @@ -216,8 +233,10 @@ pub mod adapter {
let amount_y = bucket_y.amount();

// Select the bins that we will contribute to.
let active_tick =
pool.get_active_tick().expect(NO_ACTIVE_BIN_ERROR);
let (price, active_tick) = self
.price_and_active_tick(pool_address, Some(pool_information))
.expect(NO_PRICE_ERROR);

let SelectedTicks {
higher_ticks,
lower_ticks,
Expand All @@ -241,7 +260,6 @@ pub mod adapter {
// is the percentage 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);
let price = pool.get_price().expect(NO_PRICE_ERROR);

let percentage_in_active_bin_x = amount_in_active_bin_x
.checked_mul(price)
Expand Down Expand Up @@ -355,16 +373,17 @@ pub mod adapter {
adapter_specific_information: AnyValue,
) -> CloseLiquidityPositionOutput {
let mut pool = pool!(pool_address);
let PoolInformation {
let pool_information @ PoolInformation {
bin_span,
resources:
ResourceIndexedData {
resource_x,
resource_y,
},
} = self.get_pool_information(pool_address);
let active_tick =
pool.get_active_tick().expect(NO_ACTIVE_BIN_ERROR);
let (price, active_tick) = self
.price_and_active_tick(pool_address, Some(pool_information))
.expect(NO_PRICE_ERROR);

// Decoding the adapter specific information as the type we expect
// it to be.
Expand All @@ -385,7 +404,7 @@ pub mod adapter {
.into_iter()
.map(|value| (value.0, value.1))
.collect::<Vec<_>>(),
pool.get_price().expect(NO_PRICE_ERROR),
price,
price_when_position_was_opened,
active_tick,
bin_span,
Expand Down
10 changes: 10 additions & 0 deletions packages/caviarnine-v1-adapter-v1/src/tick_math.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use scrypto::prelude::*;
use scrypto_math::*;

pub const BASE: Decimal = dec!(1.0005);
pub const MIN_TICK: u32 = 0;
Expand All @@ -12,3 +13,12 @@ pub fn tick_to_spot(tick: u32) -> Option<Decimal> {
BASE.checked_powi((tick as i64).checked_sub(27000)?.checked_mul(2)?)
}
}

pub fn spot_to_tick(price: Decimal) -> Option<u32> {
price
.log_base(BASE)
.and_then(|value| value.checked_div(dec!(2)))
.and_then(|value| value.checked_add(dec!(27000)))
.and_then(|value| value.0.checked_div(Decimal::ONE.0))
.and_then(|value| u32::try_from(value).ok())
}
65 changes: 64 additions & 1 deletion tests/tests/caviarnine_v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub fn liquidity_receipt_information_can_be_read_through_adapter(
let Environment {
environment: ref mut env,
mut protocol,
caviarnine_v1,
mut caviarnine_v1,
resources,
..
} = ScryptoTestEnv::new()?;
Expand Down Expand Up @@ -791,3 +791,66 @@ fn approximately_equals(a: Decimal, b: Decimal) -> bool {
};
difference <= dec!(0.000001)
}

#[test]
pub fn price_and_active_tick_reported_by_adapter_must_match_whats_reported_by_pool(
) -> Result<(), RuntimeError> {
// Arrange
let Environment {
environment: ref mut env,
mut caviarnine_v1,
resources,
..
} = ScryptoTestEnv::new()?;
env.disable_limits_module();

let bin_span = 100u32;
for bin in (0u32..=54000u32).step_by(bin_span as usize) {
let mut pool = CaviarnineV1PoolInterfaceScryptoTestStub::new(
rule!(allow_all),
rule!(allow_all),
XRD,
resources.bitcoin,
bin_span,
None,
caviarnine_v1.package,
env,
)?;

let bucket_x = ResourceManager(XRD).mint_fungible(dec!(100), env)?;
let bucket_y =
ResourceManager(resources.bitcoin).mint_fungible(dec!(100), env)?;

let _ = pool.add_liquidity(
bucket_x,
bucket_y,
vec![(bin, dec!(100), dec!(100))],
env,
)?;

// Act
let (adapter_reported_price, adapter_reported_active_tick) =
caviarnine_v1
.adapter
.price_and_active_tick(
pool.try_into().unwrap(),
Some(PoolInformation {
bin_span,
resources: ResourceIndexedData {
resource_x: XRD,
resource_y: resources.bitcoin,
},
}),
env,
)?
.unwrap();

// Assert
let pool_reported_price = pool.get_price(env)?.unwrap();
let pool_reported_active_tick = pool.get_active_tick(env)?.unwrap();
assert_eq!(adapter_reported_price, pool_reported_price);
assert_eq!(adapter_reported_active_tick, pool_reported_active_tick);
}

Ok(())
}

0 comments on commit 2e2f167

Please sign in to comment.