From 3884c4bdf8cdec465f9293cad90e5cde4e879ea7 Mon Sep 17 00:00:00 2001 From: Vim Wickramasinghe Date: Fri, 18 Mar 2022 12:26:57 +0100 Subject: [PATCH 1/6] [cu-23v2xng][Pablo] Integrate Liquidity Bootstrapping Pool --- frame/composable-support/src/validation.rs | 2 +- frame/composable-traits/src/dex.rs | 102 ++- frame/pablo/src/lib.rs | 157 ++++- frame/pablo/src/liquidity_bootstrapping.rs | 211 ++++++ .../src/liquidity_bootstrapping_tests.rs | 658 ++++++++++++++++++ frame/pablo/src/mock.rs | 14 +- frame/pablo/src/uniswap.rs | 4 +- 7 files changed, 1128 insertions(+), 20 deletions(-) create mode 100644 frame/pablo/src/liquidity_bootstrapping.rs create mode 100644 frame/pablo/src/liquidity_bootstrapping_tests.rs diff --git a/frame/composable-support/src/validation.rs b/frame/composable-support/src/validation.rs index b222dcc0616..0d2e352b9db 100644 --- a/frame/composable-support/src/validation.rs +++ b/frame/composable-support/src/validation.rs @@ -1,7 +1,7 @@ //! Module for validating extrinsic inputs //! //! This module is made of two main parts that are needed to validate an -//! extrinsic input, the `Validated` struct and the `Valitate` trait. +//! extrinsic input, the `Validated` struct and the `Validate` trait. //! //! # Example //! ## Single Validation diff --git a/frame/composable-traits/src/dex.rs b/frame/composable-traits/src/dex.rs index b276e24b793..a38b7130007 100644 --- a/frame/composable-traits/src/dex.rs +++ b/frame/composable-traits/src/dex.rs @@ -1,11 +1,17 @@ +use crate::{ + defi::CurrencyPair, + math::{SafeAdd, SafeSub}, +}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{traits::Get, BoundedVec, RuntimeDebug}; use scale_info::TypeInfo; -use sp_runtime::{DispatchError, Permill}; +use sp_arithmetic::traits::Saturating; +use sp_runtime::{ + traits::{CheckedMul, CheckedSub}, + ArithmeticError, DispatchError, Permill, +}; use sp_std::vec::Vec; -use crate::defi::CurrencyPair; - /// Trait for automated market maker. pub trait Amm { /// The asset ID type @@ -144,6 +150,96 @@ pub struct ConstantProductPoolInfo { pub owner_fee: Permill, } +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum SaleState { + NotStarted, + Ongoing, + Ended, +} + +#[derive(RuntimeDebug, Encode, Decode, MaxEncodedLen, Copy, Clone, PartialEq, Eq, TypeInfo)] +pub struct Sale { + /// Block at which the sale start. + pub start: BlockNumber, + /// Block at which the sale stop. + pub end: BlockNumber, + /// Initial weight of the base asset of the current pair. + pub initial_weight: Permill, + /// Final weight of the base asset of the current pair. + pub final_weight: Permill, +} + +impl + Ord + Copy + Saturating + SafeAdd + SafeSub> Sale { + // TODO unit test + pub fn current_weights( + &self, + current_block: BlockNumber, + ) -> Result<(Permill, Permill), DispatchError> { + /* NOTE(hussein-aitlahcen): currently only linear + + Linearly decrease the base asset initial_weight to final_weight. + Quote asset weight is simple 1-base_asset_weight + + Assuming final_weight < initial_weight + current_weight = initial_weight - (current - start) / (end - start) * (initial_weight - final_weight) + = initial_weight - normalized_current / sale_duration * weight_range + = initial_weight - point_in_sale * weight_range + */ + let normalized_current_block = current_block.safe_sub(&self.start)?; + let point_in_sale = Permill::from_rational( + normalized_current_block.try_into().map_err(|_| ArithmeticError::Overflow)?, + self.duration().try_into().map_err(|_| ArithmeticError::Overflow)?, + ); + let weight_range = self + .initial_weight + .checked_sub(&self.final_weight) + .ok_or(ArithmeticError::Underflow)?; + let current_base_weight = self + .initial_weight + .checked_sub( + &point_in_sale.checked_mul(&weight_range).ok_or(ArithmeticError::Overflow)?, + ) + .ok_or(ArithmeticError::Underflow)?; + let current_quote_weight = Permill::one() + .checked_sub(¤t_base_weight) + .ok_or(ArithmeticError::Underflow)?; + Ok((current_base_weight, current_quote_weight)) + } +} + +impl Sale { + pub fn duration(&self) -> BlockNumber { + // NOTE(hussein-aitlahcen): end > start as previously checked by PoolIsValid. + self.end.saturating_sub(self.start) + } +} + +impl Sale { + pub fn state(&self, current_block: BlockNumber) -> SaleState { + if current_block < self.start { + SaleState::NotStarted + } else if current_block >= self.end { + SaleState::Ended + } else { + SaleState::Ongoing + } + } +} + +#[derive(RuntimeDebug, Encode, Decode, MaxEncodedLen, Copy, Clone, PartialEq, Eq, TypeInfo)] +pub struct LiquidityBootstrappingPoolInfo { + /// Owner of the pool + pub owner: AccountId, + /// Asset pair of the pool along their weight. + /// Base asset is the project token. + /// Quote asset is the collateral token. + pub pair: CurrencyPair, + /// Sale period of the LBP. + pub sale: Sale, + /// Trading fees. + pub fee: Permill, +} + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Eq, RuntimeDebug)] pub enum DexRouteNode { Curve(PoolId), diff --git a/frame/pablo/src/lib.rs b/frame/pablo/src/lib.rs index 67b565836d1..9eca4611d8d 100644 --- a/frame/pablo/src/lib.rs +++ b/frame/pablo/src/lib.rs @@ -38,12 +38,15 @@ pub use pallet::*; #[cfg(test)] mod common_test_functions; #[cfg(test)] +mod liquidity_bootstrapping_tests; +#[cfg(test)] mod mock; #[cfg(test)] mod stable_swap_tests; #[cfg(test)] mod uniswap_tests; +mod liquidity_bootstrapping; mod stable_swap; mod uniswap; @@ -64,41 +67,62 @@ pub mod pallet { transactional, PalletId, RuntimeDebug, }; - use frame_system::{ensure_signed, pallet_prelude::OriginFor}; + use crate::liquidity_bootstrapping::LiquidityBootstrapping; + use composable_support::validation::Validated; + use composable_traits::{currency::LocalAssets, dex::LiquidityBootstrappingPoolInfo}; + use frame_system::{ + ensure_signed, + pallet_prelude::{BlockNumberFor, OriginFor}, + }; use sp_runtime::{ - traits::{AccountIdConversion, Convert, One, Zero}, + traits::{AccountIdConversion, BlockNumberProvider, Convert, One, Zero}, Permill, }; #[derive(RuntimeDebug, Encode, Decode, MaxEncodedLen, Clone, PartialEq, Eq, TypeInfo)] - pub enum PoolInitConfiguration { + pub enum PoolInitConfiguration { StableSwap { + // TODO consider adding the owner here to allow a third party owner pair: CurrencyPair, amplification_coefficient: u16, fee: Permill, protocol_fee: Permill, }, ConstantProduct { + // TODO consider adding the owner here to allow a third party owner pair: CurrencyPair, fee: Permill, owner_fee: Permill, }, + LiquidityBootstrapping(LiquidityBootstrappingPoolInfo), } #[derive(RuntimeDebug, Encode, Decode, MaxEncodedLen, Clone, PartialEq, Eq, TypeInfo)] - pub enum PoolConfiguration { + pub enum PoolConfiguration { StableSwap(StableSwapPoolInfo), ConstantProduct(ConstantProductPoolInfo), + LiquidityBootstrapping(LiquidityBootstrappingPoolInfo), } - type AssetIdOf = ::AssetId; - type BalanceOf = ::Balance; - type AccountIdOf = ::AccountId; + pub(crate) type AssetIdOf = ::AssetId; + pub(crate) type BalanceOf = ::Balance; + pub(crate) type AccountIdOf = ::AccountId; + pub(crate) type LiquidityBootstrappingPoolInfoOf = LiquidityBootstrappingPoolInfo< + ::AccountId, + ::AssetId, + ::BlockNumber, + >; type PoolIdOf = ::PoolId; - type PoolConfigurationOf = - PoolConfiguration<::AccountId, ::AssetId>; - type PoolInitConfigurationOf = PoolInitConfiguration<::AssetId>; - + type PoolConfigurationOf = PoolConfiguration< + ::AccountId, + ::AssetId, + ::BlockNumber, + >; + type PoolInitConfigurationOf = PoolInitConfiguration< + ::AccountId, + ::AssetId, + ::BlockNumber, + >; // TODO refactor event publishing with cu-23v2y3n #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -240,6 +264,25 @@ pub mod pallet { #[pallet::constant] type PalletId: Get; + + // Why this is + type LocalAssets: LocalAssets>; + + /// Minimum duration for a sale. + #[pallet::constant] + type LbpMinSaleDuration: Get>; + + /// Maximum duration for a sale. + #[pallet::constant] + type LbpMaxSaleDuration: Get>; + + /// Maximum initial weight. + #[pallet::constant] + type LbpMaxInitialWeight: Get; + + /// Minimum final weight. + #[pallet::constant] + type LbpMinFinalWeight: Get; } #[pallet::pallet] @@ -407,6 +450,13 @@ pub mod pallet { Self::deposit_event(Event::PoolCreated { owner: who.clone(), pool_id }); Ok(pool_id) }, + PoolInitConfiguration::LiquidityBootstrapping(pool_config) => { + let validated_pool_config = Validated::new(pool_config)?; + let pool_id = + LiquidityBootstrapping::::do_create_pool(validated_pool_config)?; + Self::deposit_event(Event::PoolCreated { owner: who.clone(), pool_id }); + Ok(pool_id) + }, } } @@ -439,6 +489,8 @@ pub mod pallet { PoolConfiguration::StableSwap(stable_swap_pool_info) => Ok(stable_swap_pool_info.pair), ConstantProduct(constant_product_pool_info) => Ok(constant_product_pool_info.pair), + PoolConfiguration::LiquidityBootstrapping(liquidity_bootstrapping_pool_info) => + Ok(liquidity_bootstrapping_pool_info.pair), } } @@ -463,6 +515,13 @@ pub mod pallet { asset_id, amount, ), + PoolConfiguration::LiquidityBootstrapping(liquidity_bootstrapping_pool_info) => + LiquidityBootstrapping::::get_exchange_value( + liquidity_bootstrapping_pool_info, + pool_account, + asset_id, + amount, + ), } } @@ -514,6 +573,24 @@ pub mod pallet { minted_lp: mint_amount, }); }, + PoolConfiguration::LiquidityBootstrapping(liquidity_bootstrapping_pool_info) => { + LiquidityBootstrapping::::add_liquidity( + who, + liquidity_bootstrapping_pool_info, + pool_account, + base_amount, + quote_amount, + min_mint_amount, + keep_alive, + )?; + Self::deposit_event(Event::::LiquidityAdded { + who: who.clone(), + pool_id, + base_amount, + quote_amount, + minted_lp: T::Balance::zero(), + }); + }, } // TODO refactor event publishing with cu-23v2y3n Ok(()) @@ -565,8 +642,20 @@ pub mod pallet { total_issuance: updated_lp, }); }, + PoolConfiguration::LiquidityBootstrapping(liquidity_bootstrapping_pool_info) => { + let (base_amount, quote_amount) = + LiquidityBootstrapping::::remove_liquidity( + who, + pool_id, + liquidity_bootstrapping_pool_info, + pool_account, + lp_amount, + min_base_amount, + min_quote_amount, + )?; + Self::deposit_event(Event::PoolDeleted { pool_id, base_amount, quote_amount }); + }, } - // TODO refactor event publishing with cu-23v2y3n Ok(()) } @@ -666,6 +755,33 @@ pub mod pallet { Ok(base_amount) }, + PoolConfiguration::LiquidityBootstrapping(liquidity_bootstrapping_pool_info) => { + let current_block = frame_system::Pallet::::current_block_number(); + let (fees, base_amount) = LiquidityBootstrapping::::do_get_exchange( + liquidity_bootstrapping_pool_info, + &pool_account, + pair, + current_block, + quote_amount, + true, + )?; + + ensure!(base_amount >= min_receive, Error::::CannotRespectMinimumRequested); + + T::Assets::transfer(pair.quote, who, &pool_account, quote_amount, keep_alive)?; + // NOTE(hussein-aitlance): no need to keep alive the pool account + T::Assets::transfer(pair.base, &pool_account, who, base_amount, false)?; + Self::deposit_event(Event::::Swapped { + pool_id, + who: who.clone(), + base_asset: pair.base, + quote_asset: pair.quote, + base_amount, + quote_amount, + fee: fees, + }); + Ok(base_amount) + }, } // TODO refactor event publishing with cu-23v2y3n @@ -701,6 +817,15 @@ pub mod pallet { let quote_amount = Self::get_exchange_value(pool_id, asset_id, amount)?; Self::exchange(who, pool_id, pair, quote_amount, T::Balance::zero(), keep_alive) }, + PoolConfiguration::LiquidityBootstrapping(liquidity_bootstrapping_pool_info) => { + let pair = if asset_id == liquidity_bootstrapping_pool_info.pair.base { + liquidity_bootstrapping_pool_info.pair + } else { + liquidity_bootstrapping_pool_info.pair.swap() + }; + let quote_amount = Self::get_exchange_value(pool_id, asset_id, amount)?; + Self::exchange(who, pool_id, pair, quote_amount, T::Balance::zero(), keep_alive) + }, } } @@ -727,6 +852,14 @@ pub mod pallet { }; Self::exchange(who, pool_id, pair, amount, T::Balance::zero(), keep_alive) }, + PoolConfiguration::LiquidityBootstrapping(liquidity_bootstrapping_pool_info) => { + let pair = if asset_id == liquidity_bootstrapping_pool_info.pair.base { + liquidity_bootstrapping_pool_info.pair.swap() + } else { + liquidity_bootstrapping_pool_info.pair + }; + Self::exchange(who, pool_id, pair, amount, T::Balance::zero(), keep_alive) + }, } } } diff --git a/frame/pablo/src/liquidity_bootstrapping.rs b/frame/pablo/src/liquidity_bootstrapping.rs new file mode 100644 index 00000000000..00708b99bb2 --- /dev/null +++ b/frame/pablo/src/liquidity_bootstrapping.rs @@ -0,0 +1,211 @@ +use crate::{ + AccountIdOf, AssetIdOf, BalanceOf, Config, Error, LiquidityBootstrappingPoolInfoOf, + PoolConfiguration, PoolCount, Pools, +}; +use composable_maths::dex::constant_product::{compute_out_given_in, compute_spot_price}; +use composable_support::validation::{Validate, Validated}; +use composable_traits::{currency::LocalAssets, defi::CurrencyPair, dex::SaleState, math::SafeAdd}; +use frame_support::{ + pallet_prelude::*, + traits::fungibles::{Inspect, Transfer}, + transactional, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_runtime::traits::{BlockNumberProvider, Convert, One, Zero}; +use std::marker::PhantomData; + +#[derive(Copy, Clone, Encode, Decode, MaxEncodedLen, PartialEq, Eq, TypeInfo)] +pub struct PoolIsValid(PhantomData); + +impl Validate, PoolIsValid> for PoolIsValid { + fn validate( + input: LiquidityBootstrappingPoolInfoOf, + ) -> Result, &'static str> { + if input.pair.base == input.pair.quote { + return Err("Pair elements must be distinct.") + } + + if input.sale.end <= input.sale.start { + return Err("Sale end must be after start.") + } + + if input.sale.duration() < T::LbpMinSaleDuration::get() { + return Err("Sale duration must be greater than minimum duration.") + } + + if input.sale.duration() > T::LbpMaxSaleDuration::get() { + return Err("Sale duration must not exceed maximum duration.") + } + + if input.sale.initial_weight < input.sale.final_weight { + return Err("Initial weight must be greater than final weight.") + } + + if input.sale.initial_weight > T::LbpMaxInitialWeight::get() { + return Err("Initial weight must not exceed the defined maximum.") + } + + if input.sale.final_weight < T::LbpMinFinalWeight::get() { + return Err("Final weight must not be lower than the defined minimum.") + } + + Ok(input) + } +} + +pub(crate) struct LiquidityBootstrapping(PhantomData); + +impl LiquidityBootstrapping { + pub(crate) fn do_create_pool( + pool: Validated, PoolIsValid>, + ) -> Result { + let pool_id = + PoolCount::::try_mutate(|pool_count| -> Result { + let pool_id = *pool_count; + Pools::::insert( + pool_id, + PoolConfiguration::LiquidityBootstrapping(pool.clone().value()), + ); + *pool_count = pool_id.safe_add(&T::PoolId::one())?; + Ok(pool_id) + })?; + + Ok(pool_id) + } + + pub fn ensure_sale_state( + pool: &LiquidityBootstrappingPoolInfoOf, + current_block: BlockNumberFor, + expected_sale_state: SaleState, + ) -> Result<(), DispatchError> { + ensure!( + pool.sale.state(current_block) == expected_sale_state, + Error::::InvalidSaleState + ); + Ok(()) + } + + #[allow(dead_code)] + pub(crate) fn do_spot_price( + pool: LiquidityBootstrappingPoolInfoOf, + pool_account: AccountIdOf, + pair: CurrencyPair>, + current_block: BlockNumberFor, + ) -> Result, DispatchError> { + Self::ensure_sale_state(&pool, current_block, SaleState::Ongoing)?; + ensure!(pair == pool.pair, Error::::PairMismatch); + + let weights = pool.sale.current_weights(current_block)?; + + let (wo, wi) = if pair.base == pool.pair.base { weights } else { (weights.1, weights.0) }; + + let bi = T::Convert::convert(T::Assets::balance(pair.quote, &pool_account)); + let bo = T::Convert::convert(T::Assets::balance(pair.base, &pool_account)); + let base_unit = T::LocalAssets::unit::(pair.base)?; + + let spot_price = compute_spot_price(wi, wo, bi, bo, base_unit)?; + + Ok(T::Convert::convert(spot_price)) + } + + pub(crate) fn do_get_exchange( + pool: LiquidityBootstrappingPoolInfoOf, + pool_account: &AccountIdOf, + pair: CurrencyPair>, + current_block: BlockNumberFor, + quote_amount: BalanceOf, + apply_fees: bool, + ) -> Result<(BalanceOf, BalanceOf), DispatchError> { + Self::ensure_sale_state(&pool, current_block, SaleState::Ongoing)?; + + ensure!(pair == pool.pair, Error::::PairMismatch); + ensure!(!quote_amount.is_zero(), Error::::InvalidAmount); + + let weights = pool.sale.current_weights(current_block)?; + + let (wo, wi) = if pair.base == pool.pair.base { weights } else { (weights.1, weights.0) }; + + let ai = T::Convert::convert(quote_amount); + let (ai_minus_fees, fees) = if apply_fees { + let fees = pool.fee.mul_floor(ai); + // Safe as fees is a fraction of ai + (ai - fees, fees) + } else { + (ai, 0) + }; + let bi = T::Convert::convert(T::Assets::balance(pair.quote, &pool_account)); + let bo = T::Convert::convert(T::Assets::balance(pair.base, &pool_account)); + + let base_amount = compute_out_given_in(wi, wo, bi, bo, ai_minus_fees)?; + + Ok((T::Convert::convert(fees), T::Convert::convert(base_amount))) + } + + pub(crate) fn get_exchange_value( + pool: LiquidityBootstrappingPoolInfoOf, + pool_account: AccountIdOf, + asset_id: T::AssetId, + amount: T::Balance, + ) -> Result { + let pair = if asset_id == pool.pair.base { pool.pair.swap() } else { pool.pair }; + let current_block = frame_system::Pallet::::current_block_number(); + let (_, base_amount) = + Self::do_get_exchange(pool, &pool_account, pair, current_block, amount, false)?; + Ok(base_amount) + } + + #[transactional] + pub(crate) fn add_liquidity( + who: &T::AccountId, + pool: LiquidityBootstrappingPoolInfoOf, + pool_account: AccountIdOf, + base_amount: T::Balance, + quote_amount: T::Balance, + _: T::Balance, + keep_alive: bool, + ) -> Result<(), DispatchError> { + let current_block = frame_system::Pallet::::current_block_number(); + Self::ensure_sale_state(&pool, current_block, SaleState::NotStarted)?; + + ensure!(pool.owner == *who, Error::::MustBeOwner); + ensure!(!base_amount.is_zero() && !quote_amount.is_zero(), Error::::InvalidAmount); + + // NOTE(hussein-aitlahcen): as we only allow the owner to provide liquidity, we don't + // mint any LP. + T::Assets::transfer(pool.pair.base, who, &pool_account, base_amount, keep_alive)?; + T::Assets::transfer(pool.pair.quote, who, &pool_account, quote_amount, keep_alive)?; + + // T::deposit_event(Event::LiquidityAdded { pool_id, base_amount, quote_amount }); + + Ok(()) + } + + #[transactional] + pub(crate) fn remove_liquidity( + who: &T::AccountId, + pool_id: T::PoolId, + pool: LiquidityBootstrappingPoolInfoOf, + pool_account: AccountIdOf, + _: T::Balance, + _: T::Balance, + _: T::Balance, + ) -> Result<(BalanceOf, BalanceOf), DispatchError> { + let current_block = frame_system::Pallet::::current_block_number(); + Self::ensure_sale_state(&pool, current_block, SaleState::Ended)?; + + ensure!(pool.owner == *who, Error::::MustBeOwner); + + let repatriate = |a| -> Result, DispatchError> { + let a_balance = T::Assets::balance(a, &pool_account); + // NOTE(hussein-aitlahcen): not need to keep the pool account alive. + T::Assets::transfer(a, &pool_account, who, a_balance, false)?; + Ok(a_balance) + }; + + let base_amount = repatriate(pool.pair.base)?; + let quote_amount = repatriate(pool.pair.quote)?; + + Pools::::remove(pool_id); + Ok((base_amount, quote_amount)) + } +} diff --git a/frame/pablo/src/liquidity_bootstrapping_tests.rs b/frame/pablo/src/liquidity_bootstrapping_tests.rs new file mode 100644 index 00000000000..2e8da5fbea7 --- /dev/null +++ b/frame/pablo/src/liquidity_bootstrapping_tests.rs @@ -0,0 +1,658 @@ +use crate::{ + liquidity_bootstrapping::PoolIsValid, + mock::{Pablo, *}, + Error, PoolInitConfiguration, +}; +use composable_support::validation::Validated; +use composable_tests_helpers::test::helper::default_acceptable_computation_error; +use composable_traits::{ + defi::CurrencyPair, + dex::{LiquidityBootstrappingPoolInfo, Sale}, +}; +use frame_support::{ + assert_noop, assert_ok, + traits::fungibles::{Inspect, Mutate}, +}; +use sp_runtime::Permill; + +fn valid_pool( +) -> Validated, PoolIsValid> { + let pair = CurrencyPair::new(PROJECT_TOKEN, USDT); + let owner = ALICE; + let duration = MaxSaleDuration::get(); + let start = 0; + let end = start + duration; + let initial_weight = MaxInitialWeight::get(); + let final_weight = MinFinalWeight::get(); + let fee = Permill::from_perthousand(1); + Validated::new(LiquidityBootstrappingPoolInfo { + owner, + pair, + sale: Sale { start, end, initial_weight, final_weight }, + fee, + }) + .expect("impossible; qed;") +} + +fn with_pool( + owner: AccountId, + sale_duration: BlockNumber, + initial_weight: Permill, + final_weight: Permill, + fee: Permill, + f: impl FnOnce( + PoolId, + &LiquidityBootstrappingPoolInfo, + &dyn Fn(BlockNumber), + &dyn Fn(), + ) -> T, +) -> T { + let random_start = 0xDEADC0DE; + let pair = CurrencyPair::new(PROJECT_TOKEN, USDT); + let end = random_start + sale_duration; + let pool = Validated::<_, PoolIsValid>::new(LiquidityBootstrappingPoolInfo { + owner, + pair, + sale: Sale { start: random_start, end, initial_weight, final_weight }, + fee, + }) + .expect("impossible; qed;"); + new_test_ext().execute_with(|| -> T { + // Actually create the pool. + assert_ok!(Pablo::create( + Origin::signed(ALICE), + PoolInitConfiguration::LiquidityBootstrapping(pool.value()) + )); + + // Will always start to 0. + let pool_id = 0; + + // Relative to sale start. + let set_block = |x: BlockNumber| { + System::set_block_number(random_start + x); + }; + + // Forward to sale end. + let end_sale = || { + set_block(sale_duration + 1); + }; + + f(pool_id, &pool, &set_block, &end_sale) + }) +} + +fn within_sale_with_liquidity( + owner: AccountId, + sale_duration: BlockNumber, + initial_weight: Permill, + final_weight: Permill, + fee: Permill, + initial_project_tokens: Balance, + initial_usdt: Balance, + f: impl FnOnce( + PoolId, + &LiquidityBootstrappingPoolInfo, + &dyn Fn(BlockNumber), + &dyn Fn(), + ) -> T, +) -> T { + with_pool( + owner, + sale_duration, + initial_weight, + final_weight, + fee, + |pool_id, pool, set_block, end_sale| -> T { + assert_ok!(Tokens::mint_into(PROJECT_TOKEN, &owner, initial_project_tokens)); + assert_ok!(Tokens::mint_into(USDT, &owner, initial_usdt)); + + // Add initial liquidity. + assert_ok!(Pablo::add_liquidity( + Origin::signed(owner), + pool_id, + initial_project_tokens, + initial_usdt, + 0_u128, + false + )); + + // Actually start the sale. + set_block(0); + + f(pool_id, pool, set_block, end_sale) + }, + ) +} + +mod create { + use super::*; + + #[test] + fn arbitrary_user_can_create() { + new_test_ext().execute_with(|| { + assert_ok!(Pablo::create( + Origin::signed(ALICE), + PoolInitConfiguration::LiquidityBootstrapping(valid_pool().value()) + ),); + }); + } + + // #[test] + // fn admin_can_create() { + // new_test_ext().execute_with(|| { + // assert_ok!(Pablo::create(Origin::root(), + // PoolInitConfiguration::LiquidityBootstrapping(valid_pool().value()))); }); + // } +} + +mod sell { + use super::*; + + #[test] + fn can_sell_one_to_one() { + /* 50% weight = constant product, no fees. + */ + let unit = 1_000_000_000_000; + let initial_project_tokens = 1_000_000 * unit; + let initial_usdt = 1_000_000 * unit; + let sale_duration = MaxSaleDuration::get(); + let initial_weight = Permill::one() / 2; + let final_weight = Permill::one() / 2; + let fee = Permill::zero(); + within_sale_with_liquidity( + ALICE, + sale_duration, + initial_weight, + final_weight, + fee, + initial_project_tokens, + initial_usdt, + |pool_id, pool, _, _| { + // Buy project token + assert_ok!(Tokens::mint_into(USDT, &BOB, unit)); + assert_ok!(Pablo::sell(Origin::signed(BOB), pool_id, pool.pair.quote, unit, false)); + assert_ok!(default_acceptable_computation_error( + Tokens::balance(PROJECT_TOKEN, &BOB), + unit + )); + }, + ) + } +} + +mod buy { + use super::*; + + #[test] + fn can_buy_one_to_one() { + /* 50% weight = constant product, no fees. + */ + let unit = 1_000_000_000_000; + let initial_project_tokens = 1_000_000 * unit; + let initial_usdt = 1_000_000 * unit; + let sale_duration = MaxSaleDuration::get(); + let initial_weight = Permill::one() / 2; + let final_weight = Permill::one() / 2; + let fee = Permill::zero(); + within_sale_with_liquidity( + ALICE, + sale_duration, + initial_weight, + final_weight, + fee, + initial_project_tokens, + initial_usdt, + |pool_id, pool, _, _| { + // Buy project token + assert_ok!(Tokens::mint_into(USDT, &BOB, unit)); + assert_ok!(Pablo::buy(Origin::signed(BOB), pool_id, pool.pair.base, unit, false)); + assert_ok!(default_acceptable_computation_error( + Tokens::balance(PROJECT_TOKEN, &BOB), + unit + )); + }, + ) + } +} + +mod remove_liquidity { + use super::*; + + #[test] + fn cannot_remove_before_sale_end() { + let owner = ALICE; + let sale_duration = MaxSaleDuration::get(); + let initial_weight = Permill::one() / 2; + let final_weight = Permill::one() / 2; + let fee = Permill::zero(); + let unit = 1_000_000_000_000; + let initial_project_tokens = 1_000_000 * unit; + let initial_usdt = 1_000_000 * unit; + with_pool(owner, sale_duration, initial_weight, final_weight, fee, |pool_id, _, _, _| { + assert_ok!(Tokens::mint_into(PROJECT_TOKEN, &owner, initial_project_tokens)); + assert_ok!(Tokens::mint_into(USDT, &owner, initial_usdt)); + assert_noop!( + Pablo::remove_liquidity(Origin::signed(owner), pool_id, 0, 0, 0), + Error::::InvalidSaleState + ); + }); + } + + #[test] + fn can_remove_after_sale_end() { + let owner = ALICE; + let sale_duration = MaxSaleDuration::get(); + let initial_weight = Permill::one() / 2; + let final_weight = Permill::one() / 2; + let fee = Permill::zero(); + let unit = 1_000_000_000_000; + let initial_project_tokens = 1_000_000 * unit; + let initial_usdt = 1_000_000 * unit; + with_pool( + owner, + sale_duration, + initial_weight, + final_weight, + fee, + |pool_id, _, _, end_sale| { + assert_ok!(Tokens::mint_into(PROJECT_TOKEN, &owner, initial_project_tokens)); + assert_ok!(Tokens::mint_into(USDT, &owner, initial_usdt)); + end_sale(); + assert_ok!(Pablo::remove_liquidity(Origin::signed(owner), pool_id, 0, 0, 0)); + assert_eq!(Tokens::balance(PROJECT_TOKEN, &owner), initial_project_tokens); + assert_eq!(Tokens::balance(USDT, &owner), initial_usdt); + }, + ); + } +} + +mod add_liquidity { + use super::*; + + #[test] + fn can_add_liquidity_before_sale() { + let owner = ALICE; + let sale_duration = MaxSaleDuration::get(); + let initial_weight = Permill::one() / 2; + let final_weight = Permill::one() / 2; + let fee = Permill::zero(); + let unit = 1_000_000_000_000; + let initial_project_tokens = 1_000_000 * unit; + let initial_usdt = 1_000_000 * unit; + with_pool(owner, sale_duration, initial_weight, final_weight, fee, |pool_id, _, _, _| { + assert_ok!(Tokens::mint_into(PROJECT_TOKEN, &owner, initial_project_tokens)); + assert_ok!(Tokens::mint_into(USDT, &owner, initial_usdt)); + assert_ok!(Pablo::add_liquidity( + Origin::signed(owner), + pool_id, + initial_project_tokens, + initial_usdt, + 0_128, + false + )); + }); + } + + #[test] + fn cannot_add_liquidity_after_sale_started() { + let owner = ALICE; + let sale_duration = MaxSaleDuration::get(); + let initial_weight = Permill::one() / 2; + let final_weight = Permill::one() / 2; + let fee = Permill::zero(); + let unit = 1_000_000_000_000; + let initial_project_tokens = 1_000_000 * unit; + let initial_usdt = 1_000_000 * unit; + with_pool( + owner, + sale_duration, + initial_weight, + final_weight, + fee, + |pool_id, _, set_sale_block, _| { + assert_ok!(Tokens::mint_into(PROJECT_TOKEN, &owner, initial_project_tokens)); + assert_ok!(Tokens::mint_into(USDT, &owner, initial_usdt)); + set_sale_block(0); + assert_noop!( + Pablo::add_liquidity( + Origin::signed(owner), + pool_id, + initial_project_tokens, + initial_usdt, + 0_128, + false + ), + Error::::InvalidSaleState + ); + }, + ); + } +} + +mod invalid_pool { + use super::*; + + #[test] + fn final_weight_below_minimum() { + new_test_ext().execute_with(|| { + let pair = CurrencyPair::new(PROJECT_TOKEN, USDT); + let owner = ALICE; + let duration = MaxSaleDuration::get() - 1; + let start = 0; + let end = start + duration; + let initial_weight = MaxInitialWeight::get(); + let final_weight = MinFinalWeight::get() - Permill::from_parts(1); + let fee = Permill::from_perthousand(1); + assert!(Validated::<_, PoolIsValid>::new(LiquidityBootstrappingPoolInfo { + owner, + pair, + sale: Sale { start, end, initial_weight, final_weight }, + fee, + }) + .is_err()); + }); + } + + #[test] + fn initial_weight_above_maximum() { + new_test_ext().execute_with(|| { + let pair = CurrencyPair::new(PROJECT_TOKEN, USDT); + let owner = ALICE; + let duration = MaxSaleDuration::get() - 1; + let start = 0; + let end = start + duration; + let initial_weight = MaxInitialWeight::get() + Permill::from_parts(1); + let final_weight = MinFinalWeight::get(); + let fee = Permill::from_perthousand(1); + assert!(Validated::<_, PoolIsValid>::new(LiquidityBootstrappingPoolInfo { + owner, + pair, + sale: Sale { start, end, initial_weight, final_weight }, + fee, + }) + .is_err()); + }); + } + + #[test] + fn final_weight_above_initial_weight() { + new_test_ext().execute_with(|| { + let pair = CurrencyPair::new(PROJECT_TOKEN, USDT); + let owner = ALICE; + let duration = MaxSaleDuration::get() - 1; + let start = 0; + let end = start + duration; + let initial_weight = MinFinalWeight::get(); + let final_weight = MaxInitialWeight::get(); + let fee = Permill::from_perthousand(1); + assert!(Validated::<_, PoolIsValid>::new(LiquidityBootstrappingPoolInfo { + owner, + pair, + sale: Sale { start, end, initial_weight, final_weight }, + fee, + }) + .is_err()); + }); + } + + #[test] + fn end_before_start() { + new_test_ext().execute_with(|| { + let pair = CurrencyPair::new(PROJECT_TOKEN, USDT); + let owner = ALICE; + let start = 1; + let end = 0; + let initial_weight = MaxInitialWeight::get(); + let final_weight = MinFinalWeight::get(); + let fee = Permill::from_perthousand(1); + assert!(Validated::<_, PoolIsValid>::new(LiquidityBootstrappingPoolInfo { + owner, + pair, + sale: Sale { start, end, initial_weight, final_weight }, + fee, + }) + .is_err()); + }); + } + + #[test] + fn above_maximum_sale_duration() { + new_test_ext().execute_with(|| { + let pair = CurrencyPair::new(PROJECT_TOKEN, USDT); + let owner = ALICE; + let duration = MaxSaleDuration::get() + 1; + let start = 0; + let end = start + duration; + let initial_weight = MaxInitialWeight::get(); + let final_weight = MinFinalWeight::get(); + let fee = Permill::from_perthousand(1); + assert!(Validated::<_, PoolIsValid>::new(LiquidityBootstrappingPoolInfo { + owner, + pair, + sale: Sale { start, end, initial_weight, final_weight }, + fee, + }) + .is_err()); + }); + } + + #[test] + fn below_minimum_sale_duration() { + new_test_ext().execute_with(|| { + let pair = CurrencyPair::new(PROJECT_TOKEN, USDT); + let owner = ALICE; + let duration = MinSaleDuration::get() - 1; + let start = 0; + let end = start + duration; + let initial_weight = MaxInitialWeight::get(); + let final_weight = MinFinalWeight::get(); + let fee = Permill::from_perthousand(1); + assert!(Validated::<_, PoolIsValid>::new(LiquidityBootstrappingPoolInfo { + owner, + pair, + sale: Sale { start, end, initial_weight, final_weight }, + fee, + }) + .is_err()); + }); + } +} + +#[cfg(feature = "visualization")] +mod visualization { + use super::*; + + #[test] + fn plot() { + new_test_ext().execute_with(|| { + let pair = CurrencyPair::new(PROJECT_TOKEN, USDT); + let owner = ALICE; + let two_days = 48 * 3600 / 12; + let window = 100; + let pool = LiquidityBootstrappingPoolInfo { + owner, + pair, + sale: Sale { + start: window, + end: two_days + window, + initial_weight: Permill::from_percent(92), + final_weight: Permill::from_percent(50), + }, + fee: Permill::from_perthousand(1), + }; + let pool_id = Pablo::do_create_pool(Validated::new(pool).expect("impossible; qed;")) + .expect("impossible; qed;"); + + let unit = 1_000_000_000_000; + let initial_project_tokens = 100_000_000 * unit; + let initial_usdt = 1_000_000 * unit; + + assert_ok!(Tokens::mint_into(PROJECT_TOKEN, &ALICE, initial_project_tokens)); + assert_ok!(Tokens::mint_into(USDT, &ALICE, initial_usdt)); + + assert_ok!(Pablo::add_liquidity( + &ALICE, + pool_id, + initial_project_tokens, + initial_usdt, + 0, + false + )); + + { + let points = (pool.sale.start..pool.sale.end) + .map(|block| { + ( + block, + Pablo::do_spot_price(pool_id, pool.pair, block) + .expect("impossible; qed;") as f64 / + unit as f64, + ) + }) + .collect::>(); + let max_amount = points.iter().copied().fold(f64::NAN, |x, (_, y)| f64::max(x, y)); + + use plotters::prelude::*; + let area = BitMapBackend::new("./plots/lbp_spot_price.png", (1024, 768)) + .into_drawing_area(); + area.fill(&WHITE).unwrap(); + + let mut chart = ChartBuilder::on(&area) + .caption("Spot price", ("Arial", 50).into_font()) + .margin(100u32) + .x_label_area_size(30u32) + .y_label_area_size(30u32) + .build_cartesian_2d(pool.sale.start..pool.sale.end, 0f64..max_amount) + .unwrap(); + + chart.configure_mesh().draw().unwrap(); + chart + .draw_series(LineSeries::new(points, &RED)) + .unwrap() + .label("base") + .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED)); + chart + .configure_series_labels() + .background_style(&WHITE.mix(0.8)) + .border_style(&BLACK) + .draw() + .unwrap(); + } + + { + use plotters::prelude::*; + + let plot_swap = |pair, swap_amount, name, caption| { + let points = (pool.sale.start..pool.sale.end) + .map(|block| { + let (fees, base_amount) = + Pablo::do_get_exchange(pool_id, pair, block, swap_amount, true) + .expect("impossible; qed;"); + (block, fees / unit, base_amount / unit) + }) + .collect::>(); + let amounts = + points.clone().iter().copied().map(|(x, _, y)| (x, y)).collect::>(); + let amounts_with_fees = + points.into_iter().map(|(x, y, z)| (x, y + z)).collect::>(); + + let max_amount = + amounts_with_fees.iter().copied().map(|(_, x)| x).max().unwrap(); + + let area = BitMapBackend::new(name, (1024, 768)).into_drawing_area(); + area.fill(&WHITE).unwrap(); + + let mut chart = ChartBuilder::on(&area) + .caption(caption, ("Arial", 50).into_font()) + .margin(100u32) + .x_label_area_size(30u32) + .y_label_area_size(30u32) + .build_cartesian_2d(pool.sale.start..pool.sale.end, 0..max_amount) + .unwrap(); + + chart.configure_mesh().draw().unwrap(); + chart + .draw_series(LineSeries::new(amounts, &BLUE)) + .unwrap() + .label("Received tokens fees applied") + .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &BLUE)); + chart + .draw_series(LineSeries::new(amounts_with_fees, &RED)) + .unwrap() + .label("Received tokens fees not applied") + .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED)); + chart + .configure_series_labels() + .background_style(&WHITE.mix(0.8)) + .border_style(&BLACK) + .draw() + .unwrap(); + }; + + let buy_amount = 500; + plot_swap( + pair, + buy_amount * unit, + "./plots/lbp_buy_project.png", + format!("Buy project tokens with {} USDT", buy_amount), + ); + let sell_amount = 100_000; + plot_swap( + pair.swap(), + sell_amount * unit, + "./plots/lbp_sell_project.png", + format!("Sell {} project tokens", sell_amount), + ); + } + + { + use plotters::prelude::*; + let area = + BitMapBackend::new("./plots/lbp_weights.png", (1024, 768)).into_drawing_area(); + area.fill(&WHITE).unwrap(); + + let mut chart = ChartBuilder::on(&area) + .caption("y = weight", ("Arial", 50).into_font()) + .margin(100u32) + .x_label_area_size(30u32) + .y_label_area_size(30u32) + .build_cartesian_2d( + pool.sale.start..pool.sale.end, + 0..Permill::one().deconstruct(), + ) + .unwrap(); + + let points = (pool.sale.start..pool.sale.end).map(|block| { + (block, pool.sale.current_weights(block).expect("impossible; qed;")) + }); + + chart.configure_mesh().draw().unwrap(); + chart + .draw_series(LineSeries::new( + points + .clone() + .map(|(block, (base_weight, _))| (block, base_weight.deconstruct())), + &RED, + )) + .unwrap() + .label("base") + .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED)); + chart + .draw_series(LineSeries::new( + points + .map(|(block, (_, quote_weight))| (block, quote_weight.deconstruct())), + &BLUE, + )) + .unwrap() + .label("quote") + .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &BLUE)); + chart + .configure_series_labels() + .background_style(&WHITE.mix(0.8)) + .border_style(&BLACK) + .draw() + .unwrap(); + } + }); + } +} diff --git a/frame/pablo/src/mock.rs b/frame/pablo/src/mock.rs index b40edbc52c8..f3a6e2012ca 100644 --- a/frame/pablo/src/mock.rs +++ b/frame/pablo/src/mock.rs @@ -8,14 +8,17 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, ConvertInto, IdentityLookup}, + Permill, }; use system::EnsureRoot; pub type CurrencyId = u128; +pub type BlockNumber = u64; pub const BTC: AssetId = 0; pub const USDT: CurrencyId = 2; pub const USDC: CurrencyId = 4; +pub const PROJECT_TOKEN: AssetId = 1; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -66,7 +69,7 @@ impl system::Config for Test { type Origin = Origin; type Call = Call; type Index = u64; - type BlockNumber = u64; + type BlockNumber = BlockNumber; type Hash = H256; type Hashing = BlakeTwo256; type AccountId = AccountId; @@ -111,6 +114,10 @@ impl orml_tokens::Config for Test { parameter_types! { pub Precision: u128 = 100_u128; pub TestPalletID : PalletId = PalletId(*b"pablo_pa"); + pub MinSaleDuration: BlockNumber = 3600 / 12; + pub MaxSaleDuration: BlockNumber = 30 * 24 * 3600 / 12; + pub MaxInitialWeight: Permill = Permill::from_percent(95); + pub MinFinalWeight: Permill = Permill::from_percent(5); } impl pablo::Config for Test { @@ -122,6 +129,11 @@ impl pablo::Config for Test { type Convert = ConvertInto; type PoolId = PoolId; type PalletId = TestPalletID; + type LocalAssets = LpTokenFactory; + type LbpMinSaleDuration = MinSaleDuration; + type LbpMaxSaleDuration = MaxSaleDuration; + type LbpMaxInitialWeight = MaxInitialWeight; + type LbpMinFinalWeight = MinFinalWeight; } // Build genesis storage according to the mock runtime. diff --git a/frame/pablo/src/uniswap.rs b/frame/pablo/src/uniswap.rs index 8032620c2e4..bf8aa936bc5 100644 --- a/frame/pablo/src/uniswap.rs +++ b/frame/pablo/src/uniswap.rs @@ -1,4 +1,4 @@ -use crate::{Config, Error, Event, Pallet, PoolConfiguration, PoolCount, Pools}; +use crate::{Config, Error, PoolConfiguration, PoolCount, Pools}; use composable_maths::dex::constant_product::{ compute_deposit_lp, compute_in_given_out, compute_out_given_in, }; @@ -55,8 +55,6 @@ impl Uniswap { Ok(pool_id) })?; - >::deposit_event(Event::PoolCreated { pool_id, owner: who.clone() }); - Ok(pool_id) } From 285fb7e11f157401da8ce1cc8e5e416625ed3112 Mon Sep 17 00:00:00 2001 From: Vim Wickramasinghe Date: Fri, 18 Mar 2022 14:11:09 +0100 Subject: [PATCH 2/6] visualizations --- frame/pablo/plots/lbp/lbp_buy_project.png | Bin 0 -> 47978 bytes frame/pablo/plots/lbp/lbp_sell_project.png | Bin 0 -> 46521 bytes frame/pablo/plots/lbp/lbp_spot_price.png | Bin 0 -> 36823 bytes frame/pablo/plots/lbp/lbp_weights.png | Bin 0 -> 40290 bytes frame/pablo/src/lib.rs | 2 +- frame/pablo/src/liquidity_bootstrapping.rs | 4 +- .../src/liquidity_bootstrapping_tests.rs | 40 ++++++++++++------ 7 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 frame/pablo/plots/lbp/lbp_buy_project.png create mode 100644 frame/pablo/plots/lbp/lbp_sell_project.png create mode 100644 frame/pablo/plots/lbp/lbp_spot_price.png create mode 100644 frame/pablo/plots/lbp/lbp_weights.png diff --git a/frame/pablo/plots/lbp/lbp_buy_project.png b/frame/pablo/plots/lbp/lbp_buy_project.png new file mode 100644 index 0000000000000000000000000000000000000000..0629b9b6e443abb29e573f77ccb922e3fcb8e72b GIT binary patch literal 47978 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�Fs)W{2cgcvv+wh0-IQmbyL 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 literal 0 HcmV?d00001 diff --git a/frame/pablo/plots/lbp/lbp_sell_project.png b/frame/pablo/plots/lbp/lbp_sell_project.png new file mode 100644 index 0000000000000000000000000000000000000000..4ac62f9f7e44e4082e96b1b36c99c28ba2bf97bf GIT binary patch literal 46521 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 YcB@$c8NhLZQH6dL1cyq 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 M`mO6?*O+_#Kix3efB*mh literal 0 HcmV?d00001 diff --git a/frame/pablo/plots/lbp/lbp_weights.png b/frame/pablo/plots/lbp/lbp_weights.png new file mode 100644 index 0000000000000000000000000000000000000000..244833c6666cfa9deffd8fc94ed35d71bc3e1a08 GIT binary patch literal 40290 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 ZXEuI2O}Od(<~C5q@O1TaS?83{1OTemiOK)~ literal 0 HcmV?d00001 diff --git a/frame/pablo/src/lib.rs b/frame/pablo/src/lib.rs index 9eca4611d8d..d2ab7770b24 100644 --- a/frame/pablo/src/lib.rs +++ b/frame/pablo/src/lib.rs @@ -265,7 +265,7 @@ pub mod pallet { #[pallet::constant] type PalletId: Get; - // Why this is + // Used for spot price calculation for LBP type LocalAssets: LocalAssets>; /// Minimum duration for a sale. diff --git a/frame/pablo/src/liquidity_bootstrapping.rs b/frame/pablo/src/liquidity_bootstrapping.rs index 00708b99bb2..028b3ed7899 100644 --- a/frame/pablo/src/liquidity_bootstrapping.rs +++ b/frame/pablo/src/liquidity_bootstrapping.rs @@ -73,7 +73,7 @@ impl LiquidityBootstrapping { Ok(pool_id) } - pub fn ensure_sale_state( + fn ensure_sale_state( pool: &LiquidityBootstrappingPoolInfoOf, current_block: BlockNumberFor, expected_sale_state: SaleState, @@ -175,8 +175,6 @@ impl LiquidityBootstrapping { T::Assets::transfer(pool.pair.base, who, &pool_account, base_amount, keep_alive)?; T::Assets::transfer(pool.pair.quote, who, &pool_account, quote_amount, keep_alive)?; - // T::deposit_event(Event::LiquidityAdded { pool_id, base_amount, quote_amount }); - Ok(()) } diff --git a/frame/pablo/src/liquidity_bootstrapping_tests.rs b/frame/pablo/src/liquidity_bootstrapping_tests.rs index 2e8da5fbea7..8d6335943b1 100644 --- a/frame/pablo/src/liquidity_bootstrapping_tests.rs +++ b/frame/pablo/src/liquidity_bootstrapping_tests.rs @@ -461,6 +461,7 @@ mod invalid_pool { #[cfg(feature = "visualization")] mod visualization { use super::*; + use crate::liquidity_bootstrapping::LiquidityBootstrapping; #[test] fn plot() { @@ -480,8 +481,9 @@ mod visualization { }, fee: Permill::from_perthousand(1), }; - let pool_id = Pablo::do_create_pool(Validated::new(pool).expect("impossible; qed;")) - .expect("impossible; qed;"); + let pool_id = + Pablo::do_create_pool(&owner, PoolInitConfiguration::LiquidityBootstrapping(pool)) + .expect("impossible; qed;"); let unit = 1_000_000_000_000; let initial_project_tokens = 100_000_000 * unit; @@ -489,23 +491,28 @@ mod visualization { assert_ok!(Tokens::mint_into(PROJECT_TOKEN, &ALICE, initial_project_tokens)); assert_ok!(Tokens::mint_into(USDT, &ALICE, initial_usdt)); - assert_ok!(Pablo::add_liquidity( - &ALICE, + Origin::signed(ALICE), pool_id, initial_project_tokens, initial_usdt, 0, false )); + let pool_account = Pablo::account_id(&pool_id); { let points = (pool.sale.start..pool.sale.end) .map(|block| { ( block, - Pablo::do_spot_price(pool_id, pool.pair, block) - .expect("impossible; qed;") as f64 / + LiquidityBootstrapping::::do_spot_price( + pool, + pool_account, + pool.pair, + block, + ) + .expect("impossible; qed;") as f64 / unit as f64, ) }) @@ -513,7 +520,7 @@ mod visualization { let max_amount = points.iter().copied().fold(f64::NAN, |x, (_, y)| f64::max(x, y)); use plotters::prelude::*; - let area = BitMapBackend::new("./plots/lbp_spot_price.png", (1024, 768)) + let area = BitMapBackend::new("./plots/lbp/lbp_spot_price.png", (1024, 768)) .into_drawing_area(); area.fill(&WHITE).unwrap(); @@ -546,8 +553,15 @@ mod visualization { let points = (pool.sale.start..pool.sale.end) .map(|block| { let (fees, base_amount) = - Pablo::do_get_exchange(pool_id, pair, block, swap_amount, true) - .expect("impossible; qed;"); + LiquidityBootstrapping::::do_get_exchange( + pool, + &pool_account, + pair, + block, + swap_amount, + true, + ) + .expect("impossible; qed;"); (block, fees / unit, base_amount / unit) }) .collect::>(); @@ -593,22 +607,22 @@ mod visualization { plot_swap( pair, buy_amount * unit, - "./plots/lbp_buy_project.png", + "./plots/lbp/lbp_buy_project.png", format!("Buy project tokens with {} USDT", buy_amount), ); let sell_amount = 100_000; plot_swap( pair.swap(), sell_amount * unit, - "./plots/lbp_sell_project.png", + "./plots/lbp/lbp_sell_project.png", format!("Sell {} project tokens", sell_amount), ); } { use plotters::prelude::*; - let area = - BitMapBackend::new("./plots/lbp_weights.png", (1024, 768)).into_drawing_area(); + let area = BitMapBackend::new("./plots/lbp/lbp_weights.png", (1024, 768)) + .into_drawing_area(); area.fill(&WHITE).unwrap(); let mut chart = ChartBuilder::on(&area) From 0c3f85d337ff48b5660c38597a203d7dd37143c6 Mon Sep 17 00:00:00 2001 From: Vim Wickramasinghe Date: Fri, 18 Mar 2022 17:26:46 +0100 Subject: [PATCH 3/6] Review --- frame/pablo/src/liquidity_bootstrapping_tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/pablo/src/liquidity_bootstrapping_tests.rs b/frame/pablo/src/liquidity_bootstrapping_tests.rs index 8d6335943b1..9a11b796e74 100644 --- a/frame/pablo/src/liquidity_bootstrapping_tests.rs +++ b/frame/pablo/src/liquidity_bootstrapping_tests.rs @@ -137,6 +137,7 @@ mod create { }); } + // TODO enable with cu-23v3155 // #[test] // fn admin_can_create() { // new_test_ext().execute_with(|| { From 1b576247b164a698ebb830320d0d89fdc7a14620 Mon Sep 17 00:00:00 2001 From: Vim Wickramasinghe Date: Fri, 18 Mar 2022 17:54:37 +0100 Subject: [PATCH 4/6] Fix for main --- frame/pablo/src/common_test_functions.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frame/pablo/src/common_test_functions.rs b/frame/pablo/src/common_test_functions.rs index 18b45bba69c..2f691ef8d81 100644 --- a/frame/pablo/src/common_test_functions.rs +++ b/frame/pablo/src/common_test_functions.rs @@ -1,6 +1,6 @@ use crate::{ mock::{Pablo, *}, - PoolConfiguration::{ConstantProduct, StableSwap}, + PoolConfiguration::{ConstantProduct, LiquidityBootstrapping, StableSwap}, PoolInitConfiguration, }; use frame_support::{ @@ -11,7 +11,7 @@ use frame_support::{ /// `expected_lp_check` takes base_amount, quote_amount and lp_tokens in order and returns /// true if lp_tokens are expected for given base_amount, quote_amount. pub fn common_add_remove_lp( - init_config: PoolInitConfiguration, + init_config: PoolInitConfiguration, init_base_amount: Balance, init_quote_amount: Balance, base_amount: Balance, @@ -22,6 +22,7 @@ pub fn common_add_remove_lp( let pair = match init_config { PoolInitConfiguration::StableSwap { pair, .. } => pair, PoolInitConfiguration::ConstantProduct { pair, .. } => pair, + PoolInitConfiguration::LiquidityBootstrapping(pool) => pool.pair, }; // Mint the tokens assert_ok!(Tokens::mint_into(pair.base, &ALICE, init_base_amount)); @@ -41,6 +42,7 @@ pub fn common_add_remove_lp( let lp_token = match pool { StableSwap(pool) => pool.lp_token, ConstantProduct(pool) => pool.lp_token, + LiquidityBootstrapping(_) => panic!("Not implemented"), }; // Mint the tokens assert_ok!(Tokens::mint_into(pair.base, &BOB, base_amount)); From 37a1df845e344d307cb1783e12c119bbce1574b2 Mon Sep 17 00:00:00 2001 From: Vim Wickramasinghe Date: Fri, 18 Mar 2022 18:06:36 +0100 Subject: [PATCH 5/6] Try fix --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 2ae4c1182c0..7d4c04ddf42 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -70,7 +70,7 @@ jobs: run: | /home/runner/.cargo/bin/cargo install -f cargo-llvm-cov /home/runner/.cargo/bin/rustup component add llvm-tools-preview --toolchain=nightly-2021-11-08 - SKIP_WASM_BUILD=1 /home/runner/.cargo/bin/cargo +nightly llvm-cov --workspace --locked --release --verbose --features=runtime-benchmarks --lcov --output-path lcov.info + SKIP_WASM_BUILD=1 /home/runner/.cargo/bin/cargo +nightly-2021-11-08 llvm-cov --workspace --locked --release --verbose --features=runtime-benchmarks --lcov --output-path lcov.info - name: Upload coverage to Codecov uses: codecov/codecov-action@v2 with: From 82841c27fd9d27db710a77599c4a26d54ca73b5c Mon Sep 17 00:00:00 2001 From: Vim Wickramasinghe Date: Fri, 18 Mar 2022 18:38:42 +0100 Subject: [PATCH 6/6] Try fix --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 7d4c04ddf42..9e27f80b68d 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -68,7 +68,7 @@ jobs: /home/runner/.cargo/bin/rustup show - name: Run Test (with coverage) run: | - /home/runner/.cargo/bin/cargo install -f cargo-llvm-cov + /home/runner/.cargo/bin/cargo +nightly-2021-11-08 install -f cargo-llvm-cov /home/runner/.cargo/bin/rustup component add llvm-tools-preview --toolchain=nightly-2021-11-08 SKIP_WASM_BUILD=1 /home/runner/.cargo/bin/cargo +nightly-2021-11-08 llvm-cov --workspace --locked --release --verbose --features=runtime-benchmarks --lcov --output-path lcov.info - name: Upload coverage to Codecov