diff --git a/Cargo.lock b/Cargo.lock index 4b4c525d9..e6c6c59df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ahash" @@ -1521,13 +1521,11 @@ version = "0.0.0" dependencies = [ "hex", "hex-literal 0.3.4", - "locking_module", "multiversx-sc", "multiversx-sc-modules", "multiversx-sc-scenario", "num-bigint", "num-traits", - "simple-lock", ] [[package]] diff --git a/dex/fuzz/src/fuzz_data.rs b/dex/fuzz/src/fuzz_data.rs index 320daf5d6..5ced198e3 100644 --- a/dex/fuzz/src/fuzz_data.rs +++ b/dex/fuzz/src/fuzz_data.rs @@ -9,19 +9,13 @@ pub mod fuzz_data_tests { use farm::exit_penalty::ExitPenaltyModule; use farm::*; use farm_token::FarmTokenModule; - use multiversx_sc::codec::Empty; use multiversx_sc::types::{Address, BigUint, EsdtLocalRole}; use multiversx_sc_scenario::{ - managed_address, managed_biguint, managed_token_id, managed_token_id_wrapped, rust_biguint, - whitebox_legacy::*, DebugApi, + managed_address, managed_biguint, managed_token_id, rust_biguint, whitebox_legacy::*, + DebugApi, }; use pair::*; use pausable::{PausableModule, State}; - use price_discovery::redeem_token::*; - use price_discovery::*; - use simple_lock::locked_token::LockedTokenModule; - use simple_lock::SimpleLock; - use rand::prelude::StdRng; use rand::SeedableRng; use std::cell::Cell; @@ -31,7 +25,6 @@ pub mod fuzz_data_tests { pub const FARM_WASM_PATH: &str = "farm/output/farm.wasm"; pub const PAIR_WASM_PATH: &str = "pair/output/pair.wasm"; - pub const PD_WASM_PATH: &str = "../output/price-discovery.wasm"; pub const WEGLD_TOKEN_ID: &[u8] = b"WEGLD-abcdef"; pub const MEX_TOKEN_ID: &[u8] = b"MEX-abcdef"; @@ -50,25 +43,8 @@ pub mod fuzz_data_tests { pub const DIVISION_SAFETY_CONSTANT: u64 = 1_000_000_000_000; // Price discovery constants - pub const DISC_LAUNCHED_TOKEN_ID: &[u8] = b"SOCOOLWOW-123456"; - pub const DISC_ACCEPTED_TOKEN_ID: &[u8] = b"USDC-123456"; - pub const DISC_REDEEM_TOKEN_ID: &[u8] = b"GIBREWARDS-123456"; - pub const LOCKED_TOKEN_ID: &[u8] = b"LOCKED-abcdef"; - pub const DISC_LAUNCHED_TOKEN_REDEEM_NONCE: u64 = 1; - pub const DISC_ACCEPTED_TOKEN_REDEEM_NONCE: u64 = 2; - pub const USER_TOTAL_DISC_TOKENS: u64 = 1_000_000_000; - pub const DISC_LAUNCHED_TOKENS: u64 = 5_000_000_000; - pub const DISC_USER_LAUNCH_TOKENS: u64 = 100_000_000; pub const TOTAL_FEE_PERCENT: u64 = 300; pub const SPECIAL_FEE_PERCENT: u64 = 50; - pub const MIN_PENALTY_PERCENTAGE: u64 = 1_000_000_000_000; // 10% - pub const MAX_PENALTY_PERCENTAGE: u64 = 5_000_000_000_000; // 50% - pub const FIXED_PENALTY_PERCENTAGE: u64 = 2_500_000_000_000; // 25% - pub const START_BLOCK: u64 = 1; - pub const NO_LIMIT_PHASE_DURATION_BLOCKS: u64 = 150; - pub const LINEAR_PENALTY_PHASE_DURATION_BLOCKS: u64 = 50; - pub const FIXED_PENALTY_PHASE_DURATION_BLOCKS: u64 = 25; - pub const UNLOCK_EPOCH: u64 = 20; #[derive(Clone, TopEncode)] pub struct FuzzDexExecutorInitArgs { @@ -81,9 +57,6 @@ pub mod fuzz_data_tests { pub exit_farm_prob: u64, pub claim_rewards_prob: u64, pub compound_rewards_prob: u64, - pub price_discovery_deposit_prob: u64, - pub price_discovery_withdraw_prob: u64, - pub price_discovery_redeem_prob: u64, pub block_nonce_increase: u64, pub compound_rewards_max_value: u64, pub token_deposit_max_value: u64, @@ -93,9 +66,6 @@ pub mod fuzz_data_tests { pub enter_farm_max_value: u64, pub exit_farm_max_value: u64, pub claim_rewards_max_value: u64, - pub price_discovery_deposit_max_value: u64, - pub price_discovery_withdraw_max_value: u64, - pub price_discovery_redeem_max_value: u64, } impl FuzzDexExecutorInitArgs { @@ -110,9 +80,6 @@ pub mod fuzz_data_tests { exit_farm_prob: 10, claim_rewards_prob: 15, compound_rewards_prob: 10, - price_discovery_deposit_prob: 30, - price_discovery_withdraw_prob: 15, - price_discovery_redeem_prob: 30, block_nonce_increase: 1, compound_rewards_max_value: 1000000u64, token_deposit_max_value: 50000000u64, @@ -122,18 +89,14 @@ pub mod fuzz_data_tests { enter_farm_max_value: 100000000u64, exit_farm_max_value: 1000000u64, claim_rewards_max_value: 1000000u64, - price_discovery_deposit_max_value: 1000000u64, - price_discovery_withdraw_max_value: 1000000u64, - price_discovery_redeem_max_value: 1000000u64, } } } - pub struct FuzzerData + pub struct FuzzerData where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { pub rng: StdRng, pub fuzz_args: FuzzDexExecutorInitArgs, @@ -142,33 +105,25 @@ pub mod fuzz_data_tests { pub users: Vec, pub swap_pairs: Vec>, pub farms: Vec>, - pub price_disc: PriceDiscSetup, } - impl - FuzzerData + impl FuzzerData where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { - pub fn new( - seed: u64, - pair_builder: PairObjBuilder, - farm_builder: FarmObjBuilder, - price_discovery: PriceDiscObjBuilder, - ) -> Self { + pub fn new(seed: u64, pair_builder: PairObjBuilder, farm_builder: FarmObjBuilder) -> Self { let egld_amount = rust_biguint!(OWNER_EGLD_BALANCE); let rng = StdRng::seed_from_u64(seed); let fuzz_args = FuzzDexExecutorInitArgs::new(); - let statistics = EventsStatistics::new(); + let statistics = EventsStatistics::default(); let mut blockchain_wrapper = BlockchainStateWrapper::new(); let owner_addr = blockchain_wrapper.create_user_account(&egld_amount); let mut users = vec![]; - for i in 1..=fuzz_args.num_users { + for _ in 1..=fuzz_args.num_users { let user_address = blockchain_wrapper.create_user_account(&egld_amount); blockchain_wrapper.set_esdt_balance( &user_address, @@ -186,27 +141,8 @@ pub mod fuzz_data_tests { &rust_biguint!(USER_TOTAL_BUSD_TOKENS), ); - // 2/3 chance for price discovery buy intention - // else sale intention - let mut price_discovery_buy = false; - if i % 3 == 0 { - blockchain_wrapper.set_esdt_balance( - &user_address, - DISC_LAUNCHED_TOKEN_ID, - &rust_biguint!(DISC_USER_LAUNCH_TOKENS), - ); - } else { - blockchain_wrapper.set_esdt_balance( - &user_address, - DISC_ACCEPTED_TOKEN_ID, - &rust_biguint!(USER_TOTAL_DISC_TOKENS), - ); - price_discovery_buy = true; - } - let user = User { address: user_address, - price_discovery_buy, }; users.push(user); @@ -264,9 +200,6 @@ pub mod fuzz_data_tests { let farms = vec![first_farm, second_farm, third_farm]; - let price_disc = - setup_price_disc(&owner_addr, &mut blockchain_wrapper, price_discovery); - FuzzerData { rng, fuzz_args, @@ -275,7 +208,6 @@ pub mod fuzz_data_tests { users, swap_pairs, farms, - price_disc, } } } @@ -283,7 +215,6 @@ pub mod fuzz_data_tests { #[derive()] pub struct User { pub address: Address, - pub price_discovery_buy: bool, } #[derive()] @@ -466,130 +397,7 @@ pub mod fuzz_data_tests { } } - pub struct PriceDiscSetup - where - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, - { - pub pd_wrapper: - ContractObjWrapper, PriceDiscObjBuilder>, - } - - pub fn setup_price_disc( - owner_addr: &Address, - blockchain_wrapper: &mut BlockchainStateWrapper, - pd_builder: PriceDiscObjBuilder, - ) -> PriceDiscSetup - where - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, - { - let rust_zero = rust_biguint!(0u64); - let pd_wrapper = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(owner_addr), - pd_builder, - PD_WASM_PATH, - ); - - // set user balances - blockchain_wrapper.set_esdt_balance( - owner_addr, - DISC_LAUNCHED_TOKEN_ID, - &rust_biguint!(DISC_LAUNCHED_TOKENS), - ); - - // set sc roles and initial minted SFTs (only needed for the purpose of SFT add quantity) - blockchain_wrapper.set_esdt_local_roles( - pd_wrapper.address_ref(), - DISC_REDEEM_TOKEN_ID, - &[ - EsdtLocalRole::NftCreate, - EsdtLocalRole::NftBurn, - EsdtLocalRole::NftAddQuantity, - ], - ); - blockchain_wrapper.set_nft_balance( - pd_wrapper.address_ref(), - DISC_REDEEM_TOKEN_ID, - LAUNCHED_TOKEN_REDEEM_NONCE, - &rust_biguint!(1), - &Empty, - ); - blockchain_wrapper.set_nft_balance( - pd_wrapper.address_ref(), - DISC_REDEEM_TOKEN_ID, - ACCEPTED_TOKEN_REDEEM_NONCE, - &rust_biguint!(1), - &Empty, - ); - - // init locking SC - let locking_sc_wrapper = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(owner_addr), - simple_lock::contract_obj, - "lock wasm path", - ); - blockchain_wrapper - .execute_tx(owner_addr, &locking_sc_wrapper, &rust_zero, |sc| { - sc.init(); - sc.locked_token() - .set_token_id(managed_token_id!(LOCKED_TOKEN_ID)); - }) - .assert_ok(); - - blockchain_wrapper.set_esdt_local_roles( - locking_sc_wrapper.address_ref(), - LOCKED_TOKEN_ID, - &[ - EsdtLocalRole::NftCreate, - EsdtLocalRole::NftAddQuantity, - EsdtLocalRole::NftBurn, - ], - ); - - // init Price Discovery SC - blockchain_wrapper - .execute_tx(owner_addr, &pd_wrapper, &rust_zero, |sc| { - sc.init( - managed_token_id!(DISC_LAUNCHED_TOKEN_ID), - managed_token_id_wrapped!(DISC_ACCEPTED_TOKEN_ID), - 18, - managed_biguint!(0), - START_BLOCK, - NO_LIMIT_PHASE_DURATION_BLOCKS, - LINEAR_PENALTY_PHASE_DURATION_BLOCKS, - FIXED_PENALTY_PHASE_DURATION_BLOCKS, - UNLOCK_EPOCH, - managed_biguint!(MIN_PENALTY_PERCENTAGE), - managed_biguint!(MAX_PENALTY_PERCENTAGE), - managed_biguint!(FIXED_PENALTY_PERCENTAGE), - managed_address!(locking_sc_wrapper.address_ref()), - ); - - sc.redeem_token() - .set_token_id(managed_token_id!(DISC_REDEEM_TOKEN_ID)); - }) - .assert_ok(); - - blockchain_wrapper.set_block_nonce(START_BLOCK); - - blockchain_wrapper - .execute_esdt_transfer( - owner_addr, - &pd_wrapper, - DISC_LAUNCHED_TOKEN_ID, - 0, - &rust_biguint!(DISC_LAUNCHED_TOKENS), - |sc| { - sc.deposit(); - }, - ) - .assert_ok(); - - PriceDiscSetup { pd_wrapper } - } - - #[derive(Clone, PartialEq)] + #[derive(Clone, PartialEq, Default)] pub struct EventsStatistics { pub swap_fixed_input_hits: u64, pub swap_fixed_input_misses: u64, @@ -616,46 +424,6 @@ pub mod fuzz_data_tests { pub compound_rewards_hits: u64, pub compound_rewards_misses: u64, - - pub price_discovery_deposit_hits: u64, - pub price_discovery_deposit_misses: u64, - - pub price_discovery_withdraw_hits: u64, - pub price_discovery_withdraw_misses: u64, - - pub price_discovery_redeem_hits: u64, - pub price_discovery_redeem_misses: u64, - } - - impl EventsStatistics { - pub fn new() -> EventsStatistics { - EventsStatistics { - swap_fixed_input_hits: 0, - swap_fixed_input_misses: 0, - swap_fixed_output_hits: 0, - swap_fixed_output_misses: 0, - add_liquidity_hits: 0, - add_liquidity_misses: 0, - remove_liquidity_hits: 0, - remove_liquidity_misses: 0, - enter_farm_hits: 0, - enter_farm_misses: 0, - exit_farm_hits: 0, - exit_farm_misses: 0, - exit_farm_with_rewards: 0, - claim_rewards_hits: 0, - claim_rewards_misses: 0, - claim_rewards_with_rewards: 0, - compound_rewards_hits: 0, - compound_rewards_misses: 0, - price_discovery_deposit_hits: 0, - price_discovery_deposit_misses: 0, - price_discovery_withdraw_hits: 0, - price_discovery_withdraw_misses: 0, - price_discovery_redeem_hits: 0, - price_discovery_redeem_misses: 0, - } - } } pub fn to_managed_biguint(value: RustBigUint) -> BigUint { diff --git a/dex/fuzz/src/fuzz_farm.rs b/dex/fuzz/src/fuzz_farm.rs index bf1d32618..6aa95861a 100644 --- a/dex/fuzz/src/fuzz_farm.rs +++ b/dex/fuzz/src/fuzz_farm.rs @@ -15,12 +15,11 @@ pub mod fuzz_farm_test { use rand::prelude::*; - pub fn enter_farm( - fuzzer_data: &mut FuzzerData, + pub fn enter_farm( + fuzzer_data: &mut FuzzerData, ) where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { let rust_zero = rust_biguint!(0u64); @@ -112,12 +111,11 @@ pub mod fuzz_farm_test { } } - pub fn exit_farm( - fuzzer_data: &mut FuzzerData, + pub fn exit_farm( + fuzzer_data: &mut FuzzerData, ) where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { let rust_zero = rust_biguint!(0u64); @@ -195,12 +193,11 @@ pub mod fuzz_farm_test { } } - pub fn claim_rewards( - fuzzer_data: &mut FuzzerData, + pub fn claim_rewards( + fuzzer_data: &mut FuzzerData, ) where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { let rust_zero = rust_biguint!(0u64); @@ -292,12 +289,11 @@ pub mod fuzz_farm_test { } } - pub fn compound_rewards( - fuzzer_data: &mut FuzzerData, + pub fn compound_rewards( + fuzzer_data: &mut FuzzerData, ) where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { let rust_zero = rust_biguint!(0u64); diff --git a/dex/fuzz/src/fuzz_pair.rs b/dex/fuzz/src/fuzz_pair.rs index e23babcf1..e6b87f23e 100644 --- a/dex/fuzz/src/fuzz_pair.rs +++ b/dex/fuzz/src/fuzz_pair.rs @@ -16,12 +16,11 @@ pub mod fuzz_pair_test { add_liq::AddLiquidityModule, remove_liq::RemoveLiquidityModule, swap::SwapModule, }; - pub fn add_liquidity( - fuzzer_data: &mut FuzzerData, + pub fn add_liquidity( + fuzzer_data: &mut FuzzerData, ) where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { let pair_index = fuzzer_data.rng.gen_range(0..fuzzer_data.swap_pairs.len()); let caller_index = fuzzer_data.rng.gen_range(0..fuzzer_data.users.len()); @@ -119,12 +118,11 @@ pub mod fuzz_pair_test { } } - pub fn remove_liquidity( - fuzzer_data: &mut FuzzerData, + pub fn remove_liquidity( + fuzzer_data: &mut FuzzerData, ) where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { let pair_index = fuzzer_data.rng.gen_range(0..fuzzer_data.swap_pairs.len()); let caller_index = fuzzer_data.rng.gen_range(0..fuzzer_data.users.len()); @@ -212,12 +210,11 @@ pub mod fuzz_pair_test { } } - pub fn swap_pair( - fuzzer_data: &mut FuzzerData, + pub fn swap_pair( + fuzzer_data: &mut FuzzerData, ) where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { let pair_index = fuzzer_data.rng.gen_range(0..fuzzer_data.swap_pairs.len()); let caller_index = fuzzer_data.rng.gen_range(0..fuzzer_data.users.len()); diff --git a/dex/fuzz/src/fuzz_price_discovery.rs b/dex/fuzz/src/fuzz_price_discovery.rs deleted file mode 100644 index 37c1539d3..000000000 --- a/dex/fuzz/src/fuzz_price_discovery.rs +++ /dev/null @@ -1,335 +0,0 @@ -#[cfg(test)] -pub mod fuzz_price_discovery_test { - #![allow(deprecated)] - - multiversx_sc::imports!(); - multiversx_sc::derive_imports!(); - - use multiversx_sc_scenario::{rust_biguint, DebugApi}; - - use rand::prelude::*; - - use crate::fuzz_data::fuzz_data_tests::*; - use price_discovery::PriceDiscovery; - - pub fn price_discovery_deposit( - fuzzer_data: &mut FuzzerData, - ) where - PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, - FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, - { - let caller_index = fuzzer_data.rng.gen_range(0..fuzzer_data.users.len()); - let caller = &mut fuzzer_data.users[caller_index]; - let price_disc = &mut fuzzer_data.price_disc; - - let redeem_token_id = DISC_REDEEM_TOKEN_ID; - let (payment_token_id, redeem_token_nonce) = if caller.price_discovery_buy { - (DISC_ACCEPTED_TOKEN_ID, DISC_ACCEPTED_TOKEN_REDEEM_NONCE) - } else { - (DISC_LAUNCHED_TOKEN_ID, DISC_LAUNCHED_TOKEN_REDEEM_NONCE) - }; - - let seed = fuzzer_data - .rng - .gen_range(0..fuzzer_data.fuzz_args.price_discovery_deposit_max_value) - + 1; - - let deposit_amount = rust_biguint!(seed); - - let caller_token_before = - fuzzer_data - .blockchain_wrapper - .get_esdt_balance(&caller.address, payment_token_id, 0); - - let redeem_token_before = fuzzer_data.blockchain_wrapper.get_esdt_balance( - &caller.address, - redeem_token_id, - redeem_token_nonce, - ); - - let sc_launched_token_before = fuzzer_data.blockchain_wrapper.get_esdt_balance( - price_disc.pd_wrapper.address_ref(), - DISC_LAUNCHED_TOKEN_ID, - 0, - ); - - let sc_accepted_token_before = fuzzer_data.blockchain_wrapper.get_esdt_balance( - price_disc.pd_wrapper.address_ref(), - DISC_ACCEPTED_TOKEN_ID, - 0, - ); - - if caller_token_before < deposit_amount { - println!("Price discovery deposit error: Not enough tokens"); - fuzzer_data.statistics.price_discovery_deposit_misses += 1; - - return; - } - - let tx_result = fuzzer_data.blockchain_wrapper.execute_esdt_transfer( - &caller.address, - &price_disc.pd_wrapper, - payment_token_id, - 0, - &deposit_amount, - |sc| { - let _ = sc.deposit(); - }, - ); - - let caller_token_after = - fuzzer_data - .blockchain_wrapper - .get_esdt_balance(&caller.address, payment_token_id, 0); - - let redeem_token_after = fuzzer_data.blockchain_wrapper.get_esdt_balance( - &caller.address, - redeem_token_id, - redeem_token_nonce, - ); - - let sc_launched_token_after = fuzzer_data.blockchain_wrapper.get_esdt_balance( - price_disc.pd_wrapper.address_ref(), - DISC_LAUNCHED_TOKEN_ID, - 0, - ); - - let sc_accepted_token_after = fuzzer_data.blockchain_wrapper.get_esdt_balance( - price_disc.pd_wrapper.address_ref(), - DISC_ACCEPTED_TOKEN_ID, - 0, - ); - - let tx_result_string = tx_result.result_message; - let tx_result_msg_is_empty = tx_result_string.trim().is_empty(); - - if caller.price_discovery_buy && tx_result_msg_is_empty { - if sc_accepted_token_after != sc_accepted_token_before + &deposit_amount { - println!( - "Price discovery deposit error: sc accepted token final balance is incorrect" - ); - fuzzer_data.statistics.price_discovery_deposit_misses += 1; - return; - } - } else if tx_result_msg_is_empty - && sc_launched_token_after != sc_launched_token_before + &deposit_amount - { - println!("Price discovery deposit error: sc launched token final balance is incorrect"); - fuzzer_data.statistics.price_discovery_deposit_misses += 1; - return; - } - - if !tx_result_msg_is_empty { - println!("Price discovery deposit error: {}", tx_result_string); - fuzzer_data.statistics.price_discovery_deposit_misses += 1; - } else if caller_token_after != caller_token_before - &deposit_amount { - println!("Price discovery deposit error: deposit token final balance is incorrect"); - fuzzer_data.statistics.price_discovery_deposit_misses += 1; - } else if redeem_token_after != redeem_token_before + &deposit_amount { - println!("Price discovery deposit error: redeem token final balance is incorrect"); - fuzzer_data.statistics.price_discovery_deposit_misses += 1; - } else if tx_result_msg_is_empty { - fuzzer_data.statistics.price_discovery_deposit_hits += 1; - } else { - println!("!!! Price discovery withdraw error: undefined case"); - fuzzer_data.statistics.price_discovery_deposit_misses += 1; - } - } - - pub fn price_discovery_withdraw( - fuzzer_data: &mut FuzzerData, - ) where - PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, - FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, - { - let caller_index = fuzzer_data.rng.gen_range(0..fuzzer_data.users.len()); - let caller = &mut fuzzer_data.users[caller_index]; - let price_disc = &mut fuzzer_data.price_disc; - - let redeem_token_id = DISC_REDEEM_TOKEN_ID; - let redeem_token_nonce = if caller.price_discovery_buy { - DISC_ACCEPTED_TOKEN_REDEEM_NONCE - } else { - DISC_LAUNCHED_TOKEN_REDEEM_NONCE - }; - - let seed = fuzzer_data - .rng - .gen_range(0..fuzzer_data.fuzz_args.price_discovery_withdraw_max_value) - + 1; - - let redeem_token_in_amount = rust_biguint!(seed); - - let sc_launched_token_before = fuzzer_data.blockchain_wrapper.get_esdt_balance( - price_disc.pd_wrapper.address_ref(), - DISC_LAUNCHED_TOKEN_ID, - 0, - ); - - let sc_accepted_token_before = fuzzer_data.blockchain_wrapper.get_esdt_balance( - price_disc.pd_wrapper.address_ref(), - DISC_ACCEPTED_TOKEN_ID, - 0, - ); - - let redeem_token_before = fuzzer_data.blockchain_wrapper.get_esdt_balance( - &caller.address, - redeem_token_id, - redeem_token_nonce, - ); - - if redeem_token_before < redeem_token_in_amount { - println!("Price discovery withdraw error: Not enough redeem tokens"); - fuzzer_data.statistics.price_discovery_withdraw_misses += 1; - - return; - } - - let mut withdrawn_amount = rust_biguint!(0); - let tx_result = fuzzer_data.blockchain_wrapper.execute_esdt_transfer( - &caller.address, - &price_disc.pd_wrapper, - redeem_token_id, - redeem_token_nonce, - &redeem_token_in_amount, - |sc| { - let output_payment = sc.withdraw(); - withdrawn_amount = num_bigint::BigUint::from_bytes_be( - output_payment.amount.to_bytes_be().as_slice(), - ); - }, - ); - - let redeem_token_after = fuzzer_data.blockchain_wrapper.get_esdt_balance( - &caller.address, - redeem_token_id, - redeem_token_nonce, - ); - - let sc_launched_token_after = fuzzer_data.blockchain_wrapper.get_esdt_balance( - price_disc.pd_wrapper.address_ref(), - DISC_LAUNCHED_TOKEN_ID, - 0, - ); - - let sc_accepted_token_after = fuzzer_data.blockchain_wrapper.get_esdt_balance( - price_disc.pd_wrapper.address_ref(), - DISC_ACCEPTED_TOKEN_ID, - 0, - ); - - let tx_result_string = tx_result.result_message; - let tx_result_msg_is_empty = tx_result_string.trim().is_empty(); - - if caller.price_discovery_buy && tx_result_msg_is_empty { - if sc_accepted_token_after != sc_accepted_token_before - &withdrawn_amount { - println!( - "Price discovery withdraw error: sc accepted token final balance is incorrect" - ); - fuzzer_data.statistics.price_discovery_withdraw_misses += 1; - return; - } - } else if tx_result_msg_is_empty - && sc_launched_token_after != sc_launched_token_before - &withdrawn_amount - { - println!( - "Price discovery withdraw error: sc launched token final balance is incorrect" - ); - fuzzer_data.statistics.price_discovery_withdraw_misses += 1; - return; - } - - if !tx_result_msg_is_empty { - println!("Price discovery withdraw error: {}", tx_result_string); - fuzzer_data.statistics.price_discovery_withdraw_misses += 1; - } else if redeem_token_after != redeem_token_before - &redeem_token_in_amount { - println!("Price discovery withdraw error: redeem token final balance is incorrect"); - fuzzer_data.statistics.price_discovery_withdraw_misses += 1; - } else if tx_result_msg_is_empty { - fuzzer_data.statistics.price_discovery_withdraw_hits += 1; - } else { - println!("!!! Price discovery withdraw error: undefined case"); - fuzzer_data.statistics.price_discovery_withdraw_misses += 1; - } - } - - pub fn price_discovery_redeem( - fuzzer_data: &mut FuzzerData, - ) where - PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, - FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, - { - let rust_zero = rust_biguint!(0u64); - - let caller_index = fuzzer_data.rng.gen_range(0..fuzzer_data.users.len()); - let caller = &mut fuzzer_data.users[caller_index]; - let price_disc = &mut fuzzer_data.price_disc; - - let redeem_token_id = DISC_REDEEM_TOKEN_ID; - let redeem_token_nonce = if caller.price_discovery_buy { - DISC_ACCEPTED_TOKEN_REDEEM_NONCE - } else { - DISC_LAUNCHED_TOKEN_REDEEM_NONCE - }; - - let seed = fuzzer_data - .rng - .gen_range(0..fuzzer_data.fuzz_args.price_discovery_redeem_max_value) - + 1; - - let mut redeem_token_amount_in = rust_biguint!(seed); - - let redeem_token_before = fuzzer_data.blockchain_wrapper.get_esdt_balance( - &caller.address, - redeem_token_id, - redeem_token_nonce, - ); - - if redeem_token_before < redeem_token_amount_in { - if redeem_token_amount_in > rust_zero { - redeem_token_amount_in = redeem_token_before.clone(); - } else { - println!("Price discovery redeem error: Not enough tokens"); - fuzzer_data.statistics.price_discovery_redeem_misses += 1; - - return; - } - } - - let tx_result = fuzzer_data.blockchain_wrapper.execute_esdt_transfer( - &caller.address, - &price_disc.pd_wrapper, - redeem_token_id, - redeem_token_nonce, - &redeem_token_amount_in, - |sc| { - let _ = sc.redeem(); - }, - ); - - let redeem_token_after = fuzzer_data.blockchain_wrapper.get_esdt_balance( - &caller.address, - redeem_token_id, - redeem_token_nonce, - ); - - let tx_result_string = tx_result.result_message; - let tx_result_msg_is_empty = tx_result_string.trim().is_empty(); - - if !tx_result_msg_is_empty { - println!("Price discovery redeem error: {}", tx_result_string); - fuzzer_data.statistics.price_discovery_redeem_misses += 1; - return; - } - - if redeem_token_after != redeem_token_before - &redeem_token_amount_in { - println!("Price discovery redeem error: redeem token final balance is incorrect"); - fuzzer_data.statistics.price_discovery_redeem_misses += 1; - } else { - fuzzer_data.statistics.price_discovery_redeem_hits += 1; - } - } -} diff --git a/dex/fuzz/src/fuzz_start.rs b/dex/fuzz/src/fuzz_start.rs index a22f7a231..f2c8eda08 100644 --- a/dex/fuzz/src/fuzz_start.rs +++ b/dex/fuzz/src/fuzz_start.rs @@ -10,7 +10,6 @@ mod test { use crate::fuzz_data::fuzz_data_tests::*; use crate::fuzz_farm::fuzz_farm_test::*; use crate::fuzz_pair::fuzz_pair_test::*; - use crate::fuzz_price_discovery::fuzz_price_discovery_test::*; use multiversx_sc_scenario::DebugApi; @@ -26,12 +25,7 @@ mod test { .expect("Incorrect output"); let seed = seed_base.as_secs() * 1000 + seed_base.subsec_nanos() as u64 / 1_000_000; //in ms - let mut fuzzer_data = FuzzerData::new( - seed, - pair::contract_obj, - farm::contract_obj, - price_discovery::contract_obj, - ); + let mut fuzzer_data = FuzzerData::new(seed, pair::contract_obj, farm::contract_obj); println!("Started fuzz testing with seed: {}", (seed)); @@ -43,9 +37,6 @@ mod test { (5, fuzzer_data.fuzz_args.exit_farm_prob), (6, fuzzer_data.fuzz_args.claim_rewards_prob), (7, fuzzer_data.fuzz_args.compound_rewards_prob), - (8, fuzzer_data.fuzz_args.price_discovery_deposit_prob), - (9, fuzzer_data.fuzz_args.price_discovery_withdraw_prob), - (10, fuzzer_data.fuzz_args.price_discovery_redeem_prob), ]; let mut block_epoch = 1; @@ -92,18 +83,6 @@ mod test { println!("Event no. {}: Compound reward", (block_nonce)); compound_rewards(&mut fuzzer_data); } - 8 => { - println!("Event no. {}: Price discovery deposit", (block_nonce)); - price_discovery_deposit(&mut fuzzer_data); - } - 9 => { - println!("Event no. {}: Price discovery withdraw", (block_nonce)); - price_discovery_withdraw(&mut fuzzer_data); - } - 10 => { - println!("Event no. {}: Price discovery redeem", (block_nonce)); - price_discovery_redeem(&mut fuzzer_data); - } _ => println!("No event triggered"), } } @@ -111,13 +90,12 @@ mod test { print_statistics(&mut fuzzer_data, seed); } - fn print_statistics( - fuzzer_data: &mut FuzzerData, + fn print_statistics( + fuzzer_data: &mut FuzzerData, seed: u64, ) where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, FarmObjBuilder: 'static + Copy + Fn() -> farm::ContractObj, - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { println!(); println!("Statistics:"); @@ -201,32 +179,5 @@ mod test { fuzzer_data.statistics.compound_rewards_misses ); println!(); - println!( - "priceDiscoveryDepositHits: {}", - fuzzer_data.statistics.price_discovery_deposit_hits - ); - println!( - "priceDiscoveryDepositMisses: {}", - fuzzer_data.statistics.price_discovery_deposit_misses - ); - println!(); - println!( - "priceDiscoveryWithdrawHits: {}", - fuzzer_data.statistics.price_discovery_withdraw_hits - ); - println!( - "priceDiscoveryWithdrawMisses: {}", - fuzzer_data.statistics.price_discovery_withdraw_misses - ); - println!(); - println!( - "priceDiscoveryRedeemHits: {}", - fuzzer_data.statistics.price_discovery_redeem_hits - ); - println!( - "priceDiscoveryRedeemMisses: {}", - fuzzer_data.statistics.price_discovery_redeem_misses - ); - println!(); } } diff --git a/dex/fuzz/src/lib.rs b/dex/fuzz/src/lib.rs index 06245cbfd..b381e3e61 100644 --- a/dex/fuzz/src/lib.rs +++ b/dex/fuzz/src/lib.rs @@ -1,5 +1,4 @@ mod fuzz_data; mod fuzz_farm; mod fuzz_pair; -mod fuzz_price_discovery; mod fuzz_start; diff --git a/dex/price-discovery/Cargo.toml b/dex/price-discovery/Cargo.toml index dbce3a106..ab5dac4af 100644 --- a/dex/price-discovery/Cargo.toml +++ b/dex/price-discovery/Cargo.toml @@ -18,16 +18,10 @@ features = ["esdt-token-payment-legacy-decode"] [dependencies.multiversx-sc-modules] version = "=0.53.2" -[dependencies.locking_module] -path = "../../common/modules/locking_module" - [dev-dependencies] num-bigint = "0.4.2" num-traits = "0.2" hex = "0.4" -[dev-dependencies.simple-lock] -path = "../../locked-asset/simple-lock" - [dev-dependencies.multiversx-sc-scenario] version = "=0.53.2" diff --git a/dex/price-discovery/README.md b/dex/price-discovery/README.md index 457d12deb..1a2013124 100644 --- a/dex/price-discovery/README.md +++ b/dex/price-discovery/README.md @@ -12,19 +12,8 @@ Once that period has ended, users will be able to redeem the opposite token of w ## Phases -Over the start-end period, we define multiple phases, in which interactions with the Price Discovery SC will impose some restrictions: +Over the start-end period, we define multiple phases: -1) No restrictions. Anyone can deposit/withdraw any amount -2) Deposits are unrestricted, withdrawals come with a linear increasing penalty -3) Deposits are not allowed, withdrawals come with a fixed penalty -4) Neither deposits nor withdrawals are allowed. - -During phase 4, also known as the _redeem_ phase, the users will be able to redeem tokens based on the current ratio of tokens in the contract. Essentially, users are "buying" the opposite token. Note that during the first few epochs the price discovery ends, the users will receive a locked token instead of the actual token. - -Accumulated penalties during phase 2 and 3 will be automatically redeemed by users when claiming. So users will actually buy at a better price than the current ratio if there are a lot of tokens accumulated from penalties. - -## Minimum price - -The minimum price for the launched tokens can be set by the owner and can be set in the init period of the contract, before entering phase 1. The first deposit has to be with the _launched_ token, otherwise minPrice invariant is not sustained. - -The minPrice check is done at the END of every deposit and withdraw function independent on the phase (1, 2, 3). This has a set of implications: if the price gets too low, users will not be able to withdraw their accepted token. The same goes into deposit as well - users will not be able to deposit launched tokens if there is not enough liquidity of accepted tokens (which means price is too low). +1) Anyone can deposit/withdraw any amount of the accepted token +2) Owner can deposit/withdraw the launched token, but not below _min_launched_tokens_ +3) Users can redeem the launched token, while the owner can redeem the accepted token diff --git a/dex/price-discovery/docs/setup.md b/dex/price-discovery/docs/setup.md index d385467f2..6ccf073fe 100644 --- a/dex/price-discovery/docs/setup.md +++ b/dex/price-discovery/docs/setup.md @@ -4,45 +4,32 @@ The contract defines two tokens: - launched_token_id - the token identifier of the newly launched token on the XExchange. - accepted_token_id - an already established token, that will be used to determine the price of the launched token -Additionally, a min price is also defined by the following arguments: +The contract also needs the decimals to correctly calculate the price - launched_token_decimals - the number of decimals for the launched token. Most tokens have 18 decimals. -- a min_launched_token_price is the minimum price of the launched token, in accepted tokens. This value has to be denominated according to the ACCEPTED token decimals. For example, if we have launched token with 18 decimals, and accepted token with 6 decimals, and we want a 1:1 ratio, the min_price argument should be 1_000_000. -Next we define the length of the phases. Over the start-end period, we define multiple phases, -in which interactions with the Price Discovery SC will impose some restrictions: - 1) No restrictions. Anyone can deposit/withdraw any amount - 2) Deposits are unrestricted, withdrawals come with a linear increasing penalty - 3) Deposits are not allowed, withdrawals come with a fixed penalty - 4) Neither deposits nor withdrawals are allowed. This is when the LP is created. +Next we define the length of the phases. Over the start-end period, we define multiple phases: +1) Anyone can deposit/withdraw any amount of the accepted token +2) Owner can deposit/withdraw the launched token, but not below _min_launched_tokens_ +3) Users can redeem the launched token, while the owner can redeem the accepted token -- start_block - phase 1 start block -- for no_limit_phase_duration_blocks - phase 1 duration -- linear_penalty_phase_duration_blocks - phase 2 duration -- fixed_penalty_phase_duration_blocks - phase 3 duration -- unlock_epoch - the unlock epoch for the redeemed tokens. Users will receive locked tokens and can unlock them at the specified epoch through an external contract -- penalty_min_percentage, penalty_max_percentage - the minimum and maximum percentage for Phase 2). - The percentage increases linearly between phase 2's start and end -- fixed_penalty_percentage - The penalty percentage for phase 3. - -- locking_sc_address - additionally, as suggested by the `unlock_epoch` argument, we need the address of the locking SC. This contract's source code can be found in the `locked_asset/simple-lock` folder. +- min_launched_tokens - minimum number of launched tokens the owner must deposit +- start_time - phase 1 timestamp start +- user_deposit_withdraw_time - phase 1 duration +- owner_deposit_withdraw_time - phase 2 duration +- owner_address - address of the owner, i.e. the one who will deposit/withdraw the launched tokens. Doesn't have to be the contract's owner. ```rust #[init] fn init( &self, launched_token_id: TokenIdentifier, - accepted_token_id: TokenIdentifier, + accepted_token_id: EgldOrEsdtTokenIdentifier, launched_token_decimals: u32, - min_launched_token_price: BigUint, - start_block: u64, - no_limit_phase_duration_blocks: u64, - linear_penalty_phase_duration_blocks: u64, - fixed_penalty_phase_duration_blocks: u64, - unlock_epoch: u64, - penalty_min_percentage: BigUint, - penalty_max_percentage: BigUint, - fixed_penalty_percentage: BigUint, - locking_sc_address: ManagedAddress, + min_launched_tokens: BigUint, + start_time: Timestamp, + user_deposit_withdraw_time: Timestamp, + owner_deposit_withdraw_time: Timestamp, + owner_address: ManagedAddress, ) ``` @@ -59,8 +46,4 @@ fn issue_redeem_token( ) ``` -The redeem token is a meta ESDT token that the users receive on deposits. Those can then be used to withdraw the initial tokens (or part of them, as per phase restrictions). We only use two nonces: -- nonce 1 for launched tokens -- nonce 2 for accepted tokens - -In the issue callback, one of each of those tokens is created, so that the SC can afterwards use NFTAddQuantity. These tokens have no additional attributes. +The redeem token is a an ESDT token that the users receive on deposits. Those can then be used to withdraw the initial tokens (or part of them, as per phase restrictions). diff --git a/dex/price-discovery/src/common_storage.rs b/dex/price-discovery/src/common_storage.rs index 7ee5ee083..3279f5ccc 100644 --- a/dex/price-discovery/src/common_storage.rs +++ b/dex/price-discovery/src/common_storage.rs @@ -1,30 +1,32 @@ +use crate::Timestamp; + multiversx_sc::imports!(); pub const MAX_PERCENTAGE: u64 = 10_000_000_000_000; // 100% #[multiversx_sc::module] pub trait CommonStorageModule { - #[view(getLaunchedTokenId)] #[storage_mapper("launchedTokenId")] fn launched_token_id(&self) -> SingleValueMapper; - #[view(getAcceptedTokenId)] - #[storage_mapper("acceptedTokenId")] - fn accepted_token_id(&self) -> SingleValueMapper; - - #[view(getLaunchedTokenBalance)] #[storage_mapper("launchedTokenBalance")] fn launched_token_balance(&self) -> SingleValueMapper; - #[view(getAcceptedTokenBalance)] + #[storage_mapper("minLaunchedTokens")] + fn min_launched_tokens(&self) -> SingleValueMapper; + + #[storage_mapper("acceptedTokenId")] + fn accepted_token_id(&self) -> SingleValueMapper; + #[storage_mapper("acceptedTokenBalance")] fn accepted_token_balance(&self) -> SingleValueMapper; - #[view(getStartBlock)] - #[storage_mapper("startBlock")] - fn start_block(&self) -> SingleValueMapper; + #[storage_mapper("startTime")] + fn start_time(&self) -> SingleValueMapper; + + #[storage_mapper("pricePrecision")] + fn price_precision(&self) -> SingleValueMapper; - #[view(getEndBlock)] - #[storage_mapper("endBlock")] - fn end_block(&self) -> SingleValueMapper; + #[storage_mapper("ownerAddress")] + fn owner_address(&self) -> SingleValueMapper; } diff --git a/dex/price-discovery/src/events.rs b/dex/price-discovery/src/events.rs index 7bcd78ddd..aa7c65536 100644 --- a/dex/price-discovery/src/events.rs +++ b/dex/price-discovery/src/events.rs @@ -1,174 +1,215 @@ -use crate::phase::Phase; +use crate::{Block, Epoch, Timestamp}; multiversx_sc::imports!(); multiversx_sc::derive_imports!(); #[derive(TypeAbi, TopEncode)] -pub struct DepositEvent { - token_id_in: EgldOrEsdtTokenIdentifier, - token_amount_in: BigUint, - redeem_token_id: TokenIdentifier, - redeem_token_nonce: u64, - redeem_token_amount: BigUint, - launched_token_amount: BigUint, - accepted_token_amount: BigUint, - current_price: BigUint, - current_phase: Phase, +pub struct UserDepositEvent<'a, M: ManagedTypeApi> { + token_amount_in: &'a BigUint, + redeem_token_id: &'a TokenIdentifier, + redeem_token_amount: &'a BigUint, + accepted_token_amount: &'a BigUint, } #[derive(TypeAbi, TopEncode)] -pub struct WithdrawEvent { - token_id_out: EgldOrEsdtTokenIdentifier, - token_amount_out: BigUint, - redeem_token_id: TokenIdentifier, - redeem_token_nonce: u64, - redeem_token_amount: BigUint, - launched_token_amount: BigUint, - accepted_token_amount: BigUint, - current_price: BigUint, - current_phase: Phase, +pub struct UserWithdrawEvent<'a, M: ManagedTypeApi> { + token_amount_out: &'a BigUint, + redeem_token_id: &'a TokenIdentifier, + redeem_token_amount: &'a BigUint, + accepted_token_amount: &'a BigUint, } #[derive(TypeAbi, TopEncode)] -pub struct RedeemEvent { - redeem_token_id: TokenIdentifier, - redeem_token_nonce: u64, - redeem_token_amount: BigUint, - bought_token_id: EgldOrEsdtTokenIdentifier, - bought_token_amount: BigUint, +pub struct OwnerDepositEvent<'a, M: ManagedTypeApi> { + token_amount_in: &'a BigUint, + launched_token_amount: &'a BigUint, +} + +#[derive(TypeAbi, TopEncode)] +pub struct OwnerWithdrawEvent<'a, M: ManagedTypeApi> { + token_amount_out: &'a BigUint, + launched_token_amount: &'a BigUint, +} + +#[derive(TypeAbi, TopEncode)] +pub struct RedeemEvent<'a, M: ManagedTypeApi> { + opt_redeem_token_id: Option<&'a TokenIdentifier>, + redeem_token_amount: &'a BigUint, + bought_token_id: &'a EgldOrEsdtTokenIdentifier, + bought_token_amount: &'a BigUint, +} + +pub struct GenericEventData { + caller: ManagedAddress, + block: Block, + epoch: Epoch, + timestamp: Timestamp, } #[multiversx_sc::module] pub trait EventsModule: crate::common_storage::CommonStorageModule { - fn emit_deposit_event( + fn emit_user_deposit_event( &self, - token_id_in: EgldOrEsdtTokenIdentifier, - token_amount_in: BigUint, - redeem_token_id: TokenIdentifier, - redeem_token_nonce: u64, - redeem_token_amount: BigUint, - current_price: BigUint, - current_phase: Phase, + token_amount_in: &BigUint, + redeem_token_id: &TokenIdentifier, + redeem_token_amount: &BigUint, ) { - let caller = self.blockchain().get_caller(); - let block = self.blockchain().get_block_nonce(); - let epoch = self.blockchain().get_block_epoch(); - let timestamp = self.blockchain().get_block_timestamp(); - - let launched_token_amount = self.launched_token_balance().get(); + let generic_event_data = self.get_generic_event_data(); let accepted_token_amount = self.accepted_token_balance().get(); - self.deposit_event( - &caller, - block, - epoch, - timestamp, - &DepositEvent { - token_id_in, + self.user_deposit_event( + &generic_event_data.caller, + generic_event_data.block, + generic_event_data.epoch, + generic_event_data.timestamp, + UserDepositEvent { token_amount_in, redeem_token_id, - redeem_token_nonce, redeem_token_amount, - launched_token_amount, - accepted_token_amount, - current_price, - current_phase, + accepted_token_amount: &accepted_token_amount, }, ); } - fn emit_withdraw_event( + fn emit_user_withdraw_event( &self, - token_id_out: EgldOrEsdtTokenIdentifier, - token_amount_out: BigUint, - redeem_token_id: TokenIdentifier, - redeem_token_nonce: u64, - redeem_token_amount: BigUint, - current_price: BigUint, - current_phase: Phase, + token_amount_out: &BigUint, + redeem_token_id: &TokenIdentifier, + redeem_token_amount: &BigUint, ) { - let caller = self.blockchain().get_caller(); - let block = self.blockchain().get_block_nonce(); - let epoch = self.blockchain().get_block_epoch(); - let timestamp = self.blockchain().get_block_timestamp(); - - let launched_token_amount = self.launched_token_balance().get(); + let generic_event_data = self.get_generic_event_data(); let accepted_token_amount = self.accepted_token_balance().get(); - self.withdraw_event( - &caller, - block, - epoch, - timestamp, - &WithdrawEvent { - token_id_out, + self.user_withdraw_event( + &generic_event_data.caller, + generic_event_data.block, + generic_event_data.epoch, + generic_event_data.timestamp, + UserWithdrawEvent { token_amount_out, redeem_token_id, - redeem_token_nonce, redeem_token_amount, - launched_token_amount, - accepted_token_amount, - current_price, - current_phase, + accepted_token_amount: &accepted_token_amount, + }, + ); + } + + fn emit_owner_deposit_event(&self, token_amount_in: &BigUint) { + let generic_event_data = self.get_generic_event_data(); + let launched_token_amount = self.launched_token_balance().get(); + + self.owner_deposit_event( + &generic_event_data.caller, + generic_event_data.block, + generic_event_data.epoch, + generic_event_data.timestamp, + OwnerDepositEvent { + token_amount_in, + launched_token_amount: &launched_token_amount, + }, + ); + } + + fn emit_owner_withdraw_event(&self, token_amount_out: &BigUint) { + let generic_event_data = self.get_generic_event_data(); + let launched_token_amount = self.launched_token_balance().get(); + + self.owner_withdraw_event( + &generic_event_data.caller, + generic_event_data.block, + generic_event_data.epoch, + generic_event_data.timestamp, + OwnerWithdrawEvent { + token_amount_out, + launched_token_amount: &launched_token_amount, }, ); } fn emit_redeem_event( &self, - redeem_token_id: TokenIdentifier, - redeem_token_nonce: u64, - redeem_token_amount: BigUint, - bought_token_id: EgldOrEsdtTokenIdentifier, - bought_token_amount: BigUint, + opt_redeem_token_id: Option<&TokenIdentifier>, + redeem_token_amount: &BigUint, + bought_token_id: &EgldOrEsdtTokenIdentifier, + bought_token_amount: &BigUint, ) { + let generic_event_data = self.get_generic_event_data(); + + self.redeem_event( + &generic_event_data.caller, + generic_event_data.block, + generic_event_data.epoch, + generic_event_data.timestamp, + RedeemEvent { + opt_redeem_token_id, + redeem_token_amount, + bought_token_id, + bought_token_amount, + }, + ) + } + + fn get_generic_event_data(&self) -> GenericEventData { let caller = self.blockchain().get_caller(); let block = self.blockchain().get_block_nonce(); let epoch = self.blockchain().get_block_epoch(); let timestamp = self.blockchain().get_block_timestamp(); - self.redeem_event( - &caller, + GenericEventData { + caller, block, epoch, timestamp, - &RedeemEvent { - redeem_token_id, - redeem_token_nonce, - redeem_token_amount, - bought_token_id, - bought_token_amount, - }, - ) + } } - #[event("depositEvent")] - fn deposit_event( + #[event("userDepositEvent")] + fn user_deposit_event( + &self, + #[indexed] caller: &ManagedAddress, + #[indexed] block: Block, + #[indexed] epoch: Epoch, + #[indexed] timestamp: Timestamp, + deposit_event: UserDepositEvent, + ); + + #[event("userWithdrawEvent")] + fn user_withdraw_event( + &self, + #[indexed] caller: &ManagedAddress, + #[indexed] block: Block, + #[indexed] epoch: Epoch, + #[indexed] timestamp: Timestamp, + withdraw_event: UserWithdrawEvent, + ); + + #[event("ownerDepositEvent")] + fn owner_deposit_event( &self, #[indexed] caller: &ManagedAddress, - #[indexed] block: u64, - #[indexed] epoch: u64, - #[indexed] timestamp: u64, - deposit_event: &DepositEvent, + #[indexed] block: Block, + #[indexed] epoch: Epoch, + #[indexed] timestamp: Timestamp, + deposit_event: OwnerDepositEvent, ); - #[event("withdrawEvent")] - fn withdraw_event( + #[event("ownerWithdrawEvent")] + fn owner_withdraw_event( &self, #[indexed] caller: &ManagedAddress, - #[indexed] block: u64, - #[indexed] epoch: u64, - #[indexed] timestamp: u64, - withdraw_event: &WithdrawEvent, + #[indexed] block: Block, + #[indexed] epoch: Epoch, + #[indexed] timestamp: Timestamp, + withdraw_event: OwnerWithdrawEvent, ); #[event("redeemEvent")] fn redeem_event( &self, #[indexed] caller: &ManagedAddress, - #[indexed] block: u64, - #[indexed] epoch: u64, - #[indexed] timestamp: u64, - redeem_event: &RedeemEvent, + #[indexed] block: Block, + #[indexed] epoch: Epoch, + #[indexed] timestamp: Timestamp, + redeem_event: RedeemEvent, ); } diff --git a/dex/price-discovery/src/lib.rs b/dex/price-discovery/src/lib.rs index b0c430e52..94534492d 100644 --- a/dex/price-discovery/src/lib.rs +++ b/dex/price-discovery/src/lib.rs @@ -2,27 +2,30 @@ multiversx_sc::imports!(); -use crate::{ - common_storage::MAX_PERCENTAGE, - redeem_token::{ACCEPTED_TOKEN_REDEEM_NONCE, LAUNCHED_TOKEN_REDEEM_NONCE}, -}; - pub mod common_storage; pub mod events; pub mod phase; pub mod redeem_token; +pub mod user_actions; +pub mod views; + +pub type Nonce = u64; +pub type Block = u64; +pub type Epoch = u64; +pub type Timestamp = u64; -static INVALID_PAYMENT_ERR_MSG: &[u8] = b"Invalid payment token"; -static BELOW_MIN_PRICE_ERR_MSG: &[u8] = b"Launched token below min price"; const MAX_TOKEN_DECIMALS: u32 = 18; #[multiversx_sc::contract] pub trait PriceDiscovery: common_storage::CommonStorageModule + events::EventsModule - + locking_module::locking_module::LockingModule + phase::PhaseModule + redeem_token::RedeemTokenModule + + user_actions::user_deposit_withdraw::UserDepositWithdrawModule + + user_actions::owner_deposit_withdraw::OwnerDepositWithdrawModule + + user_actions::redeem::RedeemModule + + views::ViewsModule + multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule { /// For explanations regarding what each parameter means, please refer to docs/setup.md @@ -32,16 +35,11 @@ pub trait PriceDiscovery: launched_token_id: TokenIdentifier, accepted_token_id: EgldOrEsdtTokenIdentifier, launched_token_decimals: u32, - min_launched_token_price: BigUint, - start_block: u64, - no_limit_phase_duration_blocks: u64, - linear_penalty_phase_duration_blocks: u64, - fixed_penalty_phase_duration_blocks: u64, - unlock_epoch: u64, - penalty_min_percentage: BigUint, - penalty_max_percentage: BigUint, - fixed_penalty_percentage: BigUint, - locking_sc_address: ManagedAddress, + min_launched_tokens: BigUint, + start_time: Timestamp, + user_deposit_withdraw_time: Timestamp, + owner_deposit_withdraw_time: Timestamp, + owner_address: ManagedAddress, ) { require!( launched_token_id.is_valid_esdt_identifier(), @@ -52,263 +50,37 @@ pub trait PriceDiscovery: accepted_token_id != launched_token_id, "Launched and accepted token must be different" ); - require!( launched_token_decimals <= MAX_TOKEN_DECIMALS, "Launched token has too many decimals" ); - let current_block = self.blockchain().get_block_nonce(); - require!( - current_block < start_block, - "Start block cannot be in the past" - ); - - let end_block = start_block - + no_limit_phase_duration_blocks - + linear_penalty_phase_duration_blocks - + fixed_penalty_phase_duration_blocks; - + let current_time = self.blockchain().get_block_timestamp(); require!( - penalty_min_percentage <= penalty_max_percentage, - "Min percentage higher than max percentage" + current_time < start_time, + "Start time cannot be in the past" ); require!( - penalty_max_percentage < MAX_PERCENTAGE, - "Max percentage higher than 100%" - ); - require!( - fixed_penalty_percentage < MAX_PERCENTAGE, - "Fixed percentage higher than 100%" + user_deposit_withdraw_time > 0 && owner_deposit_withdraw_time > 0, + "Invalid timestamps" ); - let current_epoch = self.blockchain().get_block_epoch(); - require!( - unlock_epoch > current_epoch, - "Unlock epoch cannot be in the past" - ); + require!(min_launched_tokens > 0, "Invalid min launched tokens"); - self.launched_token_id().set(&launched_token_id); - self.accepted_token_id().set(&accepted_token_id); - self.start_block().set(start_block); - self.end_block().set(end_block); - self.unlock_epoch().set(unlock_epoch); + self.launched_token_id().set(launched_token_id); + self.accepted_token_id().set(accepted_token_id); + self.start_time().set(start_time); + self.user_deposit_withdraw_time() + .set(owner_deposit_withdraw_time); + self.owner_deposit_withdraw_time() + .set(owner_deposit_withdraw_time); + self.owner_address().set(owner_address); let price_precision = 10u64.pow(launched_token_decimals); self.price_precision().set(price_precision); - self.min_launched_token_price() - .set(&min_launched_token_price); - - self.no_limit_phase_duration_blocks() - .set(no_limit_phase_duration_blocks); - self.linear_penalty_phase_duration_blocks() - .set(linear_penalty_phase_duration_blocks); - self.fixed_penalty_phase_duration_blocks() - .set(fixed_penalty_phase_duration_blocks); - self.penalty_min_percentage().set(&penalty_min_percentage); - self.penalty_max_percentage().set(&penalty_max_percentage); - self.fixed_penalty_percentage() - .set(&fixed_penalty_percentage); - - self.set_locking_sc_address(locking_sc_address); + self.min_launched_tokens().set(min_launched_tokens); } #[upgrade] fn upgrade(&self) {} - - /// Users can deposit either launched_token or accepted_token. - /// They will receive an SFT that can be used to withdraw said tokens - #[payable("*")] - #[endpoint] - fn deposit(&self) -> EsdtTokenPayment { - let phase = self.get_current_phase(); - self.require_deposit_allowed(&phase); - - let (payment_token, payment_amount) = self.call_value().egld_or_single_fungible_esdt(); - let accepted_token_id = self.accepted_token_id().get(); - let launched_token_id = self.launched_token_id().get(); - let (redeem_token_nonce, balance_mapper) = if payment_token == accepted_token_id { - (ACCEPTED_TOKEN_REDEEM_NONCE, self.accepted_token_balance()) - } else if payment_token == launched_token_id { - (LAUNCHED_TOKEN_REDEEM_NONCE, self.launched_token_balance()) - } else { - sc_panic!(INVALID_PAYMENT_ERR_MSG); - }; - - self.increase_balance(balance_mapper, &payment_amount); - - let current_price = self.calculate_price(); - let min_price = self.min_launched_token_price().get(); - require!( - current_price == 0 || current_price >= min_price || payment_token == accepted_token_id, - BELOW_MIN_PRICE_ERR_MSG - ); - - let caller = self.blockchain().get_caller(); - let payment_result = - self.mint_and_send_redeem_token(&caller, redeem_token_nonce, payment_amount.clone()); - - self.emit_deposit_event( - payment_token, - payment_amount.clone(), - payment_result.token_identifier.clone(), - redeem_token_nonce, - payment_amount, - current_price, - phase, - ); - - payment_result - } - - /// Deposit SFTs received after deposit to withdraw the initially deposited tokens. - /// Depending on the current Phase, a penalty may be applied and only a part - /// of the initial tokens will be received. - #[payable("*")] - #[endpoint] - fn withdraw(&self) -> EgldOrEsdtTokenPayment { - let phase = self.get_current_phase(); - self.require_withdraw_allowed(&phase); - - let (payment_token, payment_nonce, payment_amount) = - self.call_value().single_esdt().into_tuple(); - let redeem_token_id = self.redeem_token().get_token_id(); - require!(payment_token == redeem_token_id, INVALID_PAYMENT_ERR_MSG); - - let (refund_token_id, balance_mapper) = match payment_nonce { - LAUNCHED_TOKEN_REDEEM_NONCE => ( - EgldOrEsdtTokenIdentifier::esdt(self.launched_token_id().get()), - self.launched_token_balance(), - ), - ACCEPTED_TOKEN_REDEEM_NONCE => ( - self.accepted_token_id().get(), - self.accepted_token_balance(), - ), - _ => sc_panic!(INVALID_PAYMENT_ERR_MSG), - }; - - self.burn_redeem_token(payment_nonce, &payment_amount); - - let penalty_percentage = phase.get_penalty_percentage(); - let penalty_amount = &payment_amount * &penalty_percentage / MAX_PERCENTAGE; - let withdraw_amount = &payment_amount - &penalty_amount; - - self.decrease_balance(balance_mapper, &withdraw_amount); - - let current_price = self.calculate_price(); - let min_price = self.min_launched_token_price().get(); - require!(current_price >= min_price, BELOW_MIN_PRICE_ERR_MSG); - - let caller = self.blockchain().get_caller(); - self.send() - .direct(&caller, &refund_token_id, 0, &withdraw_amount); - - self.emit_withdraw_event( - refund_token_id.clone(), - withdraw_amount.clone(), - payment_token, - payment_nonce, - payment_amount, - current_price, - phase, - ); - - EgldOrEsdtTokenPayment::new(refund_token_id, 0, withdraw_amount) - } - - /// After all phases have ended, - /// users can withdraw their fair share of either accepted or launched tokens, - /// depending on which token they deposited initially. - /// Users that deposited accepted tokens will receive Locked launched tokens. - /// Users that deposited launched tokens will receive Locked accepted tokens. - /// The users can unlock said tokens at the configured unlock_epoch, - /// through the SC at locking_sc_address - #[payable("*")] - #[endpoint] - fn redeem(&self) -> EgldOrEsdtTokenPayment { - let phase = self.get_current_phase(); - self.require_redeem_allowed(&phase); - - let (payment_token, payment_nonce, payment_amount) = - self.call_value().single_esdt().into_tuple(); - let redeem_token_id = self.redeem_token().get_token_id(); - require!(payment_token == redeem_token_id, INVALID_PAYMENT_ERR_MSG); - - let bought_tokens = self.compute_bought_tokens(payment_nonce, &payment_amount); - self.burn_redeem_token_without_supply_decrease(payment_nonce, &payment_amount); - - if bought_tokens.amount > 0 { - let caller = self.blockchain().get_caller(); - let _ = self.lock_tokens_and_forward( - caller, - bought_tokens.token_identifier.clone(), - bought_tokens.amount.clone(), - ); - } - - self.emit_redeem_event( - payment_token, - payment_nonce, - payment_amount, - bought_tokens.token_identifier.clone(), - bought_tokens.amount.clone(), - ); - - bought_tokens - } - - // private - - fn compute_bought_tokens( - &self, - redeem_token_nonce: u64, - redeem_token_amount: &BigUint, - ) -> EgldOrEsdtTokenPayment { - let redeem_token_supply = self - .redeem_token_total_circulating_supply(redeem_token_nonce) - .get(); - - // users that deposited accepted tokens get launched tokens, and vice-versa - let (token_id, total_token_supply) = match redeem_token_nonce { - ACCEPTED_TOKEN_REDEEM_NONCE => ( - EgldOrEsdtTokenIdentifier::esdt(self.launched_token_id().get()), - self.launched_token_balance().get(), - ), - LAUNCHED_TOKEN_REDEEM_NONCE => ( - self.accepted_token_id().get(), - self.accepted_token_balance().get(), - ), - _ => sc_panic!(INVALID_PAYMENT_ERR_MSG), - }; - let reward_amount = total_token_supply * redeem_token_amount / redeem_token_supply; - - EgldOrEsdtTokenPayment::new(token_id, 0, reward_amount) - } - - #[view(getCurrentPrice)] - fn calculate_price(&self) -> BigUint { - let launched_token_balance = self.launched_token_balance().get(); - let accepted_token_balance = self.accepted_token_balance().get(); - - require!(launched_token_balance > 0, "No launched tokens available"); - - let price_precision = self.price_precision().get(); - accepted_token_balance * price_precision / launched_token_balance - } - - fn increase_balance(&self, mapper: SingleValueMapper, amount: &BigUint) { - mapper.update(|b| *b += amount); - } - - fn decrease_balance(&self, mapper: SingleValueMapper, amount: &BigUint) { - mapper.update(|b| *b -= amount); - } - - #[view(getMinLaunchedTokenPrice)] - #[storage_mapper("minLaunchedTokenPrice")] - fn min_launched_token_price(&self) -> SingleValueMapper; - - #[view(getPricePrecision)] - #[storage_mapper("pricePrecision")] - fn price_precision(&self) -> SingleValueMapper; } diff --git a/dex/price-discovery/src/phase.rs b/dex/price-discovery/src/phase.rs index 733008631..d127af952 100644 --- a/dex/price-discovery/src/phase.rs +++ b/dex/price-discovery/src/phase.rs @@ -1,126 +1,69 @@ +use crate::Timestamp; + multiversx_sc::imports!(); multiversx_sc::derive_imports!(); -#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, PartialEq)] -pub enum Phase { +#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode)] +pub enum Phase { Idle, - NoPenalty, - LinearIncreasingPenalty { penalty_percentage: BigUint }, - OnlyWithdrawFixedPenalty { penalty_percentage: BigUint }, + UserDepositWithdraw, + OwnerDepositWithdraw, Redeem, } -impl Phase { - pub fn get_penalty_percentage(&self) -> BigUint { - match self { - Self::LinearIncreasingPenalty { penalty_percentage } => penalty_percentage.clone(), - Self::OnlyWithdrawFixedPenalty { penalty_percentage } => penalty_percentage.clone(), - _ => BigUint::zero(), - } - } -} - #[multiversx_sc::module] pub trait PhaseModule: crate::common_storage::CommonStorageModule + crate::events::EventsModule { #[view(getCurrentPhase)] - fn get_current_phase(&self) -> Phase { - let current_block = self.blockchain().get_block_nonce(); - let start_block = self.start_block().get(); - if current_block < start_block { + fn get_current_phase(&self) -> Phase { + let current_time = self.blockchain().get_block_timestamp(); + let start_time = self.start_time().get(); + if current_time < start_time { return Phase::Idle; } - let no_limit_phase_duration_blocks = self.no_limit_phase_duration_blocks().get(); - let no_limit_phase_end = start_block + no_limit_phase_duration_blocks; - if current_block < no_limit_phase_end { - return Phase::NoPenalty; + let user_deposit_time = self.user_deposit_withdraw_time().get(); + let user_deposit_phase_end = start_time + user_deposit_time; + if current_time < user_deposit_phase_end { + return Phase::UserDepositWithdraw; } - let linear_penalty_phase_duration_blocks = - self.linear_penalty_phase_duration_blocks().get(); - let linear_penalty_phase_start = no_limit_phase_end; - let linear_penalty_phase_end = - linear_penalty_phase_start + linear_penalty_phase_duration_blocks; - if current_block < linear_penalty_phase_end { - let blocks_passed_in_penalty_phase = current_block - linear_penalty_phase_start; - let min_percentage = self.penalty_min_percentage().get(); - let max_percentage = self.penalty_max_percentage().get(); - let percentage_diff = &max_percentage - &min_percentage; - - let penalty_percentage_increase = if linear_penalty_phase_duration_blocks > 1 { - percentage_diff * blocks_passed_in_penalty_phase - / (linear_penalty_phase_duration_blocks - 1) - } else { - BigUint::zero() - }; - - return Phase::LinearIncreasingPenalty { - penalty_percentage: min_percentage + penalty_percentage_increase, - }; - } - - let fixed_penalty_phase_duration_blocks = self.fixed_penalty_phase_duration_blocks().get(); - let fixed_penalty_phase_start = linear_penalty_phase_end; - let fixed_penalty_phase_end = - fixed_penalty_phase_start + fixed_penalty_phase_duration_blocks; - if current_block < fixed_penalty_phase_end { - return Phase::OnlyWithdrawFixedPenalty { - penalty_percentage: self.fixed_penalty_percentage().get(), - }; + let owner_deposit_time = self.owner_deposit_withdraw_time().get(); + let owner_deposit_phase_end = user_deposit_phase_end + owner_deposit_time; + if current_time < owner_deposit_phase_end { + return Phase::OwnerDepositWithdraw; } Phase::Redeem } - fn require_deposit_allowed(&self, phase: &Phase) { - match phase { - Phase::Idle - | Phase::OnlyWithdrawFixedPenalty { - penalty_percentage: _, - } - | Phase::Redeem => { - sc_panic!("Deposit not allowed in this phase") - } - _ => {} - }; + fn require_user_deposit_withdraw_allowed(&self, phase: &Phase) { + require!( + matches!(phase, Phase::UserDepositWithdraw), + "User deposit/withdraw not allowed in this phase" + ); } - fn require_withdraw_allowed(&self, phase: &Phase) { - match phase { - Phase::Idle | Phase::Redeem => { - sc_panic!("Withdraw not allowed in this phase") - } - _ => {} - }; + fn require_owner_deposit_withdraw_allowed(&self, phase: &Phase) { + require!( + matches!(phase, Phase::OwnerDepositWithdraw), + "Owner deposit/withdraw not allowed in this phase" + ); } - fn require_redeem_allowed(&self, phase: &Phase) { - require!(phase == &Phase::Redeem, "Redeem not allowed in this phase"); + fn require_redeem_allowed(&self, phase: &Phase) { + require!( + matches!(phase, Phase::Redeem), + "Redeem not allowed in this phase" + ); } - #[view(getNoLimitPhaseDurationBlocks)] - #[storage_mapper("noLimitPhaseDurationBlocks")] - fn no_limit_phase_duration_blocks(&self) -> SingleValueMapper; - - #[view(getLinearPenaltyPhaseDurationBlocks)] - #[storage_mapper("linearPenaltyPhaseDurationBlocks")] - fn linear_penalty_phase_duration_blocks(&self) -> SingleValueMapper; - - #[view(getFixedPenaltyPhaseDurationBlocks)] - #[storage_mapper("fixedPenaltyPhaseDurationBlocks")] - fn fixed_penalty_phase_duration_blocks(&self) -> SingleValueMapper; - - #[view(getPenaltyMinPercentage)] - #[storage_mapper("penaltyMinPercentage")] - fn penalty_min_percentage(&self) -> SingleValueMapper; - - #[view(getPenaltyMaxPercentage)] - #[storage_mapper("penaltyMaxPercentage")] - fn penalty_max_percentage(&self) -> SingleValueMapper; + #[view(getUserDepositWithdrawTime)] + #[storage_mapper("userDepositWithdrawTime")] + fn user_deposit_withdraw_time(&self) -> SingleValueMapper; - #[view(getFixedPenaltyPercentage)] - #[storage_mapper("fixedPenaltyPercentage")] - fn fixed_penalty_percentage(&self) -> SingleValueMapper; + #[view(getOwnerDepositWithdrawTime)] + #[storage_mapper("ownerDepositWithdrawTime")] + fn owner_deposit_withdraw_time(&self) -> SingleValueMapper; } diff --git a/dex/price-discovery/src/redeem_token.rs b/dex/price-discovery/src/redeem_token.rs index e54b091d8..300ee3312 100644 --- a/dex/price-discovery/src/redeem_token.rs +++ b/dex/price-discovery/src/redeem_token.rs @@ -1,10 +1,5 @@ -use multiversx_sc::codec::Empty; - multiversx_sc::imports!(); -pub const LAUNCHED_TOKEN_REDEEM_NONCE: u64 = 1; -pub const ACCEPTED_TOKEN_REDEEM_NONCE: u64 = 2; - #[multiversx_sc::module] pub trait RedeemTokenModule: crate::common_storage::CommonStorageModule @@ -21,7 +16,6 @@ pub trait RedeemTokenModule: ) { let payment_amount = self.call_value().egld_value().clone_value(); self.redeem_token().issue_and_set_all_roles( - EsdtTokenType::Meta, payment_amount, token_name, token_ticker, @@ -30,56 +24,30 @@ pub trait RedeemTokenModule: ); } - #[only_owner] - #[endpoint(createInitialRedeemTokens)] - fn create_initial_redeem_tokens(&self) { - require!(!self.redeem_token().is_empty(), "Token not issued"); - - // create SFT for both types so NFTAddQuantity works - let launched_token_id = self.launched_token_id().get(); - let accepted_token_id = self.accepted_token_id().get(); - let one = BigUint::from(1u32); - - let token_mapper = self.redeem_token(); - let _ = token_mapper.nft_create_named( - one.clone(), - launched_token_id.as_managed_buffer(), - &Empty, - ); - let _ = token_mapper.nft_create_named(one, &accepted_token_id.into_name(), &Empty); - } - - fn mint_and_send_redeem_token( - &self, - to: &ManagedAddress, - nonce: u64, - amount: BigUint, - ) -> EsdtTokenPayment { - self.redeem_token_total_circulating_supply(nonce) + fn mint_and_send_redeem_token(&self, to: &ManagedAddress, amount: BigUint) -> EsdtTokenPayment { + self.redeem_token_total_circulating_supply() .update(|supply| *supply += &amount); - self.redeem_token() - .nft_add_quantity_and_send(to, nonce, amount) + self.redeem_token().mint_and_send(to, amount) } - fn burn_redeem_token(&self, nonce: u64, amount: &BigUint) { - self.burn_redeem_token_without_supply_decrease(nonce, amount); + fn burn_redeem_token(&self, amount: &BigUint) { + self.burn_redeem_token_without_supply_decrease(amount); - self.redeem_token_total_circulating_supply(nonce) + self.redeem_token_total_circulating_supply() .update(|supply| *supply -= amount); } #[inline] - fn burn_redeem_token_without_supply_decrease(&self, nonce: u64, amount: &BigUint) { - self.redeem_token().nft_burn(nonce, amount); + fn burn_redeem_token_without_supply_decrease(&self, amount: &BigUint) { + self.redeem_token().burn(amount); } #[view(getRedeemTokenId)] #[storage_mapper("redeemTokenId")] - fn redeem_token(&self) -> NonFungibleTokenMapper; + fn redeem_token(&self) -> FungibleTokenMapper; #[view(getRedeemTokenTotalCirculatingSupply)] #[storage_mapper("totalCirculatingSupply")] - fn redeem_token_total_circulating_supply(&self, token_nonce: u64) - -> SingleValueMapper; + fn redeem_token_total_circulating_supply(&self) -> SingleValueMapper; } diff --git a/dex/price-discovery/src/user_actions/mod.rs b/dex/price-discovery/src/user_actions/mod.rs new file mode 100644 index 000000000..1c1982b4c --- /dev/null +++ b/dex/price-discovery/src/user_actions/mod.rs @@ -0,0 +1,3 @@ +pub mod owner_deposit_withdraw; +pub mod redeem; +pub mod user_deposit_withdraw; diff --git a/dex/price-discovery/src/user_actions/owner_deposit_withdraw.rs b/dex/price-discovery/src/user_actions/owner_deposit_withdraw.rs new file mode 100644 index 000000000..d60892267 --- /dev/null +++ b/dex/price-discovery/src/user_actions/owner_deposit_withdraw.rs @@ -0,0 +1,77 @@ +use crate::user_actions::user_deposit_withdraw::INVALID_PAYMENT_ERR_MSG; + +multiversx_sc::imports!(); + +pub static INVALID_AMOUNT_ERR_MSG: &[u8] = b"Invalid amount"; + +#[multiversx_sc::module] +pub trait OwnerDepositWithdrawModule: + crate::common_storage::CommonStorageModule + + crate::events::EventsModule + + crate::phase::PhaseModule + + crate::redeem_token::RedeemTokenModule + + multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule +{ + #[payable("*")] + #[endpoint(ownerDeposit)] + fn owner_deposit(&self) { + let phase = self.get_current_phase(); + self.require_owner_deposit_withdraw_allowed(&phase); + + let caller = self.blockchain().get_caller(); + self.require_owner_caller(&caller); + + let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); + let launched_token_id = self.launched_token_id().get(); + require!(payment_token == launched_token_id, INVALID_PAYMENT_ERR_MSG); + + let min_launched_tokens_amount = self.min_launched_tokens().get(); + let current_total_launched_tokens_amount = self.launched_token_balance().get(); + require!( + ¤t_total_launched_tokens_amount + &payment_amount >= min_launched_tokens_amount, + INVALID_AMOUNT_ERR_MSG + ); + + self.launched_token_balance() + .set(¤t_total_launched_tokens_amount + &payment_amount); + + self.emit_owner_deposit_event(&payment_amount); + } + + #[endpoint(ownerWithdraw)] + fn owner_withdraw(&self, withdraw_amount: BigUint) -> EsdtTokenPayment { + let phase = self.get_current_phase(); + self.require_owner_deposit_withdraw_allowed(&phase); + + let caller = self.blockchain().get_caller(); + self.require_owner_caller(&caller); + + let current_total_launched_tokens = self.launched_token_balance().get(); + require!( + withdraw_amount > 0 && withdraw_amount <= current_total_launched_tokens, + INVALID_AMOUNT_ERR_MSG + ); + + let min_launched_tokens = self.min_launched_tokens().get(); + require!( + ¤t_total_launched_tokens - &withdraw_amount >= min_launched_tokens, + INVALID_AMOUNT_ERR_MSG + ); + + self.launched_token_balance() + .update(|balance| *balance -= &withdraw_amount); + + let launched_token_id = self.launched_token_id().get(); + self.send() + .direct_esdt(&caller, &launched_token_id, 0, &withdraw_amount); + + self.emit_owner_withdraw_event(&withdraw_amount); + + EsdtTokenPayment::new(launched_token_id, 0, withdraw_amount) + } + + fn require_owner_caller(&self, caller: &ManagedAddress) { + let owner = self.owner_address().get(); + require!(caller == &owner, "Only owner may call this endpoint"); + } +} diff --git a/dex/price-discovery/src/user_actions/redeem.rs b/dex/price-discovery/src/user_actions/redeem.rs new file mode 100644 index 000000000..ab1ae4a65 --- /dev/null +++ b/dex/price-discovery/src/user_actions/redeem.rs @@ -0,0 +1,104 @@ +use crate::user_actions::user_deposit_withdraw::INVALID_PAYMENT_ERR_MSG; + +multiversx_sc::imports!(); + +#[multiversx_sc::module] +pub trait RedeemModule: + crate::common_storage::CommonStorageModule + + crate::events::EventsModule + + crate::phase::PhaseModule + + crate::redeem_token::RedeemTokenModule + + multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule +{ + /// After all phases have ended, + /// users can withdraw their fair share of launched tokens, + /// while the owner can withdraw the accepted tokens. + #[payable("*")] + #[endpoint] + fn redeem(&self) -> EgldOrEsdtTokenPayment { + let phase = self.get_current_phase(); + self.require_redeem_allowed(&phase); + + let caller = self.blockchain().get_caller(); + let owner = self.owner_address().get(); + if caller == owner { + let redeemed_tokens = self.owner_redeem(&caller); + + self.emit_redeem_event( + None, + &BigUint::zero(), + &redeemed_tokens.token_identifier, + &redeemed_tokens.amount, + ); + + return redeemed_tokens; + } + + let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); + let bought_tokens = self.user_redeem(&caller, &payment_token, &payment_amount); + + self.emit_redeem_event( + Some(&payment_token), + &payment_amount, + &bought_tokens.token_identifier, + &bought_tokens.amount, + ); + + bought_tokens + } + + fn owner_redeem(&self, owner: &ManagedAddress) -> EgldOrEsdtTokenPayment { + let launched_token_supply = self.launched_token_balance().get(); + require!( + launched_token_supply > 0, + "May not withdraw tokens as launched tokens were not deposited" + ); + + let accepted_token_id = self.accepted_token_id().get(); + let accepted_token_balance = self.accepted_token_balance().get(); + self.send() + .direct(owner, &accepted_token_id, 0, &accepted_token_balance); + + EgldOrEsdtTokenPayment::new(accepted_token_id, 0, accepted_token_balance) + } + + fn user_redeem( + &self, + user: &ManagedAddress, + payment_token: &TokenIdentifier, + payment_amount: &BigUint, + ) -> EgldOrEsdtTokenPayment { + let redeem_token_id = self.redeem_token().get_token_id(); + require!(payment_token == &redeem_token_id, INVALID_PAYMENT_ERR_MSG); + + let launched_token_supply = self.launched_token_balance().get(); + if launched_token_supply == 0 { + let accepted_token_id = self.accepted_token_id().get(); + self.send() + .direct(user, &accepted_token_id, 0, payment_amount); + + return EgldOrEsdtTokenPayment::new(accepted_token_id, 0, payment_amount.clone()); + } + + let bought_tokens = self.compute_user_bought_tokens(payment_amount); + self.burn_redeem_token_without_supply_decrease(payment_amount); + + self.send().direct( + user, + &bought_tokens.token_identifier, + 0, + &bought_tokens.amount, + ); + + bought_tokens + } + + fn compute_user_bought_tokens(&self, redeem_token_amount: &BigUint) -> EgldOrEsdtTokenPayment { + let redeem_token_supply = self.redeem_token_total_circulating_supply().get(); + let launched_token_id = EgldOrEsdtTokenIdentifier::esdt(self.launched_token_id().get()); + let total_token_supply = self.launched_token_balance().get(); + let reward_amount = total_token_supply * redeem_token_amount / redeem_token_supply; + + EgldOrEsdtTokenPayment::new(launched_token_id, 0, reward_amount) + } +} diff --git a/dex/price-discovery/src/user_actions/user_deposit_withdraw.rs b/dex/price-discovery/src/user_actions/user_deposit_withdraw.rs new file mode 100644 index 000000000..778a8b491 --- /dev/null +++ b/dex/price-discovery/src/user_actions/user_deposit_withdraw.rs @@ -0,0 +1,65 @@ +multiversx_sc::imports!(); + +pub static INVALID_PAYMENT_ERR_MSG: &[u8] = b"Invalid payment token"; + +#[multiversx_sc::module] +pub trait UserDepositWithdrawModule: + crate::common_storage::CommonStorageModule + + crate::events::EventsModule + + crate::phase::PhaseModule + + crate::redeem_token::RedeemTokenModule + + multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule +{ + /// Users can deposit accepted_tokens. + /// They will receive an ESDT that can be used to withdraw launched tokens + #[payable("*")] + #[endpoint(userDeposit)] + fn user_deposit(&self) -> EsdtTokenPayment { + let phase = self.get_current_phase(); + self.require_user_deposit_withdraw_allowed(&phase); + + let (payment_token, payment_amount) = self.call_value().egld_or_single_fungible_esdt(); + let accepted_token_id = self.accepted_token_id().get(); + require!(payment_token == accepted_token_id, INVALID_PAYMENT_ERR_MSG); + + self.accepted_token_balance() + .update(|balance| *balance += &payment_amount); + + let caller = self.blockchain().get_caller(); + let payment_result = self.mint_and_send_redeem_token(&caller, payment_amount.clone()); + + self.emit_user_deposit_event( + &payment_amount, + &payment_result.token_identifier, + &payment_amount, + ); + + payment_result + } + + /// Deposit ESDT received after deposit to withdraw the initially deposited tokens. + #[payable("*")] + #[endpoint(userWithdraw)] + fn user_withdraw(&self) -> EgldOrEsdtTokenPayment { + let phase = self.get_current_phase(); + self.require_user_deposit_withdraw_allowed(&phase); + + let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); + let redeem_token_id = self.redeem_token().get_token_id(); + require!(payment_token == redeem_token_id, INVALID_PAYMENT_ERR_MSG); + + self.burn_redeem_token(&payment_amount); + self.accepted_token_balance() + .update(|balance| *balance -= &payment_amount); + + let refund_token_id = self.accepted_token_id().get(); + + let caller = self.blockchain().get_caller(); + self.send() + .direct(&caller, &refund_token_id, 0, &payment_amount); + + self.emit_user_withdraw_event(&payment_amount, &payment_token, &payment_amount); + + EgldOrEsdtTokenPayment::new(refund_token_id, 0, payment_amount) + } +} diff --git a/dex/price-discovery/src/views.rs b/dex/price-discovery/src/views.rs new file mode 100644 index 000000000..f4341d167 --- /dev/null +++ b/dex/price-discovery/src/views.rs @@ -0,0 +1,15 @@ +multiversx_sc::imports!(); + +#[multiversx_sc::module] +pub trait ViewsModule: crate::common_storage::CommonStorageModule { + #[view(getCurrentPrice)] + fn get_current_price(&self) -> BigUint { + let launched_token_balance = self.launched_token_balance().get(); + let accepted_token_balance = self.accepted_token_balance().get(); + + require!(launched_token_balance > 0, "No launched tokens available"); + + let price_precision = self.price_precision().get(); + accepted_token_balance * price_precision / launched_token_balance + } +} diff --git a/dex/price-discovery/tests/price_disc_tests.rs b/dex/price-discovery/tests/price_disc_tests.rs index a6cfbec43..518c17bfb 100644 --- a/dex/price-discovery/tests/price_disc_tests.rs +++ b/dex/price-discovery/tests/price_disc_tests.rs @@ -1,431 +1,304 @@ #![allow(deprecated)] -use multiversx_sc::codec::Empty; -use multiversx_sc_scenario::{managed_biguint, managed_token_id_wrapped}; -use multiversx_sc_scenario::{rust_biguint, DebugApi}; -use price_discovery::common_storage::*; -use price_discovery::redeem_token::*; -use price_discovery::PriceDiscovery; - mod tests_common; -use simple_lock::locked_token::LockedTokenAttributes; +use multiversx_sc_scenario::rust_biguint; use tests_common::*; -const MIN_PRICE_PRECISION: u64 = 1_000_000_000_000_000_000; - #[test] -fn test_init() { - let _ = init(price_discovery::contract_obj); +fn setup_test() { + let _ = PriceDiscSetup::new(price_discovery::contract_obj); } #[test] -fn test_deposit_launched_tokens_ok() { - let mut pd_setup = init(price_discovery::contract_obj); +fn user_deposit_too_early_test() { + let mut setup = PriceDiscSetup::new(price_discovery::contract_obj); + setup + .call_user_deposit(&setup.first_user_address.clone(), 1_000) + .assert_user_error("User deposit/withdraw not allowed in this phase"); +} - pd_setup.blockchain_wrapper.set_block_nonce(START_BLOCK); +#[test] +fn user_deposit_ok_test() { + let mut setup = PriceDiscSetup::new(price_discovery::contract_obj); - let init_deposit_amt = rust_biguint!(5_000_000_000); + setup.b_mock.set_block_timestamp(START_TIME + 1); - call_deposit_initial_tokens(&mut pd_setup, &init_deposit_amt); + setup + .call_user_deposit(&setup.first_user_address.clone(), 1_000) + .assert_ok(); - pd_setup.blockchain_wrapper.check_esdt_balance( - pd_setup.pd_wrapper.address_ref(), - LAUNCHED_TOKEN_ID, - &init_deposit_amt, + setup.b_mock.check_esdt_balance( + setup.pd_wrapper.address_ref(), + ACCEPTED_TOKEN_ID, + &rust_biguint!(1_000), + ); + setup.b_mock.check_esdt_balance( + &setup.first_user_address, + ACCEPTED_TOKEN_ID, + &rust_biguint!(USER_BALANCE - 1_000), + ); + setup.b_mock.check_esdt_balance( + &setup.first_user_address, + REDEEM_TOKEN_ID, + &rust_biguint!(1_000), ); } #[test] -fn deposit_too_early() { - let mut pd_setup = init(price_discovery::contract_obj); - - pd_setup.blockchain_wrapper.set_block_nonce(START_BLOCK - 1); - - // must clone, as we can't borrow pd_setup as mutable and as immutable at the same time - let first_user_address = pd_setup.first_user_address.clone(); - call_deposit( - &mut pd_setup, - &first_user_address, - &rust_biguint!(1_000_000_000), - ) - .assert_user_error("Deposit not allowed in this phase"); -} - -pub fn user_deposit_ok_steps( - pd_setup: &mut PriceDiscSetup, -) where - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, -{ - pd_setup.blockchain_wrapper.set_block_nonce(START_BLOCK); +fn user_deposit_withdraw_ok_test() { + let mut setup = PriceDiscSetup::new(price_discovery::contract_obj); - call_deposit_initial_tokens(pd_setup, &rust_biguint!(5_000_000_000)); + setup.b_mock.set_block_timestamp(START_TIME + 1); - // must clone, as we can't borrow pd_setup as mutable and as immutable at the same time - let first_user_address = pd_setup.first_user_address.clone(); - let first_deposit_amt = rust_biguint!(1_000_000_000); - call_deposit(pd_setup, &first_user_address, &first_deposit_amt).assert_ok(); - - pd_setup.blockchain_wrapper.check_nft_balance( - &first_user_address, - REDEEM_TOKEN_ID, - ACCEPTED_TOKEN_REDEEM_NONCE, - &first_deposit_amt, - Some(&Empty), - ); - - // second user deposit - let second_user_address = pd_setup.second_user_address.clone(); - let second_deposit_amt = rust_biguint!(500_000_000); - call_deposit(pd_setup, &second_user_address, &second_deposit_amt).assert_ok(); + setup + .call_user_deposit(&setup.first_user_address.clone(), 1_000) + .assert_ok(); + setup + .call_user_withdraw(&setup.first_user_address.clone(), 400) + .assert_ok(); - pd_setup.blockchain_wrapper.check_nft_balance( - &second_user_address, - REDEEM_TOKEN_ID, - ACCEPTED_TOKEN_REDEEM_NONCE, - &second_deposit_amt, - Some(&Empty), + setup.b_mock.check_esdt_balance( + setup.pd_wrapper.address_ref(), + ACCEPTED_TOKEN_ID, + &rust_biguint!(600), ); - - // check SC balance - pd_setup.blockchain_wrapper.check_esdt_balance( - pd_setup.pd_wrapper.address_ref(), + setup.b_mock.check_esdt_balance( + &setup.first_user_address, ACCEPTED_TOKEN_ID, - &(first_deposit_amt + second_deposit_amt), + &rust_biguint!(USER_BALANCE - 600), + ); + setup.b_mock.check_esdt_balance( + &setup.first_user_address, + REDEEM_TOKEN_ID, + &rust_biguint!(600), ); } #[test] -fn user_deposit_ok() { - let mut pd_setup = init(price_discovery::contract_obj); - user_deposit_ok_steps(&mut pd_setup); +fn owner_deposit_too_early_test() { + let mut setup = PriceDiscSetup::new(price_discovery::contract_obj); + + setup.b_mock.set_block_timestamp(START_TIME + 1); + + setup + .call_owner_deposit(1_000) + .assert_user_error("Owner deposit/withdraw not allowed in this phase"); } #[test] -fn try_deposit_below_min_price() { - let mut pd_setup = init(price_discovery::contract_obj); - pd_setup.blockchain_wrapper.set_block_nonce(START_BLOCK); - - let owner_addr = pd_setup.owner_address.clone(); - pd_setup - .blockchain_wrapper - .execute_tx(&owner_addr, &pd_setup.pd_wrapper, &rust_biguint!(0), |sc| { - // each launched token = 0.5 accepted token - sc.min_launched_token_price() - .set(&managed_biguint!(MIN_PRICE_PRECISION / 2)); - }) +fn owner_deposit_ok_test() { + let mut setup = PriceDiscSetup::new(price_discovery::contract_obj); + + setup.b_mock.set_block_timestamp(START_TIME + 1); + + setup + .call_user_deposit(&setup.first_user_address.clone(), 1_000) .assert_ok(); + setup + .call_user_deposit(&setup.second_user_address.clone(), 9_000) + .assert_ok(); + + setup + .b_mock + .set_block_timestamp(START_TIME + USER_DEPOSIT_TIME + 1); - call_deposit_initial_tokens(&mut pd_setup, &rust_biguint!(5_000_000_000)); - - // deposit accepted tokens, even if below min price - let first_user_address = pd_setup.first_user_address.clone(); - let first_deposit_amt = rust_biguint!(1_000_000_000); - call_deposit(&mut pd_setup, &first_user_address, &first_deposit_amt).assert_ok(); - - // try deposit more launched tokens - let b_mock = &mut pd_setup.blockchain_wrapper; - let rand_user = b_mock.create_user_account(&rust_biguint!(0)); - b_mock.set_esdt_balance(&rand_user, LAUNCHED_TOKEN_ID, &rust_biguint!(500)); - - b_mock - .execute_esdt_transfer( - &rand_user, - &pd_setup.pd_wrapper, - LAUNCHED_TOKEN_ID, - 0, - &rust_biguint!(500), - |sc| { - sc.deposit(); - }, - ) - .assert_user_error("Launched token below min price"); + setup.call_owner_deposit(2_000).assert_ok(); } #[test] -fn deposit_above_min_price() { - let mut pd_setup = init(price_discovery::contract_obj); - pd_setup.blockchain_wrapper.set_block_nonce(START_BLOCK); - - let owner_addr = pd_setup.owner_address.clone(); - pd_setup - .blockchain_wrapper - .execute_tx(&owner_addr, &pd_setup.pd_wrapper, &rust_biguint!(0), |sc| { - // each launched token = 0.2 accepted token - sc.min_launched_token_price() - .set(&managed_biguint!(MIN_PRICE_PRECISION / 5)); - }) +fn owner_withdraw_too_much_test() { + let mut setup = PriceDiscSetup::new(price_discovery::contract_obj); + + setup.b_mock.set_block_timestamp(START_TIME + 1); + + setup + .call_user_deposit(&setup.first_user_address.clone(), 1_000) + .assert_ok(); + setup + .call_user_deposit(&setup.second_user_address.clone(), 9_000) .assert_ok(); - call_deposit_initial_tokens(&mut pd_setup, &rust_biguint!(5_000_000_000)); + setup + .b_mock + .set_block_timestamp(START_TIME + USER_DEPOSIT_TIME + 1); + + setup.call_owner_deposit(2_000).assert_ok(); - let first_user_address = pd_setup.first_user_address.clone(); - let first_deposit_amt = rust_biguint!(1_000_000_000); - call_deposit(&mut pd_setup, &first_user_address, &first_deposit_amt).assert_ok(); + setup + .call_owner_withdraw(1_500) + .assert_user_error("Invalid amount"); } #[test] -fn withdraw_below_min_price() { - let mut pd_setup = init(price_discovery::contract_obj); - pd_setup.blockchain_wrapper.set_block_nonce(START_BLOCK); - - let owner_addr = pd_setup.owner_address.clone(); - pd_setup - .blockchain_wrapper - .execute_tx(&owner_addr, &pd_setup.pd_wrapper, &rust_biguint!(0), |sc| { - // each launched token = 0.1 accepted token - sc.min_launched_token_price() - .set(&managed_biguint!(MIN_PRICE_PRECISION / 10)); - }) - .assert_ok(); +fn owner_withdraw_ok_test() { + let mut setup = PriceDiscSetup::new(price_discovery::contract_obj); - call_deposit_initial_tokens(&mut pd_setup, &rust_biguint!(5_000_000_000)); + setup.b_mock.set_block_timestamp(START_TIME + 1); - let first_user_address = pd_setup.first_user_address.clone(); - let first_deposit_amt = rust_biguint!(1_000_000_000); - call_deposit(&mut pd_setup, &first_user_address, &first_deposit_amt).assert_ok(); + setup + .call_user_deposit(&setup.first_user_address.clone(), 1_000) + .assert_ok(); + setup + .call_user_deposit(&setup.second_user_address.clone(), 9_000) + .assert_ok(); - call_withdraw( - &mut pd_setup, - &first_user_address, - &rust_biguint!(600_000_000), - ) - .assert_user_error("Launched token below min price"); -} + setup + .b_mock + .set_block_timestamp(START_TIME + USER_DEPOSIT_TIME + 1); -pub fn withdraw_ok_steps( - pd_setup: &mut PriceDiscSetup, - penalty_percentage: u64, -) where - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, -{ - let first_user_address = pd_setup.first_user_address.clone(); - let balance_before = rust_biguint!(0); - let deposit_amt = rust_biguint!(1_000_000_000); - let withdraw_amt = rust_biguint!(400_000_000); - call_withdraw(pd_setup, &first_user_address, &withdraw_amt).assert_ok(); - - let penalty_amount = &withdraw_amt * penalty_percentage / MAX_PERCENTAGE; - let withdrawn_amount = &withdraw_amt - &penalty_amount; - - pd_setup.blockchain_wrapper.check_nft_balance( - &first_user_address, - REDEEM_TOKEN_ID, - ACCEPTED_TOKEN_REDEEM_NONCE, - &(&deposit_amt - &withdraw_amt), - Some(&Empty), - ); + setup.call_owner_deposit(2_000).assert_ok(); - // check that the SC burned the tokens - // 1 remains for ESDTNFTAddQuantity purposes - pd_setup.blockchain_wrapper.check_nft_balance( - pd_setup.pd_wrapper.address_ref(), - REDEEM_TOKEN_ID, - ACCEPTED_TOKEN_REDEEM_NONCE, - &rust_biguint!(1), - Some(&Empty), - ); + setup.call_owner_withdraw(500).assert_ok(); - pd_setup.blockchain_wrapper.check_esdt_balance( - &first_user_address, - ACCEPTED_TOKEN_ID, - &(&balance_before + &withdrawn_amount), + setup.b_mock.check_esdt_balance( + &setup.owner_address, + LAUNCHED_TOKEN_ID, + &rust_biguint!(USER_BALANCE - 1_500), ); - - let sc_balance_before = rust_biguint!(1_500_000_000); - pd_setup.blockchain_wrapper.check_esdt_balance( - pd_setup.pd_wrapper.address_ref(), - ACCEPTED_TOKEN_ID, - &(&sc_balance_before - &withdrawn_amount), + setup.b_mock.check_esdt_balance( + setup.pd_wrapper.address_ref(), + LAUNCHED_TOKEN_ID, + &rust_biguint!(1_500), ); } #[test] -fn withdraw_ok() { - let mut pd_setup = init(price_discovery::contract_obj); - user_deposit_ok_steps(&mut pd_setup); - withdraw_ok_steps(&mut pd_setup, 0); -} +fn user_redeem_too_early_test() { + let mut setup = PriceDiscSetup::new(price_discovery::contract_obj); -#[test] -fn withdraw_linear_penalty_start() { - let mut pd_setup = init(price_discovery::contract_obj); - user_deposit_ok_steps(&mut pd_setup); - - let linear_penalty_start_block = START_BLOCK + NO_LIMIT_PHASE_DURATION_BLOCKS; - pd_setup - .blockchain_wrapper - .set_block_nonce(linear_penalty_start_block); - withdraw_ok_steps(&mut pd_setup, MIN_PENALTY_PERCENTAGE); -} + setup.b_mock.set_block_timestamp(START_TIME + 1); -#[test] -fn withdraw_linear_penalty_end() { - let mut pd_setup = init(price_discovery::contract_obj); - user_deposit_ok_steps(&mut pd_setup); - - let linear_penalty_end_block = - START_BLOCK + NO_LIMIT_PHASE_DURATION_BLOCKS + LINEAR_PENALTY_PHASE_DURATION_BLOCKS - 1; - pd_setup - .blockchain_wrapper - .set_block_nonce(linear_penalty_end_block); - withdraw_ok_steps(&mut pd_setup, MAX_PENALTY_PERCENTAGE); + setup + .call_user_deposit(&setup.first_user_address.clone(), 1_000) + .assert_ok(); + setup + .call_user_deposit(&setup.second_user_address.clone(), 9_000) + .assert_ok(); + + setup + .b_mock + .set_block_timestamp(START_TIME + USER_DEPOSIT_TIME + 1); + + setup.call_owner_deposit(2_000).assert_ok(); + + setup + .call_user_redeem(&setup.first_user_address.clone(), 1_000) + .assert_user_error("Redeem not allowed in this phase"); } #[test] -fn withdraw_linear_penalty_middle() { - let mut pd_setup = init(price_discovery::contract_obj); - user_deposit_ok_steps(&mut pd_setup); - - let linear_penalty_start_block = START_BLOCK + NO_LIMIT_PHASE_DURATION_BLOCKS; - let linear_penalty_end_block = - START_BLOCK + NO_LIMIT_PHASE_DURATION_BLOCKS + LINEAR_PENALTY_PHASE_DURATION_BLOCKS - 1; - pd_setup - .blockchain_wrapper - .set_block_nonce((linear_penalty_start_block + linear_penalty_end_block) / 2); - withdraw_ok_steps( - &mut pd_setup, - (MIN_PENALTY_PERCENTAGE + MAX_PENALTY_PERCENTAGE) / 2, +fn user_redeem_no_owner_deposit() { + let mut setup = PriceDiscSetup::new(price_discovery::contract_obj); + + setup.b_mock.set_block_timestamp(START_TIME + 1); + + setup + .call_user_deposit(&setup.first_user_address.clone(), 1_000) + .assert_ok(); + setup + .call_user_deposit(&setup.second_user_address.clone(), 9_000) + .assert_ok(); + + setup + .b_mock + .set_block_timestamp(START_TIME + USER_DEPOSIT_TIME + OWNER_DEPOSIT_TIME + 1); + + setup + .call_user_redeem(&setup.first_user_address.clone(), 1_000) + .assert_ok(); + setup + .call_user_redeem(&setup.second_user_address.clone(), 9_000) + .assert_ok(); + + setup.b_mock.check_esdt_balance( + &setup.first_user_address, + ACCEPTED_TOKEN_ID, + &rust_biguint!(USER_BALANCE), + ); + setup.b_mock.check_esdt_balance( + &setup.second_user_address, + ACCEPTED_TOKEN_ID, + &rust_biguint!(USER_BALANCE), ); } #[test] -fn withdraw_fixed_penalty() { - let mut pd_setup = init(price_discovery::contract_obj); - user_deposit_ok_steps(&mut pd_setup); - - let fixed_penalty_start_block = - START_BLOCK + NO_LIMIT_PHASE_DURATION_BLOCKS + LINEAR_PENALTY_PHASE_DURATION_BLOCKS; - pd_setup - .blockchain_wrapper - .set_block_nonce(fixed_penalty_start_block); - withdraw_ok_steps(&mut pd_setup, FIXED_PENALTY_PERCENTAGE); -} +fn user_redeem_ok_test() { + let mut setup = PriceDiscSetup::new(price_discovery::contract_obj); -#[test] -fn try_deposit_in_withdraw_only_phase() { - let mut pd_setup = init(price_discovery::contract_obj); - user_deposit_ok_steps(&mut pd_setup); - - let fixed_penalty_start_block = - START_BLOCK + NO_LIMIT_PHASE_DURATION_BLOCKS + LINEAR_PENALTY_PHASE_DURATION_BLOCKS; - pd_setup - .blockchain_wrapper - .set_block_nonce(fixed_penalty_start_block); - - let caller_addr = pd_setup.second_user_address.clone(); - call_deposit(&mut pd_setup, &caller_addr, &rust_biguint!(1_000)) - .assert_user_error("Deposit not allowed in this phase"); -} + setup.b_mock.set_block_timestamp(START_TIME + 1); -#[test] -fn withdraw_too_late() { - let mut pd_setup = init(price_discovery::contract_obj); - user_deposit_ok_steps(&mut pd_setup); + setup + .call_user_deposit(&setup.first_user_address.clone(), 1_000) + .assert_ok(); + setup + .call_user_deposit(&setup.second_user_address.clone(), 9_000) + .assert_ok(); - pd_setup.blockchain_wrapper.set_block_nonce(END_BLOCK + 1); + setup + .b_mock + .set_block_timestamp(START_TIME + USER_DEPOSIT_TIME + 1); - let caller_addr = pd_setup.first_user_address.clone(); - call_withdraw(&mut pd_setup, &caller_addr, &rust_biguint!(1_000)) - .assert_user_error("Withdraw not allowed in this phase"); -} + setup.call_owner_deposit(2_000).assert_ok(); -#[test] -fn redeem_ok() { - let mut pd_setup = init(price_discovery::contract_obj); - user_deposit_ok_steps(&mut pd_setup); - withdraw_ok_steps(&mut pd_setup, 0); - - pd_setup.blockchain_wrapper.set_block_nonce(END_BLOCK + 1); - - let first_user_address = pd_setup.first_user_address.clone(); - let first_user_redeem_token_amount = rust_biguint!(600_000_000); - call_redeem( - &mut pd_setup, - &first_user_address, - ACCEPTED_TOKEN_REDEEM_NONCE, - &first_user_redeem_token_amount, - ) - .assert_ok(); - - let second_user_address = pd_setup.second_user_address.clone(); - let second_user_redeem_token_amount = rust_biguint!(500_000_000); - call_redeem( - &mut pd_setup, - &second_user_address, - ACCEPTED_TOKEN_REDEEM_NONCE, - &second_user_redeem_token_amount, - ) - .assert_ok(); - - let owner_address = pd_setup.owner_address.clone(); - let owner_redeem_amount = rust_biguint!(5_000_000_000); - call_redeem( - &mut pd_setup, - &owner_address, - LAUNCHED_TOKEN_REDEEM_NONCE, - &owner_redeem_amount, - ) - .assert_ok(); - - DebugApi::dummy(); - let first_user_expected_launched_tokens_balance = - rust_biguint!(5_000_000_000u64 * 600_000_000 / 1_100_000_000); - pd_setup.blockchain_wrapper.check_nft_balance( - &first_user_address, - LOCKED_TOKEN_ID, - 1, - &first_user_expected_launched_tokens_balance, - Some(&LockedTokenAttributes:: { - original_token_id: managed_token_id_wrapped!(LAUNCHED_TOKEN_ID), - original_token_nonce: 0, - unlock_epoch: UNLOCK_EPOCH, - }), - ); + setup + .b_mock + .set_block_timestamp(START_TIME + USER_DEPOSIT_TIME + OWNER_DEPOSIT_TIME + 1); - let second_user_expected_launched_tokens_balance = - rust_biguint!(5_000_000_000u64 * 500_000_000 / 1_100_000_000); - pd_setup.blockchain_wrapper.check_nft_balance( - &second_user_address, - LOCKED_TOKEN_ID, - 1, - &second_user_expected_launched_tokens_balance, - Some(&LockedTokenAttributes:: { - original_token_id: managed_token_id_wrapped!(LAUNCHED_TOKEN_ID), - original_token_nonce: 0, - unlock_epoch: UNLOCK_EPOCH, - }), - ); + setup + .call_user_redeem(&setup.first_user_address.clone(), 1_000) + .assert_ok(); + setup + .call_user_redeem(&setup.second_user_address.clone(), 9_000) + .assert_ok(); + setup.call_owner_redeem().assert_ok(); - let owner_expected_accepted_tokens_balance = - rust_biguint!(1_100_000_000u64 * 5_000_000_000 / 5_000_000_000); - pd_setup.blockchain_wrapper.check_nft_balance( - &owner_address, - LOCKED_TOKEN_ID, - 2, - &owner_expected_accepted_tokens_balance, - Some(&LockedTokenAttributes:: { - original_token_id: managed_token_id_wrapped!(ACCEPTED_TOKEN_ID), - original_token_nonce: 0, - unlock_epoch: UNLOCK_EPOCH, - }), + // owner try withdraw twice + setup + .call_owner_redeem() + .assert_error(10, "insufficient funds"); + + // check accepted token balance + setup.b_mock.check_esdt_balance( + &setup.first_user_address, + ACCEPTED_TOKEN_ID, + &rust_biguint!(USER_BALANCE - 1_000), + ); + setup.b_mock.check_esdt_balance( + &setup.second_user_address, + ACCEPTED_TOKEN_ID, + &rust_biguint!(USER_BALANCE - 9_000), + ); + setup.b_mock.check_esdt_balance( + &setup.owner_address, + ACCEPTED_TOKEN_ID, + &rust_biguint!(10_000), + ); + setup.b_mock.check_esdt_balance( + setup.pd_wrapper.address_ref(), + ACCEPTED_TOKEN_ID, + &rust_biguint!(0), ); -} -#[test] -fn redeem_too_early() { - let mut pd_setup = init(price_discovery::contract_obj); - user_deposit_ok_steps(&mut pd_setup); - withdraw_ok_steps(&mut pd_setup, 0); - - pd_setup.blockchain_wrapper.set_block_nonce(END_BLOCK - 1); - - let first_user_address = pd_setup.first_user_address.clone(); - let first_user_redeem_token_amount = rust_biguint!(600_000_000); - call_redeem( - &mut pd_setup, - &first_user_address, - ACCEPTED_TOKEN_REDEEM_NONCE, - &first_user_redeem_token_amount, - ) - .assert_user_error("Redeem not allowed in this phase"); + // check launched token balance + setup.b_mock.check_esdt_balance( + &setup.first_user_address, + LAUNCHED_TOKEN_ID, + &rust_biguint!(200), + ); + setup.b_mock.check_esdt_balance( + &setup.second_user_address, + LAUNCHED_TOKEN_ID, + &rust_biguint!(1_800), + ); + setup.b_mock.check_esdt_balance( + &setup.owner_address, + LAUNCHED_TOKEN_ID, + &rust_biguint!(USER_BALANCE - 2_000), + ); + setup.b_mock.check_esdt_balance( + setup.pd_wrapper.address_ref(), + LAUNCHED_TOKEN_ID, + &rust_biguint!(0), + ); } diff --git a/dex/price-discovery/tests/tests_common.rs b/dex/price-discovery/tests/tests_common.rs index 984a7ec0c..81f3c6cd7 100644 --- a/dex/price-discovery/tests/tests_common.rs +++ b/dex/price-discovery/tests/tests_common.rs @@ -1,8 +1,6 @@ #![allow(deprecated)] -use multiversx_sc::codec::Empty; use multiversx_sc::types::{Address, EsdtLocalRole}; -use multiversx_sc_scenario::whitebox_legacy::TxResult; use multiversx_sc_scenario::{ managed_address, managed_biguint, managed_token_id_wrapped, whitebox_legacy::*, }; @@ -12,249 +10,175 @@ use price_discovery::redeem_token::*; use price_discovery::*; use multiversx_sc::storage::mappers::StorageTokenWrapper; -use simple_lock::locked_token::LockedTokenModule; -use simple_lock::SimpleLock; +use user_actions::owner_deposit_withdraw::OwnerDepositWithdrawModule; +use user_actions::redeem::RedeemModule; +use user_actions::user_deposit_withdraw::UserDepositWithdrawModule; -const PD_WASM_PATH: &str = "../output/price-discovery.wasm"; +static PD_WASM_PATH: &str = "../output/price-discovery.wasm"; -pub const LAUNCHED_TOKEN_ID: &[u8] = b"SOCOOLWOW-123456"; -pub const ACCEPTED_TOKEN_ID: &[u8] = b"USDC-123456"; -pub const REDEEM_TOKEN_ID: &[u8] = b"GIBREWARDS-123456"; -pub const LOCKED_TOKEN_ID: &[u8] = b"NOOO0-123456"; +pub static LAUNCHED_TOKEN_ID: &[u8] = b"SOCOOLWOW-123456"; +pub static ACCEPTED_TOKEN_ID: &[u8] = b"USDC-123456"; +pub static REDEEM_TOKEN_ID: &[u8] = b"GIBREWARDS-123456"; pub const OWNER_EGLD_BALANCE: u64 = 100_000_000; +pub const USER_BALANCE: u64 = 1_000_000_000; -pub const START_BLOCK: u64 = 10; -pub const NO_LIMIT_PHASE_DURATION_BLOCKS: u64 = 5; -pub const LINEAR_PENALTY_PHASE_DURATION_BLOCKS: u64 = 5; -pub const FIXED_PENALTY_PHASE_DURATION_BLOCKS: u64 = 5; -pub const END_BLOCK: u64 = START_BLOCK - + NO_LIMIT_PHASE_DURATION_BLOCKS - + LINEAR_PENALTY_PHASE_DURATION_BLOCKS - + FIXED_PENALTY_PHASE_DURATION_BLOCKS; -pub const UNLOCK_EPOCH: u64 = 20; - -pub const MIN_PENALTY_PERCENTAGE: u64 = 1_000_000_000_000; // 10% -pub const MAX_PENALTY_PERCENTAGE: u64 = 5_000_000_000_000; // 50% -pub const FIXED_PENALTY_PERCENTAGE: u64 = 2_500_000_000_000; // 25% +pub const START_TIME: Timestamp = 10; +pub const USER_DEPOSIT_TIME: Timestamp = 5; +pub const OWNER_DEPOSIT_TIME: Timestamp = 5; +// pub const END_TIME: u64 = START_TIME + USER_DEPOSIT_TIME + OWNER_DEPOSIT_TIME; +pub const MIN_LAUNCHED_TOKENS: u64 = 1_000; pub struct PriceDiscSetup where PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { - pub blockchain_wrapper: BlockchainStateWrapper, + pub b_mock: BlockchainStateWrapper, pub owner_address: Address, pub first_user_address: Address, pub second_user_address: Address, pub pd_wrapper: ContractObjWrapper, PriceDiscObjBuilder>, - pub locking_sc_address: Address, } -pub fn init( - pd_builder: PriceDiscObjBuilder, -) -> PriceDiscSetup +impl PriceDiscSetup where PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, { - let rust_zero = rust_biguint!(0u64); - let mut blockchain_wrapper = BlockchainStateWrapper::new(); - let first_user_address = blockchain_wrapper.create_user_account(&rust_zero); - let second_user_address = blockchain_wrapper.create_user_account(&rust_zero); - let owner_address = blockchain_wrapper.create_user_account(&rust_biguint!(OWNER_EGLD_BALANCE)); - - let pd_wrapper = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - pd_builder, - PD_WASM_PATH, - ); - - // set user balances - let prev_owner_balance = - blockchain_wrapper.get_esdt_balance(&owner_address, LAUNCHED_TOKEN_ID, 0); - blockchain_wrapper.set_esdt_balance( - &owner_address, - LAUNCHED_TOKEN_ID, - &(prev_owner_balance + rust_biguint!(5_000_000_000)), - ); - blockchain_wrapper.set_esdt_balance( - &first_user_address, - ACCEPTED_TOKEN_ID, - &rust_biguint!(1_000_000_000), - ); - blockchain_wrapper.set_esdt_balance( - &second_user_address, - ACCEPTED_TOKEN_ID, - &rust_biguint!(1_000_000_000), - ); - - // set sc roles and initial minted SFTs (only needed for the purpose of SFT add quantity) - blockchain_wrapper.set_esdt_local_roles( - pd_wrapper.address_ref(), - REDEEM_TOKEN_ID, - &[ - EsdtLocalRole::NftCreate, - EsdtLocalRole::NftBurn, - EsdtLocalRole::NftAddQuantity, - ], - ); - blockchain_wrapper.set_nft_balance( - pd_wrapper.address_ref(), - REDEEM_TOKEN_ID, - LAUNCHED_TOKEN_REDEEM_NONCE, - &rust_biguint!(1), - &Empty, - ); - blockchain_wrapper.set_nft_balance( - pd_wrapper.address_ref(), - REDEEM_TOKEN_ID, - ACCEPTED_TOKEN_REDEEM_NONCE, - &rust_biguint!(1), - &Empty, - ); - - blockchain_wrapper.set_block_nonce(START_BLOCK - 1); - - // init locking SC - let locking_sc_wrapper = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - simple_lock::contract_obj, - "Some path", - ); - - blockchain_wrapper - .execute_tx(&owner_address, &locking_sc_wrapper, &rust_zero, |sc| { - sc.init(); - sc.locked_token() - .set_token_id(managed_token_id!(LOCKED_TOKEN_ID)); - }) - .assert_ok(); - - blockchain_wrapper.set_esdt_local_roles( - locking_sc_wrapper.address_ref(), - LOCKED_TOKEN_ID, - &[ - EsdtLocalRole::NftCreate, - EsdtLocalRole::NftAddQuantity, - EsdtLocalRole::NftBurn, - ], - ); - - // init Price Discovery SC - blockchain_wrapper - .execute_tx(&owner_address, &pd_wrapper, &rust_zero, |sc| { - sc.init( - managed_token_id!(LAUNCHED_TOKEN_ID), - managed_token_id_wrapped!(ACCEPTED_TOKEN_ID), - 18, - managed_biguint!(0), - START_BLOCK, - NO_LIMIT_PHASE_DURATION_BLOCKS, - LINEAR_PENALTY_PHASE_DURATION_BLOCKS, - FIXED_PENALTY_PHASE_DURATION_BLOCKS, - UNLOCK_EPOCH, - managed_biguint!(MIN_PENALTY_PERCENTAGE), - managed_biguint!(MAX_PENALTY_PERCENTAGE), - managed_biguint!(FIXED_PENALTY_PERCENTAGE), - managed_address!(locking_sc_wrapper.address_ref()), - ); + pub fn new(pd_builder: PriceDiscObjBuilder) -> Self { + let rust_zero = rust_biguint!(0u64); + let mut b_mock = BlockchainStateWrapper::new(); + let first_user_address = b_mock.create_user_account(&rust_zero); + let second_user_address = b_mock.create_user_account(&rust_zero); + let owner_address = b_mock.create_user_account(&rust_biguint!(OWNER_EGLD_BALANCE)); + + let pd_wrapper = + b_mock.create_sc_account(&rust_zero, Some(&owner_address), pd_builder, PD_WASM_PATH); + + // set user balances + let prev_owner_balance = b_mock.get_esdt_balance(&owner_address, LAUNCHED_TOKEN_ID, 0); + b_mock.set_esdt_balance( + &owner_address, + LAUNCHED_TOKEN_ID, + &(prev_owner_balance + rust_biguint!(USER_BALANCE)), + ); + b_mock.set_esdt_balance( + &first_user_address, + ACCEPTED_TOKEN_ID, + &rust_biguint!(USER_BALANCE), + ); + b_mock.set_esdt_balance( + &second_user_address, + ACCEPTED_TOKEN_ID, + &rust_biguint!(USER_BALANCE), + ); + + // set sc roles and initial minted SFTs (only needed for the purpose of SFT add quantity) + b_mock.set_esdt_local_roles( + pd_wrapper.address_ref(), + REDEEM_TOKEN_ID, + &[EsdtLocalRole::Mint, EsdtLocalRole::Burn], + ); + + b_mock.set_block_timestamp(START_TIME - 1); + + // init Price Discovery SC + b_mock + .execute_tx(&owner_address, &pd_wrapper, &rust_zero, |sc| { + sc.init( + managed_token_id!(LAUNCHED_TOKEN_ID), + managed_token_id_wrapped!(ACCEPTED_TOKEN_ID), + 18, + managed_biguint!(MIN_LAUNCHED_TOKENS), + START_TIME, + USER_DEPOSIT_TIME, + OWNER_DEPOSIT_TIME, + managed_address!(&owner_address), + ); + + sc.redeem_token() + .set_token_id(managed_token_id!(REDEEM_TOKEN_ID)); + }) + .assert_ok(); + + PriceDiscSetup { + b_mock, + owner_address, + first_user_address, + second_user_address, + pd_wrapper, + } + } - sc.redeem_token() - .set_token_id(managed_token_id!(REDEEM_TOKEN_ID)); - }) - .assert_ok(); + pub fn call_user_deposit(&mut self, user: &Address, amount: u64) -> TxResult { + self.b_mock.execute_esdt_transfer( + user, + &self.pd_wrapper, + ACCEPTED_TOKEN_ID, + 0, + &rust_biguint!(amount), + |sc| { + sc.user_deposit(); + }, + ) + } - PriceDiscSetup { - blockchain_wrapper, - owner_address, - first_user_address, - second_user_address, - pd_wrapper, - locking_sc_address: locking_sc_wrapper.address_ref().clone(), + pub fn call_user_withdraw(&mut self, user: &Address, amount: u64) -> TxResult { + self.b_mock.execute_esdt_transfer( + user, + &self.pd_wrapper, + REDEEM_TOKEN_ID, + 0, + &rust_biguint!(amount), + |sc| { + sc.user_withdraw(); + }, + ) } -} -pub fn call_deposit_initial_tokens( - pd_setup: &mut PriceDiscSetup, - amount: &num_bigint::BigUint, -) where - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, -{ - let b_wrapper = &mut pd_setup.blockchain_wrapper; - b_wrapper - .execute_esdt_transfer( - &pd_setup.owner_address, - &pd_setup.pd_wrapper, + pub fn call_owner_deposit(&mut self, amount: u64) -> TxResult { + self.b_mock.execute_esdt_transfer( + &self.owner_address, + &self.pd_wrapper, LAUNCHED_TOKEN_ID, 0, - amount, + &rust_biguint!(amount), |sc| { - sc.deposit(); + sc.owner_deposit(); }, ) - .assert_ok(); -} + } -pub fn call_deposit( - pd_setup: &mut PriceDiscSetup, - caller: &Address, - amount: &num_bigint::BigUint, -) -> TxResult -where - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, -{ - let b_wrapper = &mut pd_setup.blockchain_wrapper; - b_wrapper.execute_esdt_transfer( - caller, - &pd_setup.pd_wrapper, - ACCEPTED_TOKEN_ID, - 0, - amount, - |sc| { - sc.deposit(); - }, - ) -} + pub fn call_owner_withdraw(&mut self, amount: u64) -> TxResult { + self.b_mock.execute_tx( + &self.owner_address, + &self.pd_wrapper, + &rust_biguint!(0), + |sc| { + sc.owner_withdraw(managed_biguint!(amount)); + }, + ) + } -pub fn call_withdraw( - pd_setup: &mut PriceDiscSetup, - caller: &Address, - amount: &num_bigint::BigUint, -) -> TxResult -where - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, -{ - let b_wrapper = &mut pd_setup.blockchain_wrapper; - b_wrapper.execute_esdt_transfer( - caller, - &pd_setup.pd_wrapper, - REDEEM_TOKEN_ID, - ACCEPTED_TOKEN_REDEEM_NONCE, - amount, - |sc| { - let _ = sc.withdraw(); - }, - ) -} + pub fn call_user_redeem(&mut self, user: &Address, amount: u64) -> TxResult { + self.b_mock.execute_esdt_transfer( + user, + &self.pd_wrapper, + REDEEM_TOKEN_ID, + 0, + &rust_biguint!(amount), + |sc| { + sc.redeem(); + }, + ) + } -pub fn call_redeem( - pd_setup: &mut PriceDiscSetup, - caller: &Address, - sft_nonce: u64, - amount: &num_bigint::BigUint, -) -> TxResult -where - PriceDiscObjBuilder: 'static + Copy + Fn() -> price_discovery::ContractObj, -{ - let b_wrapper = &mut pd_setup.blockchain_wrapper; - b_wrapper.execute_esdt_transfer( - caller, - &pd_setup.pd_wrapper, - REDEEM_TOKEN_ID, - sft_nonce, - amount, - |sc| { - sc.redeem(); - }, - ) + pub fn call_owner_redeem(&mut self) -> TxResult { + self.b_mock.execute_tx( + &self.owner_address, + &self.pd_wrapper, + &rust_biguint!(0), + |sc| { + sc.redeem(); + }, + ) + } } diff --git a/dex/price-discovery/wasm/Cargo.lock b/dex/price-discovery/wasm/Cargo.lock index 4fbe6c51b..a12f6e00d 100644 --- a/dex/price-discovery/wasm/Cargo.lock +++ b/dex/price-discovery/wasm/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "arrayvec" @@ -20,53 +20,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -[[package]] -name = "common_errors" -version = "0.0.0" -dependencies = [ - "multiversx-sc", -] - -[[package]] -name = "common_structs" -version = "0.0.0" -dependencies = [ - "fixed-supply-token", - "math", - "mergeable", - "multiversx-sc", - "unwrappable", -] - [[package]] name = "endian-type" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" -[[package]] -name = "energy-factory" -version = "0.0.0" -dependencies = [ - "common_structs", - "legacy_token_decode_module", - "math", - "mergeable", - "multiversx-sc", - "multiversx-sc-modules", - "sc_whitelist_module", - "simple-lock", - "unwrappable", - "utils", -] - -[[package]] -name = "fixed-supply-token" -version = "0.0.0" -dependencies = [ - "multiversx-sc", -] - [[package]] name = "hex" version = "0.4.3" @@ -85,38 +44,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" -[[package]] -name = "legacy_token_decode_module" -version = "0.0.0" -dependencies = [ - "common_structs", - "multiversx-sc", - "utils", -] - -[[package]] -name = "locking_module" -version = "0.0.0" -dependencies = [ - "energy-factory", - "multiversx-sc", - "simple-lock", -] - -[[package]] -name = "math" -version = "0.0.0" -dependencies = [ - "multiversx-sc", -] - -[[package]] -name = "mergeable" -version = "0.0.0" -dependencies = [ - "multiversx-sc", -] - [[package]] name = "multiversx-sc" version = "0.53.2" @@ -208,7 +135,6 @@ name = "price-discovery" version = "0.0.0" dependencies = [ "hex-literal 0.3.4", - "locking_module", "multiversx-sc", "multiversx-sc-modules", ] @@ -249,23 +175,6 @@ dependencies = [ "nibble_vec", ] -[[package]] -name = "sc_whitelist_module" -version = "0.0.0" -dependencies = [ - "common_errors", - "multiversx-sc", -] - -[[package]] -name = "simple-lock" -version = "0.0.0" -dependencies = [ - "common_structs", - "multiversx-sc", - "multiversx-sc-modules", -] - [[package]] name = "smallvec" version = "1.13.2" @@ -294,20 +203,3 @@ name = "unwrap-infallible" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151ac09978d3c2862c4e39b557f4eceee2cc72150bc4cb4f16abf061b6e381fb" - -[[package]] -name = "unwrappable" -version = "0.0.0" -dependencies = [ - "multiversx-sc", -] - -[[package]] -name = "utils" -version = "0.0.0" -dependencies = [ - "common_structs", - "fixed-supply-token", - "mergeable", - "multiversx-sc", -] diff --git a/dex/price-discovery/wasm/src/lib.rs b/dex/price-discovery/wasm/src/lib.rs index 5e55db7a5..f7476cb48 100644 --- a/dex/price-discovery/wasm/src/lib.rs +++ b/dex/price-discovery/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 27 +// Endpoints: 12 // Async Callback: 1 -// Total number of exported functions: 30 +// Total number of exported functions: 15 #![no_std] @@ -20,33 +20,18 @@ multiversx_sc_wasm_adapter::endpoints! { ( init => init upgrade => upgrade - deposit => deposit - withdraw => withdraw - redeem => redeem - getCurrentPrice => calculate_price - getMinLaunchedTokenPrice => min_launched_token_price - getPricePrecision => price_precision - getLaunchedTokenId => launched_token_id - getAcceptedTokenId => accepted_token_id - getLaunchedTokenBalance => launched_token_balance - getAcceptedTokenBalance => accepted_token_balance - getStartBlock => start_block - getEndBlock => end_block - setLockingScAddress => set_locking_sc_address - setUnlockEpoch => set_unlock_epoch - getLockingScAddress => locking_sc_address - getUnlockEpoch => unlock_epoch getCurrentPhase => get_current_phase - getNoLimitPhaseDurationBlocks => no_limit_phase_duration_blocks - getLinearPenaltyPhaseDurationBlocks => linear_penalty_phase_duration_blocks - getFixedPenaltyPhaseDurationBlocks => fixed_penalty_phase_duration_blocks - getPenaltyMinPercentage => penalty_min_percentage - getPenaltyMaxPercentage => penalty_max_percentage - getFixedPenaltyPercentage => fixed_penalty_percentage + getUserDepositWithdrawTime => user_deposit_withdraw_time + getOwnerDepositWithdrawTime => owner_deposit_withdraw_time issueRedeemToken => issue_redeem_token - createInitialRedeemTokens => create_initial_redeem_tokens getRedeemTokenId => redeem_token getRedeemTokenTotalCirculatingSupply => redeem_token_total_circulating_supply + userDeposit => user_deposit + userWithdraw => user_withdraw + ownerDeposit => owner_deposit + ownerWithdraw => owner_withdraw + redeem => redeem + getCurrentPrice => get_current_price ) }