diff --git a/contracts/dao/treasury_manager/tests/integration/query.rs b/contracts/dao/treasury_manager/tests/integration/query.rs index 0531c588a..367953a36 100644 --- a/contracts/dao/treasury_manager/tests/integration/query.rs +++ b/contracts/dao/treasury_manager/tests/integration/query.rs @@ -26,6 +26,7 @@ pub fn query() { .timestamp() as u64, ), chain_id: "chain_id".to_string(), + random: None, }); init_dao( &mut app, @@ -366,6 +367,7 @@ pub fn query() { .timestamp() as u64, ), chain_id: "chain_id".to_string(), + random: None, }); update_dao(&mut app, "admin", &contracts, "SSCRT", 1).unwrap(); update_dao(&mut app, "admin", &contracts, "SSCRT", 1).unwrap(); diff --git a/contracts/liquidity_book/lb_factory/src/contract.rs b/contracts/liquidity_book/lb_factory/src/contract.rs index e25ed51d4..d57e2eb67 100644 --- a/contracts/liquidity_book/lb_factory/src/contract.rs +++ b/contracts/liquidity_book/lb_factory/src/contract.rs @@ -37,9 +37,7 @@ use shade_protocol::{ lb_pair::ExecuteMsg::{ForceDecay as LbPairForceDecay, SetStaticFeeParameters}, }, swap::core::TokenType, - utils::{ - callback::ExecuteCallback, - }, + utils::callback::ExecuteCallback, }; pub static _OFFSET_IS_PRESET_OPEN: u8 = 255; @@ -75,6 +73,8 @@ pub fn instantiate( lb_pair_implementation: ContractInstantiationInfo::default(), lb_token_implementation: ContractInstantiationInfo::default(), admin_auth: msg.admin_auth.into_valid(deps.api)?, + total_reward_bins: msg.total_reward_bins, + rewards_distribution_algorithm: msg.rewards_distribution_algorithm, }; CONFIG.save(deps.storage, &config)?; @@ -378,6 +378,8 @@ fn try_create_lb_pair( entropy, protocol_fee_recipient: config.fee_recipient, admin_auth: config.admin_auth.into(), + total_reward_bins: Some(config.total_reward_bins), + rewards_distribution_algorithm: config.rewards_distribution_algorithm, })?, code_hash: config.lb_pair_implementation.code_hash.clone(), funds: vec![], diff --git a/contracts/liquidity_book/lb_factory/src/state.rs b/contracts/liquidity_book/lb_factory/src/state.rs index ac865e670..b3b807ce8 100644 --- a/contracts/liquidity_book/lb_factory/src/state.rs +++ b/contracts/liquidity_book/lb_factory/src/state.rs @@ -4,6 +4,7 @@ use shade_protocol::{ c_std::{Addr, ContractInfo, Storage}, cosmwasm_schema::cw_serde, lb_libraries::{pair_parameter_helper::PairParameters, types::ContractInstantiationInfo}, + liquidity_book::lb_pair::RewardsDistributionAlgorithm, secret_storage_plus::{AppendStore, Item, Map}, storage::{singleton, singleton_read, ReadonlySingleton, Singleton}, swap::core::TokenType, @@ -53,6 +54,8 @@ pub struct Config { pub lb_pair_implementation: ContractInstantiationInfo, pub lb_token_implementation: ContractInstantiationInfo, pub admin_auth: Contract, + pub total_reward_bins: u32, + pub rewards_distribution_algorithm: RewardsDistributionAlgorithm, } pub fn ephemeral_storage_w(storage: &mut dyn Storage) -> Singleton { diff --git a/contracts/liquidity_book/lb_pair/src/contract.rs b/contracts/liquidity_book/lb_pair/src/contract.rs index 7f80d5b45..82ebe4c74 100644 --- a/contracts/liquidity_book/lb_pair/src/contract.rs +++ b/contracts/liquidity_book/lb_pair/src/contract.rs @@ -26,12 +26,24 @@ use shade_protocol::{ Timestamp, Uint128, Uint256, + Uint512, WasmMsg, }, - contract_interfaces::liquidity_book::{lb_pair::*, lb_token}, + contract_interfaces::{ + liquidity_book::{lb_pair::*, lb_token}, + swap::{ + amm_pair::{ + FeeInfo, + QueryMsgResponse::{GetPairInfo, SwapSimulation}, + }, + core::{Fee, TokenPair, TokenType}, + router::ExecuteMsgResponse, + }, + }, lb_libraries::{ + approx_div, bin_helper::BinHelper, - constants::{MAX_FEE, SCALE_OFFSET}, + constants::{BASIS_POINT_MAX, MAX_FEE, SCALE_OFFSET}, fee_helper::FeeHelper, lb_token::state_structs::{LbPair, TokenAmount, TokenIdBalance}, math::{ @@ -46,22 +58,20 @@ use shade_protocol::{ oracle_helper::{Oracle, MAX_SAMPLE_LIFETIME}, pair_parameter_helper::PairParameters, price_helper::PriceHelper, - types::{Bytes32, MintArrays}, + types::{self, Bytes32, LBPairInformation, MintArrays}, viewing_keys::{register_receive, set_viewing_key_msg, ViewingKey}, }, snip20, - swap::{ - amm_pair::{FeeInfo, QueryMsgResponse::GetPairInfo}, - core::{Fee, TokenPair, TokenType}, - router::{ExecuteMsgResponse, QueryMsgResponse::SwapSimulation}, - }, + utils::pad_handle_result, Contract, + BLOCK_SIZE, }; -use std::collections::HashMap; +use std::{collections::HashMap, ops::Sub}; pub const INSTANTIATE_LP_TOKEN_REPLY_ID: u64 = 1u64; pub const MINT_REPLY_ID: u64 = 1u64; const LB_PAIR_CONTRACT_VERSION: u32 = 1; +const DEFAULT_REWARDS_BINS: u32 = 100; /////////////// INSTANTIATE /////////////// @@ -151,6 +161,14 @@ pub fn instantiate( } } + match msg.total_reward_bins { + Some(t_r_b) => { + if { t_r_b == U24::MAX } { + return Err(Error::InvalidInput {}); + } + } + None => {} + } let state = State { creator: info.sender, factory: msg.factory, @@ -167,9 +185,17 @@ pub fn instantiate( viewing_key, protocol_fees_recipient: msg.protocol_fee_recipient, admin_auth: msg.admin_auth.into_valid(deps.api)?, + last_swap_timestamp: env.block.time, + rewards_epoch_id: 0, + base_rewards_bins: msg.total_reward_bins, + toggle_distributions_algorithm: false, + //TODO: set using the setter function and instantiate msg }; - let tree = TreeUint24::new(); + // deps.api + // .debug(format!("Contract was initialized by {}", info.sender).as_str()); + + let tree: TreeUint24 = TreeUint24::new(); let oracle = Oracle { samples: HashMap::::new(), }; @@ -179,6 +205,12 @@ pub fn instantiate( CONTRACT_STATUS.save(deps.storage, &ContractStatus::Active)?; CONTRACT_STATUS.save(deps.storage, &ContractStatus::Active)?; BIN_TREE.save(deps.storage, &tree)?; + FEE_MAP_TREE.save(deps.storage, 0, &tree)?; + REWARDS_STATS_STORE.save(deps.storage, 0, &RewardStats { + cumm_value: Uint256::zero(), + cumm_value_mul_bin_id: Uint256::zero(), + rewards_distribution_algorithm: msg.rewards_distribution_algorithm, + }); ephemeral_storage_w(deps.storage).save(&NextTokenKey { code_hash: msg.lb_token_implementation.code_hash, @@ -276,6 +308,11 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> R max_volatility_accumulator, ), ExecuteMsg::ForceDecay {} => try_force_decay(deps, env, info), + ExecuteMsg::CalculateRewards {} => try_calculate_rewards(deps, env, info), + ExecuteMsg::ResetRewardsConfig { + distribution, + base_rewards_bins, + } => try_reset_rewards_config(deps, env, info, distribution, base_rewards_bins), } } @@ -365,10 +402,21 @@ fn try_swap( let mut params = state.pair_parameters; let bin_step = state.bin_step; + let mut reward_stats = REWARDS_STATS_STORE + .load(deps.storage, state.rewards_epoch_id) + .unwrap(); let mut active_id = params.get_active_id(); params.update_references(&env.block.time)?; + if reward_stats.rewards_distribution_algorithm == RewardsDistributionAlgorithm::TimeBasedRewards + { + let time_difference = + Uint256::from(env.block.time.seconds() - state.last_swap_timestamp.seconds()); + + reward_stats.cumm_value += time_difference; + reward_stats.cumm_value_mul_bin_id += time_difference * (Uint256::from(active_id)); + } loop { let bin_reserves = BIN_MAP @@ -376,6 +424,8 @@ fn try_swap( .map_err(|_| Error::ZeroBinReserve { active_id })?; if !BinHelper::is_empty(bin_reserves, !swap_for_y) { + let price = PriceHelper::get_price_from_id(active_id, bin_step)?; + params.update_volatility_accumulator(active_id)?; let (mut amounts_in_with_fees, amounts_out_of_bin, fees) = BinHelper::get_amounts( @@ -385,9 +435,61 @@ fn try_swap( swap_for_y, active_id, amounts_left, + price, )?; if U256::from_le_bytes(amounts_in_with_fees) > U256::ZERO { + // let fee_obj = FeeLog { + // is_token_x: swap_for_y, + // fee: Uint128::from(fees.decode_alt(swap_for_y)), + // bin_id: active_id, + // timestamp: env.block.time, + // last_rewards_epoch_id: state.rewards_epoch_id, + // }; + // //TODO: check if appending is needed + // FEE_APPEND_STORE.push(deps.storage, &fee_obj)?; + + if reward_stats.rewards_distribution_algorithm + == RewardsDistributionAlgorithm::VolumeBasedRewards + { + let feeu128 = fees.decode_alt(swap_for_y); + let swap_value_uint256 = match swap_for_y { + true => U256x256Math::mul_shift_round_up( + U256::from(feeu128), + price, + SCALE_OFFSET, + )? + .u256_to_uint256(), + false => Uint256::from(feeu128), + }; + println!( + "swap_value_uint256: {:?}, id: {:?}", + swap_value_uint256, active_id + ); + reward_stats.cumm_value += swap_value_uint256; + let mut fee_map_tree = FEE_MAP_TREE.update( + deps.storage, + state.rewards_epoch_id, + |mut fee_tree| -> Result<_> { + Ok(match fee_tree { + Some(mut t) => { + t.add(active_id); + t + } + None => panic!("Fee tree not initialized"), + }) + }, + ); + + FEE_MAP.update(deps.storage, active_id, |mut cumm_fee| -> Result<_> { + let updated_cumm_fee = match cumm_fee { + Some(f) => f + swap_value_uint256, + None => swap_value_uint256, + }; + Ok(updated_cumm_fee) + })?; + } + amounts_left = amounts_left.sub(amounts_in_with_fees); amounts_out = amounts_out.add(amounts_out_of_bin); @@ -424,6 +526,8 @@ fn try_swap( } } + REWARDS_STATS_STORE.save(deps.storage, state.rewards_epoch_id, &reward_stats)?; + if amounts_out == [0u8; 32] { return Err(Error::InsufficientAmountOut); } @@ -434,9 +538,12 @@ fn try_swap( oracle.update(&env.block.time, params, active_id)?; CONFIG.update(deps.storage, |mut state| { + state.last_swap_timestamp = env.block.time; state.protocol_fees = protocol_fees; // TODO - map the error to a StdError - state.pair_parameters.set_active_id(active_id) + state + .pair_parameters + .set_active_id(active_id) .map_err(|err| StdError::generic_err(err.to_string()))?; state.reserves = reserves; Ok::(state) @@ -914,7 +1021,6 @@ fn _update_bin( let bin_reserves = BIN_MAP.load(deps.storage, id).unwrap_or([0u8; 32]); let config = CONFIG.load(deps.storage)?; let price = PriceHelper::get_price_from_id(id, bin_step)?; - let total_supply = _query_total_supply( deps.as_ref(), id, @@ -1396,6 +1502,202 @@ fn try_force_decay(deps: DepsMut, _env: Env, info: MessageInfo) -> Result Result { + let mut state = CONFIG.load(deps.storage)?; + validate_admin( + &deps.querier, + AdminPermissions::LiquidityBookAdmin, + info.sender.to_string(), + &state.admin_auth, + )?; + + // loop through the fee_logs uptil a maximum iterations + // save the results in temporary storage + + let reward_stats = REWARDS_STATS_STORE.load(deps.storage, state.rewards_epoch_id)?; + let distribution = if !reward_stats.cumm_value.is_zero() { + match reward_stats.rewards_distribution_algorithm { + RewardsDistributionAlgorithm::TimeBasedRewards => { + calculate_time_based_rewards_distribution(&env, &state, &reward_stats)? + } + RewardsDistributionAlgorithm::VolumeBasedRewards => { + calculate_volume_based_rewards_distribution(deps.as_ref(), &state, &reward_stats)? + } + } + } else { + let rewards_bins = match state.base_rewards_bins { + Some(r_b) => r_b, + None => DEFAULT_REWARDS_BINS, + }; + calculate_default_distribution(rewards_bins, state.pair_parameters.get_active_id())? + }; + + REWARDS_DISTRIBUTION.save(deps.storage, state.rewards_epoch_id, &distribution)?; + state.rewards_epoch_id += 1; + let toggle = state.toggle_distributions_algorithm; + state.last_swap_timestamp = env.block.time; + state.toggle_distributions_algorithm = false; + CONFIG.save(deps.storage, &state)?; + + let mut distribution_algorithm = &reward_stats.rewards_distribution_algorithm; + if toggle { + distribution_algorithm = match reward_stats.rewards_distribution_algorithm { + RewardsDistributionAlgorithm::TimeBasedRewards => { + &RewardsDistributionAlgorithm::VolumeBasedRewards + } + RewardsDistributionAlgorithm::VolumeBasedRewards => { + &RewardsDistributionAlgorithm::TimeBasedRewards + } + }; + } + + REWARDS_STATS_STORE.save(deps.storage, state.rewards_epoch_id, &RewardStats { + cumm_value: Uint256::zero(), + cumm_value_mul_bin_id: Uint256::zero(), + rewards_distribution_algorithm: distribution_algorithm.clone(), + })?; + + if distribution_algorithm == &RewardsDistributionAlgorithm::VolumeBasedRewards { + let tree: TreeUint24 = TreeUint24::new(); + FEE_MAP_TREE.save(deps.storage, state.rewards_epoch_id, &tree)?; + } + Ok(Response::default()) +} + +fn calculate_time_based_rewards_distribution( + env: &Env, + state: &State, + reward_stats: &RewardStats, +) -> Result { + let mut cumm_value_mul_bin = reward_stats.cumm_value_mul_bin_id; + let mut cumm_value = reward_stats.cumm_value; + + let active_id = state.pair_parameters.get_active_id(); + + let time_difference = + Uint256::from(env.block.time.seconds() - state.last_swap_timestamp.seconds()); + + cumm_value += time_difference; + cumm_value_mul_bin += time_difference * (Uint256::from(active_id)); + + let avg_bin = approx_div(cumm_value_mul_bin, cumm_value) + .uint256_to_u256() + .as_u32(); + + let rewards_bins = match state.base_rewards_bins { + Some(r_b) => r_b, + None => DEFAULT_REWARDS_BINS, + }; + + calculate_default_distribution(rewards_bins, avg_bin) +} + +fn calculate_default_distribution(rewards_bins: u32, avg_bin: u32) -> Result { + let half_total = rewards_bins / 2; + let min_bin = avg_bin.saturating_sub(half_total) + 1; + let max_bin = avg_bin.saturating_add(half_total); + + let difference = max_bin - min_bin + 1; + + let ids: Vec = (min_bin..=max_bin).collect(); + let weightages = vec![BASIS_POINT_MAX as u16 / difference as u16; difference as usize]; + + Ok(RewardsDistribution { + ids, + weightages, + denominator: BASIS_POINT_MAX as u16, + }) +} + +fn calculate_volume_based_rewards_distribution( + deps: Deps, + state: &State, + reward_stats: &RewardStats, +) -> Result { + let mut cum_fee = reward_stats.cumm_value; + let mut ids: Vec = Vec::new(); + let mut weightages: Vec = Vec::new(); + + let fee_tree: TreeUint24 = FEE_MAP_TREE.load(deps.storage, state.rewards_epoch_id)?; + let mut id: u32 = 0; + let basis_point_max: Uint256 = Uint256::from(BASIS_POINT_MAX); + let mut total_weight = 0; + + loop { + id = fee_tree.find_first_left(id); + if id == U24::MAX || id == 0 { + break; + } + + let fee: Uint256 = FEE_MAP.load(deps.storage, id)?; + ids.push(id); + let weightage: u16 = fee + .multiply_ratio(basis_point_max, cum_fee) + .uint256_to_u256() + .as_u16(); + weightages.push(weightage); + total_weight += weightage; + } + + let reminder = BASIS_POINT_MAX as u16 - total_weight; + + if reminder > 0 { + let len = weightages.len() - 1; + weightages[len] += reminder; + } + + let distribution = RewardsDistribution { + ids, + weightages, + denominator: BASIS_POINT_MAX as u16, + }; + + Ok(distribution) +} + +//Can only change the distribution algorithm at the start of next epoch +//Eventhough the distribution was changes mid epoch the effects of change will occur after the epoch. +fn try_reset_rewards_config( + deps: DepsMut, + env: Env, + info: MessageInfo, + rewards_distribution_algorithm: Option, + base_reward_bins: Option, +) -> Result { + let mut state = CONFIG.load(deps.storage)?; + validate_admin( + &deps.querier, + AdminPermissions::LiquidityBookAdmin, + info.sender.to_string(), + &state.admin_auth, + )?; + let mut reward_stats = REWARDS_STATS_STORE.load(deps.storage, state.rewards_epoch_id)?; + + //Eventhough the distribution was changes mid epoch the effects of change will occur after the epoch. + match rewards_distribution_algorithm { + Some(distribution) => { + if reward_stats.rewards_distribution_algorithm != distribution { + state.toggle_distributions_algorithm = true; + } + } + None => {} + }; + + match base_reward_bins { + Some(b_r_b) => { + if b_r_b > U24::MAX { + return Err(Error::U24Overflow); + } + state.base_rewards_bins = Some(b_r_b) + } + None => {} + } + + CONFIG.save(deps.storage, &state)?; + + Ok(Response::default()) +} + fn only_factory(sender: &Addr, factory: &Addr) -> Result<()> { if sender != factory { return Err(Error::OnlyFactory); @@ -1493,6 +1795,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { QueryMsg::SwapSimulation { offer, exclude_fee } => { query_swap_simulation(deps, env, offer, exclude_fee) } + QueryMsg::GetRewardsDistribution { epoch_id } => query_rewards_distribution(deps, epoch_id), } } @@ -2112,7 +2415,9 @@ fn query_swap_out(deps: Deps, env: Env, amount_in: u128, swap_for_y: bool) -> Re loop { let bin_reserves = BIN_MAP.load(deps.storage, id).unwrap_or_default(); if !BinHelper::is_empty(bin_reserves, !swap_for_y) { - params.update_volatility_accumulator(id)?; + let price = PriceHelper::get_price_from_id(id, bin_step)?; + + params = *params.update_volatility_accumulator(id)?; let (amounts_in_with_fees, amounts_out_of_bin, fees) = BinHelper::get_amounts( bin_reserves, @@ -2121,6 +2426,7 @@ fn query_swap_out(deps: Deps, env: Env, amount_in: u128, swap_for_y: bool) -> Re swap_for_y, id, amounts_in_left, + price, )?; if U256::from_le_bytes(amounts_in_with_fees) > U256::ZERO { @@ -2172,8 +2478,19 @@ fn query_total_supply(deps: Deps, id: u32) -> Result { let total_supply = _query_total_supply(deps, id, state.lb_token.code_hash, state.lb_token.address)? .u256_to_uint256(); - let response = TotalSupplyResponse { total_supply }; - to_binary(&response).map_err(Error::CwErr) + to_binary(&TotalSupplyResponse { total_supply }).map_err(Error::CwErr) +} + +fn query_rewards_distribution(deps: Deps, epoch_id: Option) -> Result { + let (epoch_id) = match epoch_id { + Some(id) => id, + None => CONFIG.load(deps.storage)?.rewards_epoch_id - 1, + }; + + to_binary(&RewardsDistributionResponse { + distribution: REWARDS_DISTRIBUTION.load(deps.storage, epoch_id)?, + }) + .map_err(Error::CwErr) } #[shd_entry_point] diff --git a/contracts/liquidity_book/lb_pair/src/error.rs b/contracts/liquidity_book/lb_pair/src/error.rs index 763d58982..c07e1dbee 100644 --- a/contracts/liquidity_book/lb_pair/src/error.rs +++ b/contracts/liquidity_book/lb_pair/src/error.rs @@ -60,6 +60,9 @@ pub enum LBPairError { #[error("Not enough liquidity!")] OutOfLiquidity, + #[error("value greater than u24!")] + U24Overflow, + #[error("Token not supported!")] TokenNotSupported(), diff --git a/contracts/liquidity_book/lb_pair/src/lib.rs b/contracts/liquidity_book/lb_pair/src/lib.rs index 0589a59f1..541f2a87c 100644 --- a/contracts/liquidity_book/lb_pair/src/lib.rs +++ b/contracts/liquidity_book/lb_pair/src/lib.rs @@ -5,6 +5,3 @@ mod prelude; pub mod contract; pub mod state; - -#[cfg(test)] -pub mod unittest; diff --git a/contracts/liquidity_book/lb_pair/src/state.rs b/contracts/liquidity_book/lb_pair/src/state.rs index 1c6fd09ad..50f12ce52 100644 --- a/contracts/liquidity_book/lb_pair/src/state.rs +++ b/contracts/liquidity_book/lb_pair/src/state.rs @@ -1,14 +1,15 @@ use shade_protocol::{ - c_std::{Addr, ContractInfo, Storage}, + c_std::{Addr, ContractInfo, Storage, Timestamp, Uint128, Uint256}, cosmwasm_schema::cw_serde, lb_libraries::{ math::tree_math::TreeUint24, oracle_helper::Oracle, - pair_parameter_helper::PairParameters, + pair_parameter_helper::{self, PairParameters}, types::Bytes32, viewing_keys::ViewingKey, }, - secret_storage_plus::{Bincode2, Item, Map}, + liquidity_book::lb_pair::{RewardsDistribution, RewardsDistributionAlgorithm}, + secret_storage_plus::{AppendStore, Bincode2, Item, Map}, storage::{singleton, singleton_read, ReadonlySingleton, Singleton}, swap::core::TokenType, Contract, @@ -20,6 +21,27 @@ pub const BIN_MAP: Map = Map::new("bins"); //? pub const BIN_TREE: Item = Item::new("bin_tree"); //? pub const ORACLE: Item = Item::new("oracle"); //? pub static EPHEMERAL_STORAGE_KEY: &[u8] = b"ephemeral_storage"; +pub const FEE_APPEND_STORE: AppendStore = AppendStore::new("fee_logs"); //? +pub const REWARDS_STATS_STORE: Map = Map::new("rewards_stats"); //? +pub const REWARDS_DISTRIBUTION: Map = Map::new("rewards_distribution"); //? +pub const FEE_MAP_TREE: Map = Map::new("fee_tree"); //? +pub const FEE_MAP: Map = Map::new("fee_map"); //? + +#[cw_serde] +pub struct RewardStats { + pub cumm_value: Uint256, + pub cumm_value_mul_bin_id: Uint256, + pub rewards_distribution_algorithm: RewardsDistributionAlgorithm, +} + +#[cw_serde] +pub struct FeeLog { + pub is_token_x: bool, + pub fee: Uint128, + pub bin_id: u32, + pub timestamp: Timestamp, + pub last_rewards_epoch_id: u64, +} #[cw_serde] pub enum ContractStatus { @@ -42,6 +64,10 @@ pub struct State { pub lb_token: ContractInfo, pub protocol_fees_recipient: Addr, pub admin_auth: Contract, + pub last_swap_timestamp: Timestamp, + pub rewards_epoch_id: u64, + pub base_rewards_bins: Option, + pub toggle_distributions_algorithm: bool, } pub fn ephemeral_storage_w(storage: &mut dyn Storage) -> Singleton { diff --git a/contracts/liquidity_book/lb_pair/src/unittest/mod.rs b/contracts/liquidity_book/lb_pair/src/unittest/mod.rs deleted file mode 100644 index 1d579ef38..000000000 --- a/contracts/liquidity_book/lb_pair/src/unittest/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[cfg(test)] -pub mod handle_test; -#[cfg(test)] -pub mod query_test; -pub mod test_helper; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_factory.rs b/contracts/liquidity_book/tests/src/multitests/lb_factory.rs index 3b63fac28..c5306a9e8 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_factory.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_factory.rs @@ -13,7 +13,7 @@ use shade_protocol::{ u24::U24, }, }, - liquidity_book::lb_factory::PresetResponse, + liquidity_book::{lb_factory::PresetResponse, lb_pair::RewardsDistributionAlgorithm}, swap::core::TokenType, utils::MultiTestable, }; @@ -24,7 +24,7 @@ use crate::multitests::test_helper::*; #[serial] pub fn test_setup() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None)?; + let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; //query fee recipient let fee_recipient = lb_factory::query_fee_recipient(&mut app, &lb_factory.clone().into())?; @@ -47,7 +47,7 @@ pub fn test_setup() -> Result<(), anyhow::Error> { #[serial] pub fn test_set_lb_pair_implementation() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None)?; + let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; let lb_pair_stored_code = app.store_code(LbPair::default().contract()); lb_factory::set_lb_pair_implementation( @@ -67,7 +67,7 @@ pub fn test_set_lb_pair_implementation() -> Result<(), anyhow::Error> { #[serial] pub fn test_revert_set_lb_pair_implementation() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None)?; + let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; let lb_pair_stored_code = app.store_code(LbPair::default().contract()); lb_factory::set_lb_pair_implementation( @@ -100,7 +100,7 @@ pub fn test_revert_set_lb_pair_implementation() -> Result<(), anyhow::Error> { #[serial] pub fn test_set_lb_token_implementation() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None)?; + let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; let lb_token_stored_code = app.store_code(LbToken::default().contract()); lb_factory::set_lb_token_implementation( &mut app, @@ -119,7 +119,7 @@ pub fn test_set_lb_token_implementation() -> Result<(), anyhow::Error> { #[serial] pub fn test_create_lb_pair() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None)?; + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; // 3. Create an LBPair. @@ -208,7 +208,7 @@ pub fn test_create_lb_pair() -> Result<(), anyhow::Error> { #[serial] pub fn test_create_lb_pair_factory_unlocked() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None)?; + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; let shd = extract_contract_info(&deployed_contracts, SHADE)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; @@ -300,7 +300,7 @@ pub fn test_create_lb_pair_factory_unlocked() -> Result<(), anyhow::Error> { #[serial] fn test_revert_create_lb_pair() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None)?; + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; let shd = extract_contract_info(&deployed_contracts, SHADE)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; @@ -336,6 +336,8 @@ fn test_revert_create_lb_pair() -> Result<(), anyhow::Error> { addrs.admin(), 0, admin_contract.into(), + 100, + Some(RewardsDistributionAlgorithm::TimeBasedRewards), )?; //can't create a pair if the preset is not set @@ -511,7 +513,7 @@ fn test_revert_create_lb_pair() -> Result<(), anyhow::Error> { #[serial] fn test_fuzz_set_preset() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None)?; + let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; let mut bin_step: u16 = generate_random(0, u16::MAX); let base_factor: u16 = generate_random(0, u16::MAX); let mut filter_period: u16 = generate_random(0, u16::MAX); @@ -587,7 +589,7 @@ fn test_fuzz_set_preset() -> Result<(), anyhow::Error> { #[serial] fn test_remove_preset() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None)?; + let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; // Set presets lb_factory::set_pair_preset( @@ -669,7 +671,7 @@ fn test_remove_preset() -> Result<(), anyhow::Error> { #[serial] pub fn test_set_fees_parameters_on_pair() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None)?; + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; let shd = extract_contract_info(&deployed_contracts, SHADE)?; @@ -792,7 +794,7 @@ pub fn test_set_fees_parameters_on_pair() -> Result<(), anyhow::Error> { #[serial] pub fn test_set_fee_recipient() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None)?; + let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; lb_factory::set_fee_recipient( &mut app, addrs.admin().as_str(), @@ -833,7 +835,7 @@ pub fn test_set_fee_recipient() -> Result<(), anyhow::Error> { #[serial] pub fn test_fuzz_open_presets() -> Result<(), anyhow::Error> { let addrs = init_addrs(); // Initialize addresses - let (mut app, lb_factory, _deployed_contracts) = setup(None)?; // Setup + let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; // Setup let min_bin_step = lb_factory::query_min_bin_step(&mut app, &lb_factory.clone().into())?; let max_bin_step = u16::MAX; @@ -932,7 +934,7 @@ pub fn test_fuzz_open_presets() -> Result<(), anyhow::Error> { #[serial] pub fn test_add_quote_asset() -> Result<(), anyhow::Error> { let addrs = init_addrs(); // Initialize addresses - let (mut app, lb_factory, mut deployed_contracts) = setup(None)?; // Setup + let (mut app, lb_factory, mut deployed_contracts) = setup(None, None)?; // Setup let num_quote_assets_before = lb_factory::query_number_of_quote_assets(&mut app, &lb_factory.clone().into())?; @@ -946,8 +948,8 @@ pub fn test_add_quote_asset() -> Result<(), anyhow::Error> { &mut app, addrs.admin().as_str(), &mut deployed_contracts, - "sBTC", - "SBTC", + "SOMOS", + "SOMOS", 8, Some(shade_protocol::snip20::InitConfig { public_total_supply: Some(true), @@ -959,18 +961,8 @@ pub fn test_add_quote_asset() -> Result<(), anyhow::Error> { }), ) .unwrap(); - - let num_quote_assets = - lb_factory::query_number_of_quote_assets(&mut app, &lb_factory.clone().into())?; - println!("check: {num_quote_assets}"); - - let sbtc = extract_contract_info(&deployed_contracts, "SBTC")?; - let new_token = token_type_snip20_generator(&sbtc)?; - - let num_quote_assets = - lb_factory::query_number_of_quote_assets(&mut app, &lb_factory.clone().into())?; - println!("check: {num_quote_assets}"); - + let sosmo = extract_contract_info(&deployed_contracts, "SOMOS")?; + let new_token = token_type_snip20_generator(&sosmo)?; // Check if the new token is a quote asset let is_quote_asset = lb_factory::query_is_quote_asset(&mut app, &lb_factory.clone().into(), new_token.clone())?; @@ -1010,13 +1002,15 @@ pub fn test_add_quote_asset() -> Result<(), anyhow::Error> { num_quote_assets_before + 1, "test_add_quote_asset::3" ); + assert_eq!(num_quote_assets_after, 6, "test_add_quote_asset::4"); + assert_eq!(num_quote_assets_before, 5, "test_add_quote_asset::5"); let last_quote_asset = lb_factory::query_quote_asset_at_index( &mut app, &lb_factory.clone().into(), num_quote_assets_before, )?; - assert_eq!(last_quote_asset, new_token, "test_add_quote_asset::4"); + assert_eq!(last_quote_asset, new_token, "test_add_quote_asset::6"); // Try to add the same asset when not the owner let err = lb_factory::add_quote_asset( @@ -1049,7 +1043,7 @@ pub fn test_add_quote_asset() -> Result<(), anyhow::Error> { #[serial] pub fn test_remove_quote_asset() -> Result<(), anyhow::Error> { let addrs = init_addrs(); // Initialize addresses - let (mut app, lb_factory, mut deployed_contracts) = setup(None)?; // Setup + let (mut app, lb_factory, mut deployed_contracts) = setup(None, None)?; // Setup //SSCRT and SHD already added as quote asset let num_quote_assets_before = @@ -1144,7 +1138,7 @@ pub fn test_remove_quote_asset() -> Result<(), anyhow::Error> { #[serial] pub fn test_force_decay() -> Result<(), anyhow::Error> { let addrs = init_addrs(); // Initialize addresses - let (mut app, lb_factory, deployed_contracts) = setup(None)?; // Setup + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; // Setup let sscrt_info = extract_contract_info(&deployed_contracts, SSCRT)?; let sscrt = token_type_snip20_generator(&sscrt_info)?; @@ -1194,7 +1188,7 @@ pub fn test_force_decay() -> Result<(), anyhow::Error> { #[serial] pub fn test_get_all_lb_pair() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None)?; + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; // 3. Create an LBPair. diff --git a/contracts/liquidity_book/tests/src/multitests/lb_pair_fees.rs b/contracts/liquidity_book/tests/src/multitests/lb_pair_fees.rs index 0eccdc047..78de0772a 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_pair_fees.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_pair_fees.rs @@ -1,4 +1,15 @@ +use crate::multitests::{lb_pair_liquidity::PRECISION, test_helper::*}; + +use super::test_helper::{ + increase_allowance_helper, + init_addrs, + liquidity_parameters_generator, + mint_token_helper, + setup, + ID_ONE, +}; use anyhow::Ok; +use ethnum::U256; use serial_test::serial; use shade_multi_test::interfaces::{ lb_factory, @@ -8,12 +19,12 @@ use shade_multi_test::interfaces::{ utils::DeployedContracts, }; use shade_protocol::{ - c_std::{ContractInfo, StdError, Uint128, Uint256}, + c_std::{ContractInfo, StdError, Timestamp, Uint128, Uint256}, lb_libraries::{ math::{encoded_sample::MASK_UINT20, u24::U24}, types::LBPairInformation, }, - liquidity_book::lb_pair::RemoveLiquidity, + liquidity_book::lb_pair::{RemoveLiquidity, RewardsDistributionAlgorithm}, multi_test::App, }; @@ -33,7 +44,7 @@ pub fn lb_pair_setup() -> Result< anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None)?; + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; @@ -2176,7 +2187,7 @@ pub fn test_revert_total_fee_exceeded() -> Result<(), anyhow::Error> { let addrs = init_addrs(); let bin_step = Uint128::from(generate_random(1u16, u16::MAX)); - let (mut app, lb_factory, deployed_contracts) = setup(Some(bin_step.u128() as u16))?; + let (mut app, lb_factory, deployed_contracts) = setup(Some(bin_step.u128() as u16), None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; let token_x = token_type_snip20_generator(&shade)?; @@ -2230,224 +2241,1031 @@ pub fn test_revert_total_fee_exceeded() -> Result<(), anyhow::Error> { Ok(()) } -// #[test] -// #[serial] -// pub fn test_fuzz_user_fee_swap_in_x() -> Result<(), anyhow::Error> { -// let addrs = init_addrs(); -// let (mut app, _lb_factory, deployed_contracts, lb_pair, lb_token) = lb_pair_setup()?; -// let amount_out = Uint128::from(generate_random(1u128, DEPOSIT_AMOUNT - 1)); - -// let (amount_in, amount_out_left, _fee) = -// lb_pair::query_swap_in(&app, &lb_pair.lb_pair.contract, amount_out, true)?; -// assert_eq!(amount_out_left, Uint128::zero()); - -// let tokens_to_mint = vec![(SHADE, amount_in)]; - -// mint_token_helper( -// &mut app, -// &deployed_contracts, -// &addrs, -// addrs.joker().into_string(), -// tokens_to_mint.clone(), -// )?; - -// let token_x = &extract_contract_info(&deployed_contracts, SHADE)?; - -// lb_pair::swap_snip_20( -// &mut app, -// addrs.joker().as_str(), -// &lb_pair.lb_pair.contract, -// Some(addrs.joker().to_string()), -// token_x, -// amount_in, -// )?; - -// let shd_balance = snip20::balance_query( -// &mut app, -// addrs.joker().as_str(), -// &deployed_contracts, -// SHADE, -// "viewing_key".to_owned(), -// )?; -// assert_eq!(shd_balance, Uint128::zero()); - -// let silk_balance = snip20::balance_query( -// &mut app, -// addrs.joker().as_str(), -// &deployed_contracts, -// SILK, -// "viewing_key".to_owned(), -// )?; -// assert_eq!(silk_balance, amount_out); - -// //REMOVE LIQUIDITY - -// let token_x = extract_contract_info(&deployed_contracts, SHADE)?; -// let token_y = extract_contract_info(&deployed_contracts, SILK)?; - -// let total_bins = get_total_bins(10, 10) as u32; -// let mut balances = vec![Uint256::zero(); total_bins as usize]; -// let mut ids = vec![0u32; total_bins as usize]; - -// for i in 0..total_bins { -// let id = get_id(ACTIVE_ID, i, 10); -// ids[i as usize] = id; -// balances[i as usize] = lb_token::query_balance( -// &app, -// &lb_token, -// addrs.batman(), -// addrs.batman(), -// String::from("viewing_key"), -// id.to_string(), -// )?; -// } - -// let (reserves_x, reserves_y) = lb_pair::query_reserves(&app, &lb_pair.lb_pair.contract)?; -// lb_pair::remove_liquidity( -// &mut app, -// addrs.batman().as_str(), -// &lb_pair.lb_pair.contract, -// RemoveLiquidity { -// token_x: token_type_snip20_generator(&token_x)?, -// token_y: token_type_snip20_generator(&token_y)?, -// bin_step: lb_pair.bin_step, -// amount_x_min: Uint128::from(reserves_x), -// amount_y_min: Uint128::from(reserves_y), -// ids: ids.clone(), -// amounts: balances.clone(), -// deadline: 99999999999, -// }, -// )?; - -// let (protocol_fee_x, _) = lb_pair::query_protocol_fees(&app, &lb_pair.lb_pair.contract)?; - -// let balance_x = snip20::balance_query( -// &mut app, -// addrs.batman().as_str(), -// &deployed_contracts, -// SHADE, -// "viewing_key".to_owned(), -// )?; - -// let balance_y = snip20::balance_query( -// &mut app, -// addrs.batman().as_str(), -// &deployed_contracts, -// SILK, -// "viewing_key".to_owned(), -// )?; - -// assert_eq!( -// balance_x.u128(), -// DEPOSIT_AMOUNT + amount_in.u128() - protocol_fee_x -// ); - -// assert_eq!(balance_y.u128(), reserves_y); - -// let amount_x = Uint128::from(DEPOSIT_AMOUNT); -// let amount_y = Uint128::from(DEPOSIT_AMOUNT); -// let nb_bins_x = 10; -// let nb_bins_y = 10; - -// let token_x = extract_contract_info(&deployed_contracts, SHADE)?; -// let token_y = extract_contract_info(&deployed_contracts, SILK)?; - -// let tokens_to_mint = vec![(SHADE, amount_x), (SILK, amount_y)]; - -// mint_token_helper( -// &mut app, -// &deployed_contracts, -// &addrs, -// addrs.scare_crow().into_string(), -// tokens_to_mint.clone(), -// )?; - -// increase_allowance_helper( -// &mut app, -// &deployed_contracts, -// addrs.scare_crow().into_string(), -// lb_pair.lb_pair.contract.address.to_string(), -// tokens_to_mint, -// )?; - -// //Adding liquidity -// let liquidity_parameters = liquidity_parameters_generator( -// &deployed_contracts, -// ACTIVE_ID, -// token_x.clone(), -// token_y.clone(), -// amount_x, -// amount_y, -// nb_bins_x, -// nb_bins_y, -// )?; - -// lb_pair::add_liquidity( -// &mut app, -// addrs.scare_crow().as_str(), -// &lb_pair.lb_pair.contract, -// liquidity_parameters, -// )?; - -// let total_bins = get_total_bins(10, 10) as u32; -// let mut balances = vec![Uint256::zero(); total_bins as usize]; -// let mut ids = vec![0u32; total_bins as usize]; - -// lb_token::set_viewing_key( -// &mut app, -// addrs.scare_crow().as_str(), -// &lb_token, -// "viewing_key".to_owned(), -// )?; -// for i in 0..total_bins { -// let id = get_id(ACTIVE_ID, i, 10); -// ids[i as usize] = id; -// balances[i as usize] = lb_token::query_balance( -// &app, -// &lb_token, -// addrs.scare_crow(), -// addrs.scare_crow(), -// String::from("viewing_key"), -// id.to_string(), -// )?; -// } - -// let (reserves_x, reserves_y) = lb_pair::query_reserves(&app, &lb_pair.lb_pair.contract)?; -// lb_pair::remove_liquidity( -// &mut app, -// addrs.scare_crow().as_str(), -// &lb_pair.lb_pair.contract, -// RemoveLiquidity { -// token_x: token_type_snip20_generator(&token_x)?, -// token_y: token_type_snip20_generator(&token_y)?, -// bin_step: lb_pair.bin_step, -// amount_x_min: Uint128::from(reserves_x), -// amount_y_min: Uint128::from(reserves_y), -// ids, -// amounts: balances, -// deadline: 99999999999, -// }, -// )?; - -// // let balance_x = snip20::balance_query( -// // &mut app, -// // addrs.scare_crow().as_str(), -// // &deployed_contracts, -// // SHADE, -// // "viewing_key".to_owned(), -// // )?; - -// // let balance_y = snip20::balance_query( -// // &mut app, -// // addrs.scare_crow().as_str(), -// // &deployed_contracts, -// // SILK, -// // "viewing_key".to_owned(), -// // )?; - -// // assert_eq!(balance_x.u128(), DEPOSIT_AMOUNT); - -// // assert_eq!(balance_y.u128(), DEPOSIT_AMOUNT); - -// Ok(()) -// } +#[test] +pub fn test_fuzz_swap_in_x_and_y_btc_silk() -> Result<(), anyhow::Error> { + let addrs = init_addrs(); + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + + let btc = extract_contract_info(&deployed_contracts, SBTC)?; + let silk = extract_contract_info(&deployed_contracts, SILK)?; + let token_x = token_type_snip20_generator(&btc)?; + let token_y = token_type_snip20_generator(&silk)?; + + //assuming the ratio of btc to silk 1:40000 + //Hence 1 usilk = 400 satoishi + // (1+DEFAULT_BIN_STEP/BASIS_POINT)^x = 400 + // x = 5994 + + lb_factory::create_lb_pair( + &mut app, + addrs.admin().as_str(), + &lb_factory.clone().into(), + DEFAULT_BIN_STEP, + ACTIVE_ID + 5994, + token_x.clone(), + token_y.clone(), + "viewing_key".to_string(), + "entropy".to_string(), + )?; + let all_pairs = + lb_factory::query_all_lb_pairs(&mut app, &lb_factory.clone().into(), token_x, token_y)?; + let lb_pair = all_pairs[0].clone(); + + let lb_token = lb_pair::lb_token_query(&app, &lb_pair.lb_pair.contract)?; + + lb_token::set_viewing_key( + &mut app, + addrs.batman().as_str(), + &lb_token, + "viewing_key".to_owned(), + )?; + + let amount_x = Uint128::from(((10000u128) * 10000_0000) / 40000); // 25_000_000 satoshi + let amount_y = Uint128::from((10000u128) * 1000_000); // 10_000 silk + + let nb_bins_x = 10; + let nb_bins_y = 10; + + let token_x = extract_contract_info(&deployed_contracts, SBTC)?; + let token_y = extract_contract_info(&deployed_contracts, SILK)?; + + let tokens_to_mint = vec![(SBTC, amount_x), (SILK, amount_y)]; + + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + + snip20::set_viewing_key_exec( + &mut app, + addrs.scare_crow().as_str(), + &deployed_contracts, + SBTC, + "viewing_key".to_owned(), + )?; + + snip20::set_viewing_key_exec( + &mut app, + addrs.scare_crow().as_str(), + &deployed_contracts, + SILK, + "viewing_key".to_owned(), + )?; + + snip20::set_viewing_key_exec( + &mut app, + addrs.joker().as_str(), + &deployed_contracts, + SBTC, + "viewing_key".to_string(), + )?; + + snip20::set_viewing_key_exec( + &mut app, + addrs.joker().as_str(), + &deployed_contracts, + SILK, + "viewing_key".to_string(), + )?; + + increase_allowance_helper( + &mut app, + &deployed_contracts, + addrs.batman().into_string(), + lb_pair.lb_pair.contract.address.to_string(), + tokens_to_mint, + )?; + + //Adding liquidity + let liquidity_parameters = liquidity_parameters_generator( + &deployed_contracts, + ACTIVE_ID + 5994, + token_x, + token_y, + amount_x, + amount_y, + nb_bins_x, + nb_bins_y, + )?; + + lb_pair::add_liquidity( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + liquidity_parameters, + )?; + + //generate random number + // let amount_y_out = Uint128::from(generate_random(1u128, DEPOSIT_AMOUNT - 1)); + let amount_y_out = Uint128::from(1 * 1000_000u128); //1000 silk + // get swap_in for y + let (amount_x_in, amount_y_out_left, _fee) = + lb_pair::query_swap_in(&app, &lb_pair.lb_pair.contract, amount_y_out, true)?; + assert_eq!(amount_y_out_left, Uint128::zero()); + + // mint the tokens + let tokens_to_mint = vec![(SBTC, amount_x_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SBTC)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_x_in, + )?; + + // check the balance of silk if it's equal to the amount_y_out + + let btc_balance = snip20::balance_query( + &mut app, + addrs.batman().as_str(), + &deployed_contracts, + SBTC, + "viewing_key".to_owned(), + )?; + assert_eq!(btc_balance, Uint128::zero()); + let silk_balance = snip20::balance_query( + &mut app, + addrs.batman().as_str(), + &deployed_contracts, + SILK, + "viewing_key".to_owned(), + )?; + assert_approx_eq_rel( + Uint256::from(silk_balance), + Uint256::from(amount_y_out), + Uint256::from(1u128).checked_mul(Uint256::from(PRECISION))?, + "Error greater than 1%", + ); + + //generate random number + // let amount_y_out = Uint128::from(generate_random(1u128, DEPOSIT_AMOUNT - 1)); + let amount_x_out = Uint128::from(2 * 1000_000u128); //5_000_000 satoshi + // get swap_in for y + let (amount_y_in, amount_x_out_left, _fee) = + lb_pair::query_swap_in(&app, &lb_pair.lb_pair.contract, amount_x_out, false)?; + assert_eq!(amount_x_out_left, Uint128::zero()); + + // mint the tokens + let tokens_to_mint = vec![(SILK, amount_y_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SILK)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_y_in, + )?; + + // check the balance of silk if it's equal to the amount_y_out + let silk_balance = snip20::balance_query( + &mut app, + addrs.batman().as_str(), + &deployed_contracts, + SILK, + "viewing_key".to_owned(), + )?; + assert_approx_eq_rel( + Uint256::from(silk_balance), + Uint256::from(amount_y_out), + Uint256::from(1u128).checked_mul(Uint256::from(PRECISION))?, + "Error greater than 1%", + ); + let btc_balance = snip20::balance_query( + &mut app, + addrs.batman().as_str(), + &deployed_contracts, + SBTC, + "viewing_key".to_owned(), + )?; + assert_eq!(btc_balance, amount_x_out); + + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.lb_pair.contract)?; + + Ok(()) +} + +#[test] +pub fn test_fuzz_calculate_volume_based_rewards() -> Result<(), anyhow::Error> { + let addrs = init_addrs(); + let (mut app, lb_factory, deployed_contracts) = + setup(None, Some(RewardsDistributionAlgorithm::VolumeBasedRewards))?; + + let btc = extract_contract_info(&deployed_contracts, SBTC)?; + let silk = extract_contract_info(&deployed_contracts, SILK)?; + let token_x = token_type_snip20_generator(&btc)?; + let token_y = token_type_snip20_generator(&silk)?; + + lb_factory::create_lb_pair( + &mut app, + addrs.admin().as_str(), + &lb_factory.clone().into(), + DEFAULT_BIN_STEP, + ACTIVE_ID + 5994, + token_x.clone(), + token_y.clone(), + "viewing_key".to_string(), + "entropy".to_string(), + )?; + let all_pairs = + lb_factory::query_all_lb_pairs(&mut app, &lb_factory.clone().into(), token_x, token_y)?; + let lb_pair = all_pairs[0].clone(); + + let lb_token = lb_pair::lb_token_query(&app, &lb_pair.lb_pair.contract)?; + + lb_token::set_viewing_key( + &mut app, + addrs.batman().as_str(), + &lb_token, + "viewing_key".to_owned(), + )?; + + let deposit_ratio = (generate_random(1u128, DEPOSIT_AMOUNT)); + + let amount_x = Uint128::from(((deposit_ratio) * 10000_0000) / 40000); // 25_000_000 satoshi + let amount_y = Uint128::from((deposit_ratio) * 1000_000); // 10_000 silk + + let nb_bins_x = 10; + let nb_bins_y = 10; + + let token_x = extract_contract_info(&deployed_contracts, SBTC)?; + let token_y = extract_contract_info(&deployed_contracts, SILK)?; + + let tokens_to_mint = vec![(SBTC, amount_x), (SILK, amount_y)]; + + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + + increase_allowance_helper( + &mut app, + &deployed_contracts, + addrs.batman().into_string(), + lb_pair.lb_pair.contract.address.to_string(), + tokens_to_mint, + )?; + + //Adding liquidity + let liquidity_parameters = liquidity_parameters_generator( + &deployed_contracts, + ACTIVE_ID + 5994, + token_x, + token_y, + amount_x, + amount_y, + nb_bins_x, + nb_bins_y, + )?; + + lb_pair::add_liquidity( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + liquidity_parameters, + )?; + + //generate random number + // let amount_y_out = Uint128::from(generate_random(1u128, DEPOSIT_AMOUNT - 1)); + let amount_y_out = Uint128::from(generate_random(1u128, amount_y.u128() - 1)); + // get swap_in for y + let (amount_x_in, _amount_y_out_left, _fee) = + lb_pair::query_swap_in(&app, &lb_pair.lb_pair.contract, amount_y_out, true)?; + + // mint the tokens + let tokens_to_mint = vec![(SBTC, amount_x_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SBTC)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_x_in, + )?; + + //generate random number + // let amount_y_out = Uint128::from(generate_random(1u128, DEPOSIT_AMOUNT - 1)); + let amount_x_out = Uint128::from(generate_random(1u128, amount_x.u128() - 1)); // get swap_in for y + let (amount_y_in, _amount_x_out_left, _fee) = + lb_pair::query_swap_in(&app, &lb_pair.lb_pair.contract, amount_x_out, false)?; + + // mint the tokens + let tokens_to_mint = vec![(SILK, amount_y_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SILK)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_y_in, + )?; + + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.lb_pair.contract)?; + + let _distribution = lb_pair::query_rewards_distribution(&app, &lb_pair.lb_pair.contract, None)?; + Ok(()) +} + +#[test] +pub fn test_calculate_volume_based_rewards() -> Result<(), anyhow::Error> { + let addrs = init_addrs(); + let (mut app, lb_factory, deployed_contracts) = + setup(None, Some(RewardsDistributionAlgorithm::VolumeBasedRewards))?; + + let btc = extract_contract_info(&deployed_contracts, SBTC)?; + let silk = extract_contract_info(&deployed_contracts, SILK)?; + let token_x = token_type_snip20_generator(&btc)?; + let token_y = token_type_snip20_generator(&silk)?; + + lb_factory::create_lb_pair( + &mut app, + addrs.admin().as_str(), + &lb_factory.clone().into(), + DEFAULT_BIN_STEP, + ACTIVE_ID + 5994, + token_x.clone(), + token_y.clone(), + "viewing_key".to_string(), + "entropy".to_string(), + )?; + let all_pairs = + lb_factory::query_all_lb_pairs(&mut app, &lb_factory.clone().into(), token_x, token_y)?; + let lb_pair = all_pairs[0].clone(); + + let lb_token = lb_pair::lb_token_query(&app, &lb_pair.lb_pair.contract)?; + + lb_token::set_viewing_key( + &mut app, + addrs.batman().as_str(), + &lb_token, + "viewing_key".to_owned(), + )?; + + let deposit_ratio = DEPOSIT_AMOUNT; + + let amount_x = Uint128::from(((deposit_ratio) * 10000_0000) / 40000); + let amount_y = Uint128::from((deposit_ratio) * 1000_000); + + let nb_bins_x = 10; + let nb_bins_y = 10; + + let token_x = extract_contract_info(&deployed_contracts, SBTC)?; + let token_y = extract_contract_info(&deployed_contracts, SILK)?; + + let tokens_to_mint = vec![(SBTC, amount_x), (SILK, amount_y)]; + + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + + increase_allowance_helper( + &mut app, + &deployed_contracts, + addrs.batman().into_string(), + lb_pair.lb_pair.contract.address.to_string(), + tokens_to_mint, + )?; + + //Adding liquidity + let liquidity_parameters = liquidity_parameters_generator( + &deployed_contracts, + ACTIVE_ID + 5994, + token_x, + token_y, + amount_x, + amount_y, + nb_bins_x, + nb_bins_y, + )?; + + lb_pair::add_liquidity( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + liquidity_parameters, + )?; + + //generate random number + // let amount_y_out = Uint128::from(generate_random(1u128, DEPOSIT_AMOUNT - 1)); + let amount_y_out = amount_y.multiply_ratio(5u128, 10u128).u128(); + // get swap_in for y + let (amount_x_in, _amount_y_out_left, _fee) = + lb_pair::query_swap_in(&app, &lb_pair.lb_pair.contract, amount_y_out.into(), true)?; + + // mint the tokens + let tokens_to_mint = vec![(SBTC, amount_x_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SBTC)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_x_in, + )?; + + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 600); + + app.set_time(timestamp); + + //generate random number + // let amount_y_out = Uint128::from(generate_random(1u128, DEPOSIT_AMOUNT - 1)); + let amount_x_out = amount_x.multiply_ratio(5u128, 10u128).u128(); // get swap_in for y + let (amount_y_in, _amount_x_out_left, _fee) = + lb_pair::query_swap_in(&app, &lb_pair.lb_pair.contract, amount_x_out.into(), false)?; + + // mint the tokens + let tokens_to_mint = vec![(SILK, amount_y_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SILK)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_y_in, + )?; + + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.lb_pair.contract)?; + + let _distribution = lb_pair::query_rewards_distribution(&app, &lb_pair.lb_pair.contract, None)?; + // println!("Distribution {:?}", _distribution); + Ok(()) +} + +#[test] +pub fn test_calculate_time_based_rewards() -> Result<(), anyhow::Error> { + let addrs = init_addrs(); + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + + let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; + let silk = extract_contract_info(&deployed_contracts, SILK)?; + let token_x = token_type_snip20_generator(&sscrt)?; + let token_y = token_type_snip20_generator(&silk)?; + + lb_factory::create_lb_pair( + &mut app, + addrs.admin().as_str(), + &lb_factory.clone().into(), + DEFAULT_BIN_STEP, + ACTIVE_ID, + token_x.clone(), + token_y.clone(), + "viewing_key".to_string(), + "entropy".to_string(), + )?; + let all_pairs = + lb_factory::query_all_lb_pairs(&mut app, &lb_factory.clone().into(), token_x, token_y)?; + let lb_pair = all_pairs[0].clone(); + + let lb_token = lb_pair::lb_token_query(&app, &lb_pair.lb_pair.contract)?; + + lb_token::set_viewing_key( + &mut app, + addrs.batman().as_str(), + &lb_token, + "viewing_key".to_owned(), + )?; + + let amount_x = Uint128::from(DEPOSIT_AMOUNT); // 25_000_000 satoshi + let amount_y = Uint128::from(DEPOSIT_AMOUNT); // 10_000 silk + + let nb_bins_x = 10; + let nb_bins_y = 10; + + let token_x = extract_contract_info(&deployed_contracts, SSCRT)?; + let token_y = extract_contract_info(&deployed_contracts, SILK)?; + + let tokens_to_mint = vec![(SSCRT, amount_x), (SILK, amount_y)]; + + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + + increase_allowance_helper( + &mut app, + &deployed_contracts, + addrs.batman().into_string(), + lb_pair.lb_pair.contract.address.to_string(), + tokens_to_mint, + )?; + + //Adding liquidity + let liquidity_parameters = liquidity_parameters_generator( + &deployed_contracts, + ACTIVE_ID, + token_x, + token_y, + amount_x, + amount_y, + nb_bins_x, + nb_bins_y, + )?; + + lb_pair::add_liquidity( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + liquidity_parameters, + )?; + + let (_, bin_reserves_y) = lb_pair::query_bin(&app, &lb_pair.lb_pair.contract, ACTIVE_ID)?; + + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 3); + app.set_time(timestamp); + + //making a swap for token y hence the bin id moves to the right + let (amount_x_in, _amount_y_out_left, _fee) = lb_pair::query_swap_in( + &app, + &lb_pair.lb_pair.contract, + Uint128::from(bin_reserves_y + 1), + true, + )?; + + // mint the tokens + let tokens_to_mint = vec![(SSCRT, amount_x_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SSCRT)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_x_in, + )?; + + let active_id = lb_pair::query_active_id(&app, &lb_pair.lb_pair.contract)?; + + assert_eq!(active_id, ACTIVE_ID - 1); + + //making a swap for token y hence the bin id moves to the right + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 7); + app.set_time(timestamp); + + let (amount_x_in, _amount_y_out_left, _fee) = lb_pair::query_swap_in( + &app, + &lb_pair.lb_pair.contract, + Uint128::from(bin_reserves_y * 5), + true, + )?; + + // mint the tokens + let tokens_to_mint = vec![(SSCRT, amount_x_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SSCRT)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_x_in, + )?; + + let active_id = lb_pair::query_active_id(&app, &lb_pair.lb_pair.contract)?; + + assert_eq!(active_id, ACTIVE_ID - 1 - 5); + + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 43); + app.set_time(timestamp); + + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.lb_pair.contract)?; + + let _distribution = lb_pair::query_rewards_distribution(&app, &lb_pair.lb_pair.contract, None)?; + + Ok(()) +} + +#[test] +pub fn test_fuzz_calculate_time_based_rewards() -> Result<(), anyhow::Error> { + let addrs = init_addrs(); + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + + let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; + let silk = extract_contract_info(&deployed_contracts, SILK)?; + let token_x = token_type_snip20_generator(&sscrt)?; + let token_y = token_type_snip20_generator(&silk)?; + + lb_factory::create_lb_pair( + &mut app, + addrs.admin().as_str(), + &lb_factory.clone().into(), + DEFAULT_BIN_STEP, + ACTIVE_ID, + token_x.clone(), + token_y.clone(), + "viewing_key".to_string(), + "entropy".to_string(), + )?; + let all_pairs = + lb_factory::query_all_lb_pairs(&mut app, &lb_factory.clone().into(), token_x, token_y)?; + let lb_pair = all_pairs[0].clone(); + + let lb_token = lb_pair::lb_token_query(&app, &lb_pair.lb_pair.contract)?; + + lb_token::set_viewing_key( + &mut app, + addrs.batman().as_str(), + &lb_token, + "viewing_key".to_owned(), + )?; + + let amount_x = Uint128::from(DEPOSIT_AMOUNT); // 25_000_000 satoshi + let amount_y = Uint128::from(DEPOSIT_AMOUNT); // 10_000 silk + + let nb_bins_x = 10; + let nb_bins_y = 10; + + let token_x = extract_contract_info(&deployed_contracts, SSCRT)?; + let token_y = extract_contract_info(&deployed_contracts, SILK)?; + + let tokens_to_mint = vec![(SSCRT, amount_x), (SILK, amount_y)]; + + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + + increase_allowance_helper( + &mut app, + &deployed_contracts, + addrs.batman().into_string(), + lb_pair.lb_pair.contract.address.to_string(), + tokens_to_mint, + )?; + + //Adding liquidity + let liquidity_parameters = liquidity_parameters_generator( + &deployed_contracts, + ACTIVE_ID, + token_x, + token_y, + amount_x, + amount_y, + nb_bins_x, + nb_bins_y, + )?; + + lb_pair::add_liquidity( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + liquidity_parameters, + )?; + + let (_, bin_reserves_y) = lb_pair::query_bin(&app, &lb_pair.lb_pair.contract, ACTIVE_ID)?; + + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 3); + app.set_time(timestamp); + + //making a swap for token y hence the bin id moves to the right + let (amount_x_in, _amount_y_out_left, _fee) = lb_pair::query_swap_in( + &app, + &lb_pair.lb_pair.contract, + Uint128::from(bin_reserves_y + 1), + true, + )?; + + // mint the tokens + let tokens_to_mint = vec![(SSCRT, amount_x_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SSCRT)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_x_in, + )?; + + let active_id = lb_pair::query_active_id(&app, &lb_pair.lb_pair.contract)?; + assert_eq!(active_id, ACTIVE_ID - 1); + + //making a swap for token y hence the bin id moves to the right + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 7); + app.set_time(timestamp); + + let (amount_x_in, _amount_y_out_left, _fee) = lb_pair::query_swap_in( + &app, + &lb_pair.lb_pair.contract, + Uint128::from(bin_reserves_y * 5), + true, + )?; + + // mint the tokens + let tokens_to_mint = vec![(SSCRT, amount_x_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SSCRT)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_x_in, + )?; + + let active_id = lb_pair::query_active_id(&app, &lb_pair.lb_pair.contract)?; + assert_eq!(active_id, ACTIVE_ID - 1 - 5); + + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 43); + app.set_time(timestamp); + + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.lb_pair.contract)?; + + let _distribution = lb_pair::query_rewards_distribution(&app, &lb_pair.lb_pair.contract, None)?; + // println!("_distribution {:?}", _distribution); + Ok(()) +} + +#[test] +pub fn test_reset_rewards_config() -> Result<(), anyhow::Error> { + let addrs = init_addrs(); + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + + let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; + let silk = extract_contract_info(&deployed_contracts, SILK)?; + let token_x = token_type_snip20_generator(&sscrt)?; + let token_y = token_type_snip20_generator(&silk)?; + + lb_factory::create_lb_pair( + &mut app, + addrs.admin().as_str(), + &lb_factory.clone().into(), + DEFAULT_BIN_STEP, + ACTIVE_ID, + token_x.clone(), + token_y.clone(), + "viewing_key".to_string(), + "entropy".to_string(), + )?; + let all_pairs = + lb_factory::query_all_lb_pairs(&mut app, &lb_factory.clone().into(), token_x, token_y)?; + let lb_pair = all_pairs[0].clone(); + + let lb_token = lb_pair::lb_token_query(&app, &lb_pair.lb_pair.contract)?; + + lb_token::set_viewing_key( + &mut app, + addrs.batman().as_str(), + &lb_token, + "viewing_key".to_owned(), + )?; + + let amount_x = Uint128::from(DEPOSIT_AMOUNT); // 25_000_000 satoshi + let amount_y = Uint128::from(DEPOSIT_AMOUNT); // 10_000 silk + + let nb_bins_x = 10; + let nb_bins_y = 10; + + let token_x = extract_contract_info(&deployed_contracts, SSCRT)?; + let token_y = extract_contract_info(&deployed_contracts, SILK)?; + + let tokens_to_mint = vec![(SSCRT, amount_x), (SILK, amount_y)]; + + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + + increase_allowance_helper( + &mut app, + &deployed_contracts, + addrs.batman().into_string(), + lb_pair.lb_pair.contract.address.to_string(), + tokens_to_mint, + )?; + + //Adding liquidity + let liquidity_parameters = liquidity_parameters_generator( + &deployed_contracts, + ACTIVE_ID, + token_x, + token_y, + amount_x, + amount_y, + nb_bins_x, + nb_bins_y, + )?; + + lb_pair::add_liquidity( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + liquidity_parameters, + )?; + + lb_pair::reset_rewards_epoch( + &mut app, + addrs.admin().as_str(), + &lb_pair.lb_pair.contract, + Some(RewardsDistributionAlgorithm::VolumeBasedRewards), + None, + )?; + + let (_, bin_reserves_y) = lb_pair::query_bin(&app, &lb_pair.lb_pair.contract, ACTIVE_ID)?; + + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 3); + app.set_time(timestamp); + + //making a swap for token y hence the bin id moves to the right + let (amount_x_in, _amount_y_out_left, _fee) = lb_pair::query_swap_in( + &app, + &lb_pair.lb_pair.contract, + Uint128::from(bin_reserves_y + 1), + true, + )?; + + // mint the tokens + let tokens_to_mint = vec![(SSCRT, amount_x_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SSCRT)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_x_in, + )?; + + let active_id = lb_pair::query_active_id(&app, &lb_pair.lb_pair.contract)?; + + assert_eq!(active_id, ACTIVE_ID - 1); + + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.lb_pair.contract)?; + + let _distribution = lb_pair::query_rewards_distribution(&app, &lb_pair.lb_pair.contract, None)?; + //Eventhough the distribution was changes mid epoch the effects of change will occur after the epoch. + + assert!( + _distribution + .weightages + .iter() + .all(|&x| x == _distribution.weightages[0]) + ); + println!("_distribution {:?}", _distribution); + + //making a swap for token y hence the bin id moves to the right + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 7); + app.set_time(timestamp); + + let (amount_x_in, _amount_y_out_left, _fee) = lb_pair::query_swap_in( + &app, + &lb_pair.lb_pair.contract, + Uint128::from(bin_reserves_y * 5), + true, + )?; + + // mint the tokens + let tokens_to_mint = vec![(SSCRT, amount_x_in)]; + mint_token_helper( + &mut app, + &deployed_contracts, + &addrs, + addrs.batman().into_string(), + tokens_to_mint.clone(), + )?; + // make a swap with amount_x_in + let token_x: &ContractInfo = &extract_contract_info(&deployed_contracts, SSCRT)?; + lb_pair::swap_snip_20( + &mut app, + addrs.batman().as_str(), + &lb_pair.lb_pair.contract, + Some(addrs.batman().to_string()), + token_x, + amount_x_in, + )?; + + let active_id = lb_pair::query_active_id(&app, &lb_pair.lb_pair.contract)?; + + assert_eq!(active_id, ACTIVE_ID - 1 - 5); + + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 43); + app.set_time(timestamp); + + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.lb_pair.contract)?; + + let _distribution = lb_pair::query_rewards_distribution(&app, &lb_pair.lb_pair.contract, None)?; + //Eventhough the distribution was changes mid epoch the effects of change will occur after the epoch. + + assert!( + _distribution + .weightages + .iter() + .any(|&x| x != _distribution.weightages[0]) + ); + + // println!("_distribution {:?}", _distribution); + + Ok(()) +} diff --git a/contracts/liquidity_book/tests/src/multitests/lb_pair_initial_state.rs b/contracts/liquidity_book/tests/src/multitests/lb_pair_initial_state.rs index a04a3cb91..24b913ede 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_pair_initial_state.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_pair_initial_state.rs @@ -13,7 +13,7 @@ use crate::multitests::test_helper::*; pub fn lb_pair_setup() -> Result<(App, ContractInfo, DeployedContracts, LBPairInformation), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None)?; + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; let shd = extract_contract_info(&deployed_contracts, SHADE)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_pair_liquidity.rs b/contracts/liquidity_book/tests/src/multitests/lb_pair_liquidity.rs index 5c050ce46..c51a6d68c 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_pair_liquidity.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_pair_liquidity.rs @@ -31,7 +31,7 @@ pub fn lb_pair_setup() -> Result< anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None)?; + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_pair_swap.rs b/contracts/liquidity_book/tests/src/multitests/lb_pair_swap.rs index 2e8efc6dc..a94fbadb2 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_pair_swap.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_pair_swap.rs @@ -35,7 +35,7 @@ pub fn lb_pair_setup() -> Result< anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None)?; + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_router_integration.rs b/contracts/liquidity_book/tests/src/multitests/lb_router_integration.rs index 914dc51b6..788c9fb8b 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_router_integration.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_router_integration.rs @@ -22,7 +22,7 @@ const SWAP_AMOUNT: u128 = 1000; #[serial] pub fn router_integration() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, mut deployed_contracts) = setup(None)?; + let (mut app, lb_factory, mut deployed_contracts) = setup(None, None)?; let starting_number_of_pairs = lb_factory::query_number_of_lb_pairs(&mut app, &lb_factory.clone().into())?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_router_register_tokens.rs b/contracts/liquidity_book/tests/src/multitests/lb_router_register_tokens.rs index b66ffb76e..d7bccf73f 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_router_register_tokens.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_router_register_tokens.rs @@ -14,7 +14,7 @@ use shade_multi_test::interfaces::{ #[serial] pub fn router_registered_tokens() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, _lb_factory, mut deployed_contracts) = setup(None)?; + let (mut app, _lb_factory, mut deployed_contracts) = setup(None, None)?; //intro app router::init(&mut app, addrs.admin().as_str(), &mut deployed_contracts)?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_token.rs b/contracts/liquidity_book/tests/src/multitests/lb_token.rs index e53f3903c..bfd75a1c3 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_token.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_token.rs @@ -40,7 +40,7 @@ pub fn init_setup() -> Result< anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None)?; + let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; diff --git a/contracts/liquidity_book/tests/src/multitests/test_helper.rs b/contracts/liquidity_book/tests/src/multitests/test_helper.rs index f6b3f9570..e997c3dc3 100644 --- a/contracts/liquidity_book/tests/src/multitests/test_helper.rs +++ b/contracts/liquidity_book/tests/src/multitests/test_helper.rs @@ -10,7 +10,7 @@ use shade_multi_test::{ use shade_protocol::{ c_std::{Addr, BlockInfo, ContractInfo, StdResult, Timestamp, Uint128, Uint256}, lb_libraries::{constants::PRECISION, math::u24::U24}, - liquidity_book::lb_pair::LiquidityParameters, + liquidity_book::lb_pair::{LiquidityParameters, RewardsDistributionAlgorithm}, multi_test::App, swap::core::TokenType, utils::{asset::Contract, cycle::parse_utc_datetime, MultiTestable}, @@ -127,7 +127,10 @@ pub fn assert_approx_eq_abs(a: Uint256, b: Uint256, delta: Uint256, error_messag } } -pub fn setup(bin_step: Option) -> Result<(App, Contract, DeployedContracts), anyhow::Error> { +pub fn setup( + bin_step: Option, + rewards_distribution_algorithm: Option, +) -> Result<(App, Contract, DeployedContracts), anyhow::Error> { // init snip-20's let mut app = App::default(); let addrs = init_addrs(); @@ -239,6 +242,11 @@ pub fn setup(bin_step: Option) -> Result<(App, Contract, DeployedContracts) addrs.joker(), 0, admin_contract.into(), + 10, + Some( + rewards_distribution_algorithm + .unwrap_or(RewardsDistributionAlgorithm::TimeBasedRewards), + ), )?; let lb_token_stored_code = app.store_code(LbToken::default().contract()); let lb_pair_stored_code = app.store_code(LbPair::default().contract()); @@ -434,6 +442,34 @@ pub fn liquidity_parameters_generator( amount_y: Uint128, nb_bins_x: u8, nb_bins_y: u8, +) -> StdResult { + liquidity_parameters_generator_custom( + // Assuming lbPair has methods to get tokenX and tokenY + // lbPair: &LBPair, + _deployed_contracts, + active_id, + token_x, + token_y, + amount_x, + amount_y, + nb_bins_x, + nb_bins_y, + DEFAULT_BIN_STEP, + ) +} + +pub fn liquidity_parameters_generator_custom( + // Assuming lbPair has methods to get tokenX and tokenY + // lbPair: &LBPair, + _deployed_contracts: &DeployedContracts, + active_id: u32, + token_x: ContractInfo, + token_y: ContractInfo, + amount_x: Uint128, + amount_y: Uint128, + nb_bins_x: u8, + nb_bins_y: u8, + bin_step: u16, ) -> StdResult { let total = get_total_bins(nb_bins_x, nb_bins_y); @@ -478,7 +514,7 @@ pub fn liquidity_parameters_generator( contract_addr: token_y.address, token_code_hash: token_y.code_hash, }, - bin_step: DEFAULT_BIN_STEP, + bin_step, amount_x, amount_y, amount_x_min: amount_x.multiply_ratio(90u128, 100u128), diff --git a/packages/multi_test/src/interfaces/lb_factory.rs b/packages/multi_test/src/interfaces/lb_factory.rs index 1633af811..67ab2434e 100644 --- a/packages/multi_test/src/interfaces/lb_factory.rs +++ b/packages/multi_test/src/interfaces/lb_factory.rs @@ -3,7 +3,9 @@ use shade_protocol::{ c_std::{Addr, ContractInfo, StdError, StdResult}, contract_interfaces::liquidity_book::lb_factory, lb_libraries::types::{ContractInstantiationInfo, LBPair, LBPairInformation}, + liquidity_book::lb_pair::RewardsDistributionAlgorithm, multi_test::App, + swap::core::TokenType, utils::{ asset::{Contract, RawContract}, ExecuteCallback, @@ -11,7 +13,6 @@ use shade_protocol::{ MultiTestable, Query, }, - swap::core::TokenType, }; pub fn init( @@ -20,6 +21,8 @@ pub fn init( fee_recipient: Addr, flash_loan_fee: u8, admin_auth: RawContract, + total_reward_bins: u32, + rewards_distribution_algorithm: Option, ) -> StdResult { let lb_factory = Contract::from( match (lb_factory::InstantiateMsg { @@ -27,6 +30,9 @@ pub fn init( fee_recipient, flash_loan_fee, admin_auth, + total_reward_bins, + rewards_distribution_algorithm: rewards_distribution_algorithm + .unwrap_or(RewardsDistributionAlgorithm::TimeBasedRewards), } .test_init( LbFactory::default(), diff --git a/packages/multi_test/src/interfaces/lb_pair.rs b/packages/multi_test/src/interfaces/lb_pair.rs index 29838b306..9342d6993 100644 --- a/packages/multi_test/src/interfaces/lb_pair.rs +++ b/packages/multi_test/src/interfaces/lb_pair.rs @@ -3,8 +3,14 @@ use shade_protocol::{ c_std::{to_binary, Addr, Coin, ContractInfo, StdError, StdResult, Uint128, Uint256}, contract_interfaces::{liquidity_book::lb_pair, snip20}, lb_libraries::types::{ContractInstantiationInfo, StaticFeeParameters}, - liquidity_book::lb_pair::{LiquidityParameters, RemoveLiquidity}, + liquidity_book::lb_pair::{ + LiquidityParameters, + RemoveLiquidity, + RewardsDistribution, + RewardsDistributionAlgorithm, + }, multi_test::App, + swap::core::TokenType, utils::{ asset::{Contract, RawContract}, ExecuteCallback, @@ -12,7 +18,6 @@ use shade_protocol::{ MultiTestable, Query, }, - swap::core::TokenType, }; pub fn init( @@ -30,6 +35,8 @@ pub fn init( entropy: String, protocol_fee_recipient: Addr, admin_auth: RawContract, + total_reward_bins: u32, + rewards_distribution_algorithm: Option, ) -> StdResult { let lb_pair = Contract::from( match (lb_pair::InstantiateMsg { @@ -44,6 +51,9 @@ pub fn init( entropy, protocol_fee_recipient, admin_auth, + total_reward_bins: Some(total_reward_bins), + rewards_distribution_algorithm: rewards_distribution_algorithm + .unwrap_or(RewardsDistributionAlgorithm::TimeBasedRewards), } .test_init( LbPair::default(), @@ -149,6 +159,36 @@ pub fn collect_protocol_fees(app: &mut App, sender: &str, lb_pair: &ContractInfo } } +pub fn calculate_rewards(app: &mut App, sender: &str, lb_pair: &ContractInfo) -> StdResult<()> { + match (lb_pair::ExecuteMsg::CalculateRewards {}.test_exec( + lb_pair, + app, + Addr::unchecked(sender), + &[], + )) { + Ok(_) => Ok(()), + Err(e) => return Err(StdError::generic_err(e.root_cause().to_string())), + } +} + +pub fn reset_rewards_epoch( + app: &mut App, + sender: &str, + lb_pair: &ContractInfo, + distribution: Option, + base_rewards_bins: Option, +) -> StdResult<()> { + match (lb_pair::ExecuteMsg::ResetRewardsConfig { + distribution, + base_rewards_bins, + } + .test_exec(lb_pair, app, Addr::unchecked(sender), &[])) + { + Ok(_) => Ok(()), + Err(e) => return Err(StdError::generic_err(e.root_cause().to_string())), + } +} + pub fn set_static_fee_parameters( app: &mut App, sender: &str, @@ -315,6 +355,16 @@ pub fn query_active_id(app: &App, lb_pair: &ContractInfo) -> StdResult { Ok(active_id) } +pub fn query_rewards_distribution( + app: &App, + lb_pair: &ContractInfo, + epoch_id: Option, +) -> StdResult { + let res = lb_pair::QueryMsg::GetRewardsDistribution { epoch_id }.test_query(lb_pair, app)?; + let lb_pair::RewardsDistributionResponse { distribution } = res; + Ok(distribution) +} + pub fn query_bin(app: &App, lb_pair: &ContractInfo, id: u32) -> StdResult<(u128, u128)> { let res = lb_pair::QueryMsg::GetBin { id }.test_query(lb_pair, app)?; let lb_pair::BinResponse { diff --git a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_factory.rs b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_factory.rs index 870a5fc4d..087853166 100644 --- a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_factory.rs +++ b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_factory.rs @@ -1,4 +1,4 @@ -use super::lb_pair; +use super::lb_pair::{self, RewardsDistributionAlgorithm}; use crate::{ c_std::Addr, cosmwasm_schema::{cw_serde, QueryResponses}, @@ -14,6 +14,8 @@ pub struct InstantiateMsg { pub owner: Option, pub fee_recipient: Addr, pub flash_loan_fee: u8, + pub total_reward_bins: u32, + pub rewards_distribution_algorithm: RewardsDistributionAlgorithm, } impl InstantiateCallback for InstantiateMsg { const BLOCK_SIZE: usize = 256; diff --git a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/bin_helper.rs b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/bin_helper.rs index 0fd1b23c8..9b1262ecb 100644 --- a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/bin_helper.rs +++ b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/bin_helper.rs @@ -316,9 +316,8 @@ impl BinHelper { swap_for_y: bool, active_id: u32, amounts_in_left: Bytes32, + price: U256, ) -> Result<(Bytes32, Bytes32, Bytes32), BinError> { - let price = PriceHelper::get_price_from_id(active_id, bin_step)?; - let bin_reserve_out = bin_reserves.decode_alt(!swap_for_y); let max_amount_in = if swap_for_y { diff --git a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/math/u256x256_math.rs b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/math/u256x256_math.rs index 77d0704f4..ad6fddfb0 100644 --- a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/math/u256x256_math.rs +++ b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/math/u256x256_math.rs @@ -371,74 +371,74 @@ impl U256x256Math { // Compute remainder using mulmod. let remainder = mulmod(x, y, denominator); - println!("remainder: {:#?}", remainder); + // println!("remainder: {:#?}", remainder); // Subtract 256 bit number from 512 bit number. if remainder > prod0 { prod1 = prod1.wrapping_sub(U256::ONE) } prod0 = prod0.wrapping_sub(remainder); - println!("prod0 - remainder: {:#?}", prod0); + // println!("prod0 - remainder: {:#?}", prod0); // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1 // See https://cs.stackexchange.com/q/138556/92363 // Does not overflow because the denominator cannot be zero at this stage in the function let mut lpotdod = denominator & (!denominator + U256::ONE); - println!("lpotdod: {:#?}", lpotdod); + // println!("lpotdod: {:#?}", lpotdod); // Divide denominator by lpotdod. let denominator = denominator / lpotdod; - println!("denominator / lpotdod: {:#?}", denominator); + // println!("denominator / lpotdod: {:#?}", denominator); // Divide [prod1 prod0] by lpotdod. let prod0 = prod0 / lpotdod; - println!("prod0 / lpotdod: {:#?}", prod0); + // println!("prod0 / lpotdod: {:#?}", prod0); // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one match lpotdod { U256::ONE => lpotdod = U256::ONE, _ => lpotdod = (U256::ZERO.wrapping_sub(lpotdod) / lpotdod).wrapping_add(U256::ONE), } - println!("lpotdod: {:#?}", lpotdod); + // println!("lpotdod: {:#?}", lpotdod); // Shift in bits from prod1 into prod0 let prod0 = prod0 | (prod1.wrapping_mul(lpotdod)); - println!("prod0 bit-shifted: {:#?}", prod0); + // println!("prod0 bit-shifted: {:#?}", prod0); // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4 let mut inverse = U256::from(3u8).wrapping_mul(denominator).bitxor(2); - println!("inverse: {:#?}", inverse); + // println!("inverse: {:#?}", inverse); // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step inverse = inverse .wrapping_mul(U256::from(2u8).wrapping_sub(denominator.wrapping_mul(inverse))); // inverse mod 2^8 - println!("inverse: {:#?}", inverse); + // println!("inverse: {:#?}", inverse); inverse = inverse .wrapping_mul(U256::from(2u8).wrapping_sub(denominator.wrapping_mul(inverse))); // inverse mod 2^16 - println!("inverse: {:#?}", inverse); + // println!("inverse: {:#?}", inverse); inverse = inverse .wrapping_mul(U256::from(2u8).wrapping_sub(denominator.wrapping_mul(inverse))); // inverse mod 2^32 - println!("inverse: {:#?}", inverse); + // println!("inverse: {:#?}", inverse); inverse = inverse .wrapping_mul(U256::from(2u8).wrapping_sub(denominator.wrapping_mul(inverse))); // inverse mod 2^64 - println!("inverse: {:#?}", inverse); + // println!("inverse: {:#?}", inverse); inverse = inverse .wrapping_mul(U256::from(2u8).wrapping_sub(denominator.wrapping_mul(inverse))); // inverse mod 2^128 - println!("inverse: {:#?}", inverse); + // println!("inverse: {:#?}", inverse); inverse = inverse .wrapping_mul(U256::from(2u8).wrapping_sub(denominator.wrapping_mul(inverse))); // inverse mod 2^256 - println!("inverse: {:#?}", inverse); + // println!("inverse: {:#?}", inverse); // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0.wrapping_mul(inverse); - println!("result: {:#?}", result); + // println!("result: {:#?}", result); Ok(result) } diff --git a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/mod.rs b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/mod.rs index 89281f38f..74aaf9288 100644 --- a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/mod.rs +++ b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_libraries/mod.rs @@ -1,5 +1,7 @@ //! Helper Libraries +use cosmwasm_std::Uint256; + pub mod bin_helper; pub mod constants; pub mod error; @@ -12,3 +14,18 @@ pub mod price_helper; pub mod transfer; pub mod types; pub mod viewing_keys; + +pub fn approx_div(a: Uint256, b: Uint256) -> Uint256 { + if b == Uint256::zero() { + panic!("Division by zero"); + } + let div = a / b; + let rem = a % b; + if rem >= b / Uint256::from(2u128) { + // If so, we add one to the division result + div + Uint256::one() + } else { + // If not, we return the division result as it is + div + } +} diff --git a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_pair.rs b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_pair.rs index ad88e31ef..0cd00a579 100644 --- a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_pair.rs +++ b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_pair.rs @@ -21,6 +21,8 @@ pub struct InstantiateMsg { pub entropy: String, pub protocol_fee_recipient: Addr, pub admin_auth: RawContract, + pub total_reward_bins: Option, + pub rewards_distribution_algorithm: RewardsDistributionAlgorithm, } impl InstantiateCallback for InstantiateMsg { @@ -66,6 +68,17 @@ pub enum ExecuteMsg { max_volatility_accumulator: u32, }, ForceDecay {}, + CalculateRewards {}, + ResetRewardsConfig { + distribution: Option, + base_rewards_bins: Option, + }, +} + +#[cw_serde] +pub enum RewardsDistributionAlgorithm { + TimeBasedRewards, + VolumeBasedRewards, } // impl ExecuteMsg { @@ -174,6 +187,8 @@ pub enum QueryMsg { }, #[returns(TotalSupplyResponse)] TotalSupply { id: u32 }, + #[returns(RewardsDistributionResponse)] + GetRewardsDistribution { epoch_id: Option }, } impl Query for QueryMsg { const BLOCK_SIZE: usize = 256; @@ -331,6 +346,18 @@ pub struct TotalSupplyResponse { pub total_supply: Uint256, } +#[cw_serde] +pub struct RewardsDistributionResponse { + pub distribution: RewardsDistribution, +} + +#[cw_serde] +pub struct RewardsDistribution { + pub ids: Vec, + pub weightages: Vec, + pub denominator: u16, +} + #[cw_serde] pub struct LiquidityParameters { pub token_x: TokenType, diff --git a/packages/shade_protocol/src/utils/liquidity_book/constants.rs b/packages/shade_protocol/src/utils/liquidity_book/constants.rs new file mode 100644 index 000000000..c479ec961 --- /dev/null +++ b/packages/shade_protocol/src/utils/liquidity_book/constants.rs @@ -0,0 +1,20 @@ +//! ### Liquidity Book Constants Library +//! Author: Kent +//! +//! Set of constants for Liquidity Book contracts. + +use ethnum::U256; +// use cosmwasm_std::Uint256; + +pub static SCALE_OFFSET: u8 = 128; + +// use this one for ethnum U256: +pub static SCALE: U256 = U256::from_words(1, 0); + +pub static PRECISION: u128 = 1_000_000_000_000_000_000; +pub static SQUARED_PRECISION: u128 = PRECISION * PRECISION; + +pub static MAX_FEE: u128 = 1_00_000_000_000_000_000; // 10% +pub static MAX_PROTOCOL_SHARE: u32 = 2_500; // 25% of the fee + +pub static BASIS_POINT_MAX: u32 = 10_000;