diff --git a/Cargo.lock b/Cargo.lock index 80c2e49ba0..01cbbe4953 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4377,7 +4377,7 @@ dependencies = [ [[package]] name = "framenode" -version = "3.0.1" +version = "3.1.0" dependencies = [ "ansi_term", "assert_cmd", @@ -4476,7 +4476,7 @@ dependencies = [ [[package]] name = "framenode-chain-spec" -version = "3.0.1" +version = "3.1.0" dependencies = [ "common", "faucet", @@ -4506,7 +4506,7 @@ dependencies = [ [[package]] name = "framenode-runtime" -version = "3.0.1" +version = "3.1.0" dependencies = [ "assets", "assets-runtime-api", diff --git a/node/Cargo.toml b/node/Cargo.toml index 1da40d58b7..6108db9b08 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framenode" -version = "3.0.1" +version = "3.1.0" authors = ["Parity Technologies "] build = "build.rs" edition = "2021" diff --git a/node/chain_spec/Cargo.toml b/node/chain_spec/Cargo.toml index 211f568dcf..c69582bbc0 100644 --- a/node/chain_spec/Cargo.toml +++ b/node/chain_spec/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framenode-chain-spec" -version = "3.0.1" +version = "3.1.0" authors = ["Parity Technologies "] edition = "2021" diff --git a/pallets/assets/src/benchmarking.rs b/pallets/assets/src/benchmarking.rs index 48de99eaf7..e32b70e267 100644 --- a/pallets/assets/src/benchmarking.rs +++ b/pallets/assets/src/benchmarking.rs @@ -59,8 +59,7 @@ fn bob() -> T::AccountId { fn add_assets(n: u32) -> Result<(), &'static str> { let owner = alice::(); frame_system::Pallet::::inc_providers(&owner); - let owner_origin: ::RuntimeOrigin = - RawOrigin::Signed(owner.clone()).into(); + let owner_origin: ::RuntimeOrigin = RawOrigin::Signed(owner).into(); for _i in 0..n { Assets::::register( owner_origin.clone(), @@ -174,7 +173,7 @@ benchmarks! { }: _( RawOrigin::Root, USDT.into(), - caller.clone(), + caller, 100_u32.into() ) verify { @@ -229,9 +228,9 @@ benchmarks! { ).unwrap(); }: _( RawOrigin::Root, - caller.clone(), + caller, USDT.into(), - 100_i128.into() + 100_i128 ) verify { let usdt_issuance = Assets::::total_issuance(&USDT.into())?; @@ -266,7 +265,7 @@ benchmarks! { let caller = alice::(); frame_system::Pallet::::inc_providers(&caller); Assets::::register_asset_id( - caller.clone(), + caller, USDT.into(), AssetSymbol(b"USDT".to_vec()), AssetName(b"USDT".to_vec()), diff --git a/pallets/assets/src/lib.rs b/pallets/assets/src/lib.rs index 179d635cd6..697f821bea 100644 --- a/pallets/assets/src/lib.rs +++ b/pallets/assets/src/lib.rs @@ -41,12 +41,6 @@ //! - `register` - registers new asset by a given ID. #![cfg_attr(not(feature = "std"), no_std)] -// TODO #167: fix clippy warnings -#![allow(clippy::all)] - -#[allow(unused_imports)] -#[macro_use] -extern crate alloc; pub mod weights; @@ -179,6 +173,7 @@ impl GetTotalBalance for () { pub use pallet::*; #[frame_support::pallet] +#[allow(clippy::too_many_arguments)] pub mod pallet { use super::*; use common::{ContentSource, Description}; @@ -354,7 +349,7 @@ pub mod pallet { let issuer = ensure_signed(origin.clone())?; Self::mint_to(&asset_id, &issuer, &to, amount)?; - Self::deposit_event(Event::Mint(issuer, to, asset_id.clone(), amount)); + Self::deposit_event(Event::Mint(issuer, to, asset_id, amount)); Ok(().into()) } @@ -412,7 +407,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let issuer = ensure_signed(origin.clone())?; Self::burn_from(&asset_id, &issuer, &issuer, amount)?; - Self::deposit_event(Event::Burn(issuer, asset_id.clone(), amount)); + Self::deposit_event(Event::Burn(issuer, asset_id, amount)); Ok(().into()) } @@ -448,7 +443,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin.clone())?; Self::set_non_mintable_from(&asset_id, &who)?; - Self::deposit_event(Event::AssetSetNonMintable(asset_id.clone())); + Self::deposit_event(Event::AssetSetNonMintable(asset_id)); Ok(().into()) } @@ -468,7 +463,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_root(origin)?; Self::ensure_asset_exists(&asset_id)?; - AssetInfos::::mutate(&asset_id, |(ref mut symbol, ref mut name, ..)| { + AssetInfos::::mutate(asset_id, |(ref mut symbol, ref mut name, ..)| { if let Some(new_name) = new_name.clone() { ensure!(new_name.is_valid(), Error::::InvalidAssetName); *name = new_name; @@ -561,6 +556,7 @@ pub mod pallet { pub type AssetRecordAssetId = StorageMap<_, Twox64Concat, T::AssetId, AssetRecord>; + #[allow(clippy::type_complexity)] #[pallet::genesis_config] pub struct GenesisConfig { pub endowed_assets: Vec<( @@ -650,7 +646,7 @@ impl Pallet { let mut keccak = Keccak::v256(); keccak.update(b"Sora Asset Id"); keccak.update(&account_id.encode()); - keccak.update(&frame_system::Pallet::::account_nonce(&account_id).encode()); + keccak.update(&frame_system::Pallet::::account_nonce(account_id).encode()); let mut output = [0u8; 32]; keccak.finalize(&mut output); // More safe to escape. @@ -659,6 +655,7 @@ impl Pallet { } /// Register the given `AssetId`. + #[allow(clippy::too_many_arguments)] pub fn register_asset_id( account_id: T::AccountId, asset_id: T::AssetId, @@ -716,7 +713,7 @@ impl Pallet { } if !initial_supply.is_zero() { - T::Currency::deposit(asset_id.clone(), &account_id, initial_supply)?; + T::Currency::deposit(asset_id, &account_id, initial_supply)?; } frame_system::Pallet::::inc_account_nonce(&account_id); @@ -725,6 +722,7 @@ impl Pallet { } /// Generates new `AssetId` and registers it from the `account_id`. + #[allow(clippy::too_many_arguments)] pub fn register_from( account_id: &T::AccountId, symbol: AssetSymbol, @@ -785,7 +783,7 @@ impl Pallet { to: &T::AccountId, amount: Balance, ) -> DispatchResult { - let r = T::Currency::transfer(asset_id.clone(), from, to, amount); + let r = T::Currency::transfer(*asset_id, from, to, amount); if r.is_err() { Self::ensure_asset_exists(asset_id)?; } @@ -812,7 +810,7 @@ impl Pallet { to: &T::AccountId, amount: Balance, ) -> DispatchResult { - T::Currency::deposit(asset_id.clone(), to, amount) + T::Currency::deposit(*asset_id, to, amount) } pub fn burn_from( @@ -836,7 +834,7 @@ impl Pallet { ) -> DispatchResult { let r = T::Currency::withdraw(*asset_id, from, amount); if r.is_err() { - Self::ensure_asset_exists(&asset_id)?; + Self::ensure_asset_exists(asset_id)?; } r } @@ -846,7 +844,7 @@ impl Pallet { let technical_account = T::GetBuyBackAccountId::get(); let buy_back_asset_id = T::GetBuyBackAssetId::get(); - Self::mint_unchecked(&asset_id, &technical_account, amount)?; + Self::mint_unchecked(asset_id, &technical_account, amount)?; let outcome = T::BuyBackLiquidityProxy::exchange( dex_id, &technical_account, @@ -874,7 +872,7 @@ impl Pallet { if by_amount.is_positive() { Self::ensure_asset_is_mintable(asset_id)?; } - T::Currency::update_balance(asset_id.clone(), who, by_amount) + T::Currency::update_balance(*asset_id, who, by_amount) } pub fn can_reserve(asset_id: T::AssetId, who: &T::AccountId, amount: Balance) -> bool { @@ -884,7 +882,7 @@ impl Pallet { pub fn reserve(asset_id: &T::AssetId, who: &T::AccountId, amount: Balance) -> DispatchResult { let r = T::Currency::reserve(*asset_id, who, amount); if r.is_err() { - Self::ensure_asset_exists(&asset_id)?; + Self::ensure_asset_exists(asset_id)?; } r } @@ -896,7 +894,7 @@ impl Pallet { ) -> Result { let amount = T::Currency::unreserve(*asset_id, who, amount); if amount != Default::default() { - Self::ensure_asset_exists(&asset_id)?; + Self::ensure_asset_exists(asset_id)?; } Ok(amount) } @@ -917,6 +915,7 @@ impl Pallet { AssetInfos::::iter().map(|(key, _)| key).collect() } + #[allow(clippy::type_complexity)] pub fn list_registered_asset_infos() -> Vec<( T::AssetId, AssetSymbol, @@ -1010,7 +1009,7 @@ impl } fn total_issuance(asset_id: &T::AssetId) -> Result { - let r = T::Currency::total_issuance(asset_id.clone()); + let r = T::Currency::total_issuance(*asset_id); if r == Default::default() { Self::ensure_asset_exists(asset_id)?; } @@ -1018,7 +1017,7 @@ impl } fn total_balance(asset_id: &T::AssetId, who: &T::AccountId) -> Result { - let r = T::Currency::total_balance(asset_id.clone(), who); + let r = T::Currency::total_balance(*asset_id, who); if r == Default::default() { Self::ensure_asset_exists(asset_id)?; } @@ -1026,7 +1025,7 @@ impl } fn free_balance(asset_id: &T::AssetId, who: &T::AccountId) -> Result { - let r = T::Currency::free_balance(asset_id.clone(), who); + let r = T::Currency::free_balance(*asset_id, who); if r == Default::default() { Self::ensure_asset_exists(asset_id)?; } @@ -1038,7 +1037,7 @@ impl who: &T::AccountId, amount: Balance, ) -> DispatchResult { - let r = T::Currency::ensure_can_withdraw(asset_id.clone(), who, amount); + let r = T::Currency::ensure_can_withdraw(*asset_id, who, amount); if r.is_err() { Self::ensure_asset_exists(asset_id)?; } diff --git a/pallets/assets/src/mock.rs b/pallets/assets/src/mock.rs index 3bc64c8926..65d0fba7e6 100644 --- a/pallets/assets/src/mock.rs +++ b/pallets/assets/src/mock.rs @@ -36,7 +36,6 @@ use currencies::BasicCurrencyAdapter; use frame_support::traits::{Everything, GenesisBuild}; use frame_support::weights::Weight; use frame_support::{construct_runtime, parameter_types}; -use frame_system; use sp_core::H256; use sp_runtime::testing::Header; use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; diff --git a/pallets/assets/src/tests.rs b/pallets/assets/src/tests.rs index 5e58a7facf..63704bf9e6 100644 --- a/pallets/assets/src/tests.rs +++ b/pallets/assets/src/tests.rs @@ -28,61 +28,90 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod tests { - - use crate::mock::*; - use crate::Error; - use crate::Event; - use common::balance; - use common::prelude::{AssetName, AssetSymbol, Balance}; - use common::test_utils::assert_last_event; - use common::DAI; - use common::PSWAP; - use common::XST; - use common::{ - AssetId32, AssetInfoProvider, ContentSource, Description, IsValid, - ASSET_CONTENT_SOURCE_MAX_LENGTH, ASSET_DESCRIPTION_MAX_LENGTH, DEFAULT_BALANCE_PRECISION, - DOT, VAL, XOR, - }; - use frame_support::assert_noop; - use frame_support::error::BadOrigin; - use frame_support::{assert_err, assert_ok}; - use hex_literal::hex; - use sp_runtime::traits::Zero; - - #[test] - fn should_gen_and_register_asset() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - let next_asset_id = Assets::gen_asset_id(&ALICE); - assert_eq!( - next_asset_id, - AssetId32::from_bytes(hex!( - "00770dfe3392f9bb8ab977ce23d11c92e25140c39a9d8115714168d6e484ea41" - )) - ); - assert!(Assets::ensure_asset_exists(&next_asset_id).is_err()); - assert_ok!(Assets::register( - RuntimeOrigin::signed(ALICE), - AssetSymbol(b"ALIC".to_vec()), - AssetName(b"ALICE".to_vec()), - Balance::zero(), - true, - false, - None, - None, - )); - assert_ok!(Assets::ensure_asset_exists(&next_asset_id)); - assert_ne!(Assets::gen_asset_id(&ALICE), next_asset_id); - }); - } - - #[test] - fn should_register_asset() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert!(Assets::ensure_asset_exists(&XOR).is_err()); - assert_ok!(Assets::register_asset_id( +use crate::mock::*; +use crate::Error; +use crate::Event; +use common::balance; +use common::prelude::{AssetName, AssetSymbol, Balance}; +use common::test_utils::assert_last_event; +use common::DAI; +use common::PSWAP; +use common::XST; +use common::{ + AssetId32, AssetInfoProvider, ContentSource, Description, IsValid, + ASSET_CONTENT_SOURCE_MAX_LENGTH, ASSET_DESCRIPTION_MAX_LENGTH, DEFAULT_BALANCE_PRECISION, DOT, + VAL, XOR, +}; +use frame_support::assert_noop; +use frame_support::error::BadOrigin; +use frame_support::{assert_err, assert_ok}; +use hex_literal::hex; +use sp_runtime::traits::Zero; + +#[test] +fn should_gen_and_register_asset() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + let next_asset_id = Assets::gen_asset_id(&ALICE); + assert_eq!( + next_asset_id, + AssetId32::from_bytes(hex!( + "00770dfe3392f9bb8ab977ce23d11c92e25140c39a9d8115714168d6e484ea41" + )) + ); + assert!(Assets::ensure_asset_exists(&next_asset_id).is_err()); + assert_ok!(Assets::register( + RuntimeOrigin::signed(ALICE), + AssetSymbol(b"ALIC".to_vec()), + AssetName(b"ALICE".to_vec()), + Balance::zero(), + true, + false, + None, + None, + )); + assert_ok!(Assets::ensure_asset_exists(&next_asset_id)); + assert_ne!(Assets::gen_asset_id(&ALICE), next_asset_id); + }); +} + +#[test] +fn should_register_asset() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert!(Assets::ensure_asset_exists(&XOR).is_err()); + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::zero(), + true, + None, + None, + )); + assert_ok!(Assets::ensure_asset_exists(&XOR)); + }); +} + +#[test] +fn should_not_register_duplicated_asset() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::zero(), + true, + None, + None, + )); + assert_noop!( + Assets::register_asset_id( ALICE, XOR, AssetSymbol(b"XOR".to_vec()), @@ -92,577 +121,566 @@ mod tests { true, None, None, - )); - assert_ok!(Assets::ensure_asset_exists(&XOR)); - }); - } - - #[test] - fn should_not_register_duplicated_asset() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( + ), + Error::::AssetIdAlreadyExists + ); + }); +} + +#[test] +fn should_not_register_invalid_asset_name() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_err!( + Assets::register_asset_id( ALICE, XOR, AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), + AssetName(b"This is a name with length over thirty three".to_vec()), DEFAULT_BALANCE_PRECISION, Balance::zero(), true, None, None, - )); - assert_noop!( - Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::zero(), - true, - None, - None, - ), - Error::::AssetIdAlreadyExists - ); - }); - } - - #[test] - fn should_not_register_invalid_asset_name() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_err!( - Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"This is a name with length over thirty three".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::zero(), - true, - None, - None, - ), - Error::::InvalidAssetName - ); - - assert_err!( - Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::zero(), - true, - None, - None, - ), - Error::::InvalidAssetName - ); - - assert_err!( - Assets::register_asset_id( - ALICE, - VAL, - AssetSymbol(b"VAL".to_vec()), - AssetName(b"This is a name with $ymbols".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::zero(), - true, - None, - None, - ), - Error::::InvalidAssetName - ); - - assert_err!( - Assets::register_asset_id( - ALICE, - DOT, - AssetSymbol(b"DOT".to_vec()), - AssetName(b"This is a name with _".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::zero(), - true, - None, - None, - ), - Error::::InvalidAssetName - ); - }); - } - - #[test] - fn should_not_register_invalid_asset_symbol() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_err!( - Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"xor".to_vec()), - AssetName(b"Super Sora".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::zero(), - true, - None, - None, - ), - Error::::InvalidAssetSymbol - ); - - assert_err!( - Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"".to_vec()), - AssetName(b"Super Sora".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::zero(), - true, - None, - None, - ), - Error::::InvalidAssetSymbol - ); - - assert_err!( - Assets::register_asset_id( - ALICE, - VAL, - AssetSymbol(b"VAL IS SUPER LONG".to_vec()), - AssetName(b"Validator".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::zero(), - true, - None, - None, - ), - Error::::InvalidAssetSymbol - ); - - assert_err!( - Assets::register_asset_id( - ALICE, - DOT, - AssetSymbol(b"D_OT".to_vec()), - AssetName(b"Bad Symbol".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::zero(), - true, - None, - None, - ), - Error::::InvalidAssetSymbol - ); - }); - } - - #[test] - fn should_allow_operation() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::zero(), - true, - None, - None, - )); - assert_ok!(Assets::mint_to(&XOR, &ALICE, &ALICE, 100u32.into())); - assert_ok!(Assets::burn_from(&XOR, &ALICE, &ALICE, 100u32.into())); - assert_ok!(Assets::update_own_balance(&XOR, &ALICE, 100.into())); - }); - } - - #[test] - fn should_not_allow_operation() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( + ), + Error::::InvalidAssetName + ); + + assert_err!( + Assets::register_asset_id( ALICE, XOR, AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), + AssetName(b"".to_vec()), DEFAULT_BALANCE_PRECISION, Balance::zero(), true, None, None, - )); - assert_noop!( - Assets::mint_to(&XOR, &BOB, &BOB, 100u32.into()), - permissions::Error::::Forbidden - ); - assert_noop!( - Assets::update_own_balance(&XOR, &BOB, 100u32.into()), - permissions::Error::::Forbidden - ); - }); - } - - #[test] - fn should_check_symbols_correctly() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert!(AssetSymbol(b"XOR".to_vec()).is_valid()); - assert!(AssetSymbol(b"DOT".to_vec()).is_valid()); - assert!(AssetSymbol(b"KSM".to_vec()).is_valid()); - assert!(AssetSymbol(b"USDT".to_vec()).is_valid()); - assert!(AssetSymbol(b"VAL".to_vec()).is_valid()); - assert!(AssetSymbol(b"PSWAP".to_vec()).is_valid()); - assert!(AssetSymbol(b"GT".to_vec()).is_valid()); - assert!(AssetSymbol(b"BP".to_vec()).is_valid()); - assert!(AssetSymbol(b"AB1".to_vec()).is_valid()); - - assert!(!AssetSymbol(b"ABCDEFGH".to_vec()).is_valid()); - assert!(!AssetSymbol(b"xor".to_vec()).is_valid()); - assert!(!AssetSymbol(b"\xF0\x9F\x98\xBF".to_vec()).is_valid()); - }) - } - - #[test] - fn should_check_names_correctly() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert!(AssetName(b"XOR".to_vec()).is_valid()); - assert!(AssetName(b"DOT".to_vec()).is_valid()); - assert!(AssetName(b"KSM".to_vec()).is_valid()); - assert!(AssetName(b"USDT".to_vec()).is_valid()); - assert!(AssetName(b"VAL".to_vec()).is_valid()); - assert!(AssetName(b"PSWAP".to_vec()).is_valid()); - assert!(AssetName(b"GT".to_vec()).is_valid()); - assert!(AssetName(b"BP".to_vec()).is_valid()); - assert!(AssetName(b"SORA Validator Token".to_vec()).is_valid()); - assert!(AssetName(b"AB1".to_vec()).is_valid()); - - assert!( - !AssetName(b"This is a name with length over thirty three".to_vec()).is_valid() - ); - assert!(!AssetName(b"AB1_".to_vec()).is_valid()); - assert!(!AssetName(b"\xF0\x9F\x98\xBF".to_vec()).is_valid()); - }) - } - - #[test] - fn should_mint_initial_supply_for_owner() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::from(123u32), - true, - None, - None, - )); - assert_eq!( - Assets::free_balance(&XOR, &ALICE).expect("Failed to query free balance."), - Balance::from(123u32), - ); - assert_ok!(Assets::register_asset_id( + ), + Error::::InvalidAssetName + ); + + assert_err!( + Assets::register_asset_id( ALICE, VAL, AssetSymbol(b"VAL".to_vec()), - AssetName(b"SORA Validator Token".to_vec()), + AssetName(b"This is a name with $ymbols".to_vec()), DEFAULT_BALANCE_PRECISION, - Balance::from(321u32), - false, - None, - None, - )); - assert_eq!( - Assets::free_balance(&VAL, &ALICE).expect("Failed to query free balance."), - Balance::from(321u32), - ); - }) - } - - #[test] - fn should_not_allow_dead_asset() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_eq!( - Assets::register_asset_id( - ALICE, - DOT, - AssetSymbol(b"DOT".to_vec()), - AssetName(b"Polkadot".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::from(0u32), - false, - None, - None, - ), - Err(Error::::DeadAsset.into()) - ); - }) - } - - #[test] - fn should_fail_with_non_mintable_asset_supply() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::from(10u32), - false, + Balance::zero(), + true, None, None, - )); - assert_noop!( - Assets::mint_to(&XOR, &ALICE, &ALICE, Balance::from(10u32)), - Error::::AssetSupplyIsNotMintable - ); - assert_noop!( - Assets::mint_to(&XOR, &ALICE, &BOB, Balance::from(10u32)), - Error::::AssetSupplyIsNotMintable - ); - assert_noop!( - Assets::update_own_balance(&XOR, &ALICE, 1i128), - Error::::AssetSupplyIsNotMintable - ); - assert_ok!(Assets::update_own_balance(&XOR, &ALICE, 0i128),); - assert_ok!(Assets::update_own_balance(&XOR, &ALICE, -1i128),); - }) - } - - #[test] - fn should_mint_for_mintable_asset() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( + ), + Error::::InvalidAssetName + ); + + assert_err!( + Assets::register_asset_id( ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), + DOT, + AssetSymbol(b"DOT".to_vec()), + AssetName(b"This is a name with _".to_vec()), DEFAULT_BALANCE_PRECISION, - Balance::from(10u32), + Balance::zero(), true, None, None, - )); - assert_ok!(Assets::mint_to(&XOR, &ALICE, &ALICE, Balance::from(10u32)),); - assert_ok!(Assets::mint_to(&XOR, &ALICE, &BOB, Balance::from(10u32)),); - assert_ok!(Assets::update_own_balance(&XOR, &ALICE, 1i128),); - assert_ok!(Assets::update_own_balance(&XOR, &ALICE, 0i128),); - assert_ok!(Assets::update_own_balance(&XOR, &ALICE, -1i128),); - - assert_noop!( - Assets::set_non_mintable_from(&XOR, &BOB), - Error::::InvalidAssetOwner - ); - assert_ok!(Assets::set_non_mintable_from(&XOR, &ALICE)); - - assert_noop!( - Assets::mint_to(&XOR, &ALICE, &ALICE, Balance::from(10u32)), - Error::::AssetSupplyIsNotMintable - ); - assert_noop!( - Assets::mint_to(&XOR, &ALICE, &BOB, Balance::from(10u32)), - Error::::AssetSupplyIsNotMintable - ); - assert_noop!( - Assets::update_own_balance(&XOR, &ALICE, 1i128), - Error::::AssetSupplyIsNotMintable - ); - assert_ok!(Assets::update_own_balance(&XOR, &ALICE, 0i128),); - assert_ok!(Assets::update_own_balance(&XOR, &ALICE, -1i128),); - }) - } - - #[test] - fn should_not_allow_duplicate_set_non_mintable() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( + ), + Error::::InvalidAssetName + ); + }); +} + +#[test] +fn should_not_register_invalid_asset_symbol() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_err!( + Assets::register_asset_id( ALICE, XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), + AssetSymbol(b"xor".to_vec()), + AssetName(b"Super Sora".to_vec()), DEFAULT_BALANCE_PRECISION, - Balance::from(10u32), + Balance::zero(), true, None, None, - )); - assert_ok!(Assets::set_non_mintable_from(&XOR, &ALICE)); - assert_noop!( - Assets::set_non_mintable_from(&XOR, &ALICE), - Error::::AssetSupplyIsNotMintable - ); - }) - } - - #[test] - fn should_burn_from() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( + ), + Error::::InvalidAssetSymbol + ); + + assert_err!( + Assets::register_asset_id( ALICE, XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), + AssetSymbol(b"".to_vec()), + AssetName(b"Super Sora".to_vec()), DEFAULT_BALANCE_PRECISION, - Balance::from(10u32), + Balance::zero(), true, None, None, - )); - assert_eq!( - Assets::free_balance(&XOR, &ALICE).expect("Failed to query free balance."), - Balance::from(10u32), - ); - assert_ok!(Assets::burn_from( - &XOR, - &ALICE, - &ALICE, - Balance::from(10u32) - )); - assert_eq!( - Assets::free_balance(&XOR, &ALICE).expect("Failed to query free balance."), - Balance::from(0u32), - ); - }) - } - - #[test] - fn should_not_allow_burn_from_due_to_permissions() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( + ), + Error::::InvalidAssetSymbol + ); + + assert_err!( + Assets::register_asset_id( ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), + VAL, + AssetSymbol(b"VAL IS SUPER LONG".to_vec()), + AssetName(b"Validator".to_vec()), DEFAULT_BALANCE_PRECISION, - Balance::from(10u32), + Balance::zero(), true, None, None, - )); - assert_noop!( - Assets::burn_from(&XOR, &BOB, &ALICE, Balance::from(10u32)), - permissions::Error::::Forbidden - ); - }) - } - - #[test] - fn should_allow_burn_from_self_without_a_permissions() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( + ), + Error::::InvalidAssetSymbol + ); + + assert_err!( + Assets::register_asset_id( ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), + DOT, + AssetSymbol(b"D_OT".to_vec()), + AssetName(b"Bad Symbol".to_vec()), DEFAULT_BALANCE_PRECISION, - Balance::from(10u32), + Balance::zero(), true, None, None, - )); - assert_ok!(Assets::mint_to(&XOR, &ALICE, &BOB, Balance::from(10u32))); - assert_eq!( - Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), - Balance::from(10u32) - ); - assert_ok!(Assets::burn_from(&XOR, &BOB, &BOB, Balance::from(10u32))); - assert_eq!( - Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), - Balance::from(0u32) - ); - }) - } - - #[test] - fn should_update_balance_correctly() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( + ), + Error::::InvalidAssetSymbol + ); + }); +} + +#[test] +fn should_allow_operation() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::zero(), + true, + None, + None, + )); + assert_ok!(Assets::mint_to(&XOR, &ALICE, &ALICE, 100u32.into())); + assert_ok!(Assets::burn_from(&XOR, &ALICE, &ALICE, 100u32.into())); + assert_ok!(Assets::update_own_balance(&XOR, &ALICE, 100.into())); + }); +} + +#[test] +fn should_not_allow_operation() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::zero(), + true, + None, + None, + )); + assert_noop!( + Assets::mint_to(&XOR, &BOB, &BOB, 100u32.into()), + permissions::Error::::Forbidden + ); + assert_noop!( + Assets::update_own_balance(&XOR, &BOB, 100u32.into()), + permissions::Error::::Forbidden + ); + }); +} + +#[test] +fn should_check_symbols_correctly() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert!(AssetSymbol(b"XOR".to_vec()).is_valid()); + assert!(AssetSymbol(b"DOT".to_vec()).is_valid()); + assert!(AssetSymbol(b"KSM".to_vec()).is_valid()); + assert!(AssetSymbol(b"USDT".to_vec()).is_valid()); + assert!(AssetSymbol(b"VAL".to_vec()).is_valid()); + assert!(AssetSymbol(b"PSWAP".to_vec()).is_valid()); + assert!(AssetSymbol(b"GT".to_vec()).is_valid()); + assert!(AssetSymbol(b"BP".to_vec()).is_valid()); + assert!(AssetSymbol(b"AB1".to_vec()).is_valid()); + + assert!(!AssetSymbol(b"ABCDEFGH".to_vec()).is_valid()); + assert!(!AssetSymbol(b"xor".to_vec()).is_valid()); + assert!(!AssetSymbol(b"\xF0\x9F\x98\xBF".to_vec()).is_valid()); + }) +} + +#[test] +fn should_check_names_correctly() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert!(AssetName(b"XOR".to_vec()).is_valid()); + assert!(AssetName(b"DOT".to_vec()).is_valid()); + assert!(AssetName(b"KSM".to_vec()).is_valid()); + assert!(AssetName(b"USDT".to_vec()).is_valid()); + assert!(AssetName(b"VAL".to_vec()).is_valid()); + assert!(AssetName(b"PSWAP".to_vec()).is_valid()); + assert!(AssetName(b"GT".to_vec()).is_valid()); + assert!(AssetName(b"BP".to_vec()).is_valid()); + assert!(AssetName(b"SORA Validator Token".to_vec()).is_valid()); + assert!(AssetName(b"AB1".to_vec()).is_valid()); + + assert!(!AssetName(b"This is a name with length over thirty three".to_vec()).is_valid()); + assert!(!AssetName(b"AB1_".to_vec()).is_valid()); + assert!(!AssetName(b"\xF0\x9F\x98\xBF".to_vec()).is_valid()); + }) +} + +#[test] +fn should_mint_initial_supply_for_owner() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(123u32), + true, + None, + None, + )); + assert_eq!( + Assets::free_balance(&XOR, &ALICE).expect("Failed to query free balance."), + Balance::from(123u32), + ); + assert_ok!(Assets::register_asset_id( + ALICE, + VAL, + AssetSymbol(b"VAL".to_vec()), + AssetName(b"SORA Validator Token".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(321u32), + false, + None, + None, + )); + assert_eq!( + Assets::free_balance(&VAL, &ALICE).expect("Failed to query free balance."), + Balance::from(321u32), + ); + }) +} + +#[test] +fn should_not_allow_dead_asset() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_eq!( + Assets::register_asset_id( ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), + DOT, + AssetSymbol(b"DOT".to_vec()), + AssetName(b"Polkadot".to_vec()), DEFAULT_BALANCE_PRECISION, - Balance::from(10u32), - true, - None, - None, - )); - assert_ok!(Assets::update_balance(RuntimeOrigin::root(), BOB, XOR, 100)); - assert_eq!( - Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), - Balance::from(100u32) - ); - - assert_ok!(Assets::update_balance(RuntimeOrigin::root(), BOB, XOR, -10)); - assert_eq!( - Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), - Balance::from(90u32) - ); - - assert_err!( - Assets::update_balance(RuntimeOrigin::signed(ALICE), BOB, XOR, -10), - BadOrigin - ); - assert_eq!( - Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), - Balance::from(90u32) - ); - - assert_noop!( - Assets::update_balance(RuntimeOrigin::root(), BOB, XOR, -100), - pallet_balances::Error::::InsufficientBalance - ); - assert_eq!( - Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), - Balance::from(90u32) - ); - }) - } - - #[test] - fn should_register_indivisible() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - let next_asset_id = Assets::gen_asset_id(&ALICE); - assert_ok!(Assets::register( - RuntimeOrigin::signed(ALICE), - AssetSymbol(b"ALIC".to_vec()), - AssetName(b"ALICE".to_vec()), - 5, - true, - true, + Balance::from(0u32), + false, None, None, - )); - let (_, _, precision, ..) = Assets::asset_infos(next_asset_id); - assert_eq!(precision, 0u8); - }) - } - - #[test] - fn should_associate_content_source() { - let mut ext = ExtBuilder::default().build(); - let content_src = ContentSource(b"https://imgur.com/gallery/24O4LUX".to_vec()); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( + ), + Err(Error::::DeadAsset.into()) + ); + }) +} + +#[test] +fn should_fail_with_non_mintable_asset_supply() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(10u32), + false, + None, + None, + )); + assert_noop!( + Assets::mint_to(&XOR, &ALICE, &ALICE, Balance::from(10u32)), + Error::::AssetSupplyIsNotMintable + ); + assert_noop!( + Assets::mint_to(&XOR, &ALICE, &BOB, Balance::from(10u32)), + Error::::AssetSupplyIsNotMintable + ); + assert_noop!( + Assets::update_own_balance(&XOR, &ALICE, 1i128), + Error::::AssetSupplyIsNotMintable + ); + assert_ok!(Assets::update_own_balance(&XOR, &ALICE, 0i128),); + assert_ok!(Assets::update_own_balance(&XOR, &ALICE, -1i128),); + }) +} + +#[test] +fn should_mint_for_mintable_asset() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(10u32), + true, + None, + None, + )); + assert_ok!(Assets::mint_to(&XOR, &ALICE, &ALICE, Balance::from(10u32)),); + assert_ok!(Assets::mint_to(&XOR, &ALICE, &BOB, Balance::from(10u32)),); + assert_ok!(Assets::update_own_balance(&XOR, &ALICE, 1i128),); + assert_ok!(Assets::update_own_balance(&XOR, &ALICE, 0i128),); + assert_ok!(Assets::update_own_balance(&XOR, &ALICE, -1i128),); + + assert_noop!( + Assets::set_non_mintable_from(&XOR, &BOB), + Error::::InvalidAssetOwner + ); + assert_ok!(Assets::set_non_mintable_from(&XOR, &ALICE)); + + assert_noop!( + Assets::mint_to(&XOR, &ALICE, &ALICE, Balance::from(10u32)), + Error::::AssetSupplyIsNotMintable + ); + assert_noop!( + Assets::mint_to(&XOR, &ALICE, &BOB, Balance::from(10u32)), + Error::::AssetSupplyIsNotMintable + ); + assert_noop!( + Assets::update_own_balance(&XOR, &ALICE, 1i128), + Error::::AssetSupplyIsNotMintable + ); + assert_ok!(Assets::update_own_balance(&XOR, &ALICE, 0i128),); + assert_ok!(Assets::update_own_balance(&XOR, &ALICE, -1i128),); + }) +} + +#[test] +fn should_not_allow_duplicate_set_non_mintable() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(10u32), + true, + None, + None, + )); + assert_ok!(Assets::set_non_mintable_from(&XOR, &ALICE)); + assert_noop!( + Assets::set_non_mintable_from(&XOR, &ALICE), + Error::::AssetSupplyIsNotMintable + ); + }) +} + +#[test] +fn should_burn_from() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(10u32), + true, + None, + None, + )); + assert_eq!( + Assets::free_balance(&XOR, &ALICE).expect("Failed to query free balance."), + Balance::from(10u32), + ); + assert_ok!(Assets::burn_from( + &XOR, + &ALICE, + &ALICE, + Balance::from(10u32) + )); + assert_eq!( + Assets::free_balance(&XOR, &ALICE).expect("Failed to query free balance."), + Balance::from(0u32), + ); + }) +} + +#[test] +fn should_not_allow_burn_from_due_to_permissions() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(10u32), + true, + None, + None, + )); + assert_noop!( + Assets::burn_from(&XOR, &BOB, &ALICE, Balance::from(10u32)), + permissions::Error::::Forbidden + ); + }) +} + +#[test] +fn should_allow_burn_from_self_without_a_permissions() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(10u32), + true, + None, + None, + )); + assert_ok!(Assets::mint_to(&XOR, &ALICE, &BOB, Balance::from(10u32))); + assert_eq!( + Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), + Balance::from(10u32) + ); + assert_ok!(Assets::burn_from(&XOR, &BOB, &BOB, Balance::from(10u32))); + assert_eq!( + Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), + Balance::from(0u32) + ); + }) +} + +#[test] +fn should_update_balance_correctly() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(10u32), + true, + None, + None, + )); + assert_ok!(Assets::update_balance(RuntimeOrigin::root(), BOB, XOR, 100)); + assert_eq!( + Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), + Balance::from(100u32) + ); + + assert_ok!(Assets::update_balance(RuntimeOrigin::root(), BOB, XOR, -10)); + assert_eq!( + Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), + Balance::from(90u32) + ); + + assert_err!( + Assets::update_balance(RuntimeOrigin::signed(ALICE), BOB, XOR, -10), + BadOrigin + ); + assert_eq!( + Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), + Balance::from(90u32) + ); + + assert_noop!( + Assets::update_balance(RuntimeOrigin::root(), BOB, XOR, -100), + pallet_balances::Error::::InsufficientBalance + ); + assert_eq!( + Assets::free_balance(&XOR, &BOB).expect("Failed to query free balance."), + Balance::from(90u32) + ); + }) +} + +#[test] +fn should_register_indivisible() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + let next_asset_id = Assets::gen_asset_id(&ALICE); + assert_ok!(Assets::register( + RuntimeOrigin::signed(ALICE), + AssetSymbol(b"ALIC".to_vec()), + AssetName(b"ALICE".to_vec()), + 5, + true, + true, + None, + None, + )); + let (_, _, precision, ..) = Assets::asset_infos(next_asset_id); + assert_eq!(precision, 0u8); + }) +} + +#[test] +fn should_associate_content_source() { + let mut ext = ExtBuilder::default().build(); + let content_src = ContentSource(b"https://imgur.com/gallery/24O4LUX".to_vec()); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(10u32), + true, + Some(content_src.clone()), + None, + )); + assert_eq!(Assets::get_asset_content_src(&XOR), Some(content_src)); + }) +} + +#[test] +fn should_fail_content_source() { + let mut ext = ExtBuilder::default().build(); + let source: Vec = vec![0; ASSET_CONTENT_SOURCE_MAX_LENGTH + 1]; + let content_src = ContentSource(source); + ext.execute_with(|| { + assert_err!( + Assets::register_asset_id( ALICE, XOR, AssetSymbol(b"XOR".to_vec()), @@ -672,40 +690,40 @@ mod tests { true, Some(content_src.clone()), None, - )); - assert_eq!(Assets::get_asset_content_src(&XOR), Some(content_src)); - }) - } - - #[test] - fn should_fail_content_source() { - let mut ext = ExtBuilder::default().build(); - let source: Vec = vec![0; ASSET_CONTENT_SOURCE_MAX_LENGTH + 1]; - let content_src = ContentSource(source); - ext.execute_with(|| { - assert_err!( - Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::from(10u32), - true, - Some(content_src.clone()), - None, - ), - Error::::InvalidContentSource - ); - }) - } - - #[test] - fn should_associate_desciption() { - let mut ext = ExtBuilder::default().build(); - let desc = Description(b"Lorem ipsum".to_vec()); - ext.execute_with(|| { - assert_ok!(Assets::register_asset_id( + ), + Error::::InvalidContentSource + ); + }) +} + +#[test] +fn should_associate_desciption() { + let mut ext = ExtBuilder::default().build(); + let desc = Description(b"Lorem ipsum".to_vec()); + ext.execute_with(|| { + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(10u32), + true, + None, + Some(desc.clone()), + )); + assert_eq!(Assets::get_asset_description(&XOR), Some(desc)); + }) +} + +#[test] +fn should_fail_description() { + let mut ext = ExtBuilder::default().build(); + let text: Vec = vec![0; ASSET_DESCRIPTION_MAX_LENGTH + 1]; + let desc = Description(text); + ext.execute_with(|| { + assert_err!( + Assets::register_asset_id( ALICE, XOR, AssetSymbol(b"XOR".to_vec()), @@ -715,232 +733,209 @@ mod tests { true, None, Some(desc.clone()), - )); - assert_eq!(Assets::get_asset_description(&XOR), Some(desc)); - }) - } - - #[test] - fn should_fail_description() { - let mut ext = ExtBuilder::default().build(); - let text: Vec = vec![0; ASSET_DESCRIPTION_MAX_LENGTH + 1]; - let desc = Description(text); - ext.execute_with(|| { - assert_err!( - Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::from(10u32), - true, - None, - Some(desc.clone()), - ), - Error::::InvalidDescription - ); - }) - } - - #[test] - fn buy_back_and_burn_should_be_performed() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - let xst_balance = balance!(1000); - Assets::register_asset_id( - MOCK_LIQUIDITY_PROXY_TECH_ACCOUNT, - XST, - AssetSymbol(b"XST".to_vec()), - AssetName(b"Sora Synthetics".to_vec()), + ), + Error::::InvalidDescription + ); + }) +} + +#[test] +fn buy_back_and_burn_should_be_performed() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + let xst_balance = balance!(1000); + Assets::register_asset_id( + MOCK_LIQUIDITY_PROXY_TECH_ACCOUNT, + XST, + AssetSymbol(b"XST".to_vec()), + AssetName(b"Sora Synthetics".to_vec()), + DEFAULT_BALANCE_PRECISION, + xst_balance, + true, + None, + None, + ) + .expect("Failed to register XST asset"); + + let xst_total = Tokens::total_issuance(XST); + // Just a sanity check, not a real test + assert_eq!(xst_total, xst_balance); + + let pswap_balance = balance!(10); + Assets::register_asset_id( + ALICE, + PSWAP, + AssetSymbol(b"PSWAP".to_vec()), + AssetName(b"Polkaswap".to_vec()), + DEFAULT_BALANCE_PRECISION, + pswap_balance, + true, + None, + None, + ) + .expect("Failed to register PSWAP asset"); + + let amount_to_mint = balance!(100); + Assets::force_mint(RuntimeOrigin::root(), PSWAP, ALICE, amount_to_mint) + .expect("Failed to mint PSWAP"); + + let pswap_balance_after = Assets::free_balance(&PSWAP, &ALICE) + .expect("Failed to query PSWAP free balance after mint."); + assert_eq!( + pswap_balance_after, + pswap_balance + (amount_to_mint * 9 / 10) + ); + + // Same as `Assets::free_balance(&XST, &MOCK_LIQUIDITY_PROXY_TECH_ACCOUNT)`, + // but it better represents the meaning of buy-back and burning + let xst_total_after = Tokens::total_issuance(XST); + + // Since `MockLiquidityProxy` exchanges 1 to 1 + // there is no need to calculate PSWAP-XST swap-rate + assert_eq!(xst_total_after, xst_total - (amount_to_mint / 10)); + }) +} + +#[test] +fn test_update_asset_info() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + // To be able to assert events + frame_system::Pallet::::set_block_number(1); + assert_ok!(Assets::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::zero(), + true, + None, + None, + )); + + let val_symbol = AssetSymbol(b"VAL".to_vec()); + let val_name = AssetName(b"VAL Token".to_vec()); + assert_ok!(Assets::update_info( + RuntimeOrigin::root(), + XOR, + Some(val_symbol.clone()), + Some(val_name.clone()) + )); + assert_last_event::( + Event::AssetUpdated(XOR, Some(val_symbol.clone()), Some(val_name.clone())).into(), + ); + assert_eq!( + Assets::asset_infos(XOR), + ( + val_symbol.clone(), + val_name, DEFAULT_BALANCE_PRECISION, - xst_balance, true, None, - None, + None ) - .expect("Failed to register XST asset"); - - let xst_total = Tokens::total_issuance(XST); - // Just a sanity check, not a real test - assert_eq!(xst_total, xst_balance); - - let pswap_balance = balance!(10); - Assets::register_asset_id( - ALICE, - PSWAP, - AssetSymbol(b"PSWAP".to_vec()), - AssetName(b"Polkaswap".to_vec()), + ); + + let pswap_symbol = AssetSymbol(b"PSWAP".to_vec()); + let pswap_name = AssetName(b"Polkaswap".to_vec()); + assert_ok!(Assets::update_info( + RuntimeOrigin::root(), + XOR, + None, + Some(pswap_name.clone()) + )); + assert_last_event::( + Event::AssetUpdated(XOR, None, Some(pswap_name.clone())).into(), + ); + assert_eq!( + Assets::asset_infos(XOR), + ( + val_symbol, + pswap_name.clone(), DEFAULT_BALANCE_PRECISION, - pswap_balance, true, None, - None, + None ) - .expect("Failed to register PSWAP asset"); - - let amount_to_mint = balance!(100); - Assets::force_mint(RuntimeOrigin::root(), PSWAP, ALICE, amount_to_mint) - .expect("Failed to mint PSWAP"); - - let pswap_balance_after = Assets::free_balance(&PSWAP, &ALICE) - .expect("Failed to query PSWAP free balance after mint."); - assert_eq!( - pswap_balance_after, - pswap_balance + (amount_to_mint * 9 / 10) - ); - - // Same as `Assets::free_balance(&XST, &MOCK_LIQUIDITY_PROXY_TECH_ACCOUNT)`, - // but it better represents the meaning of buy-back and burning - let xst_total_after = Tokens::total_issuance(XST); - - // Since `MockLiquidityProxy` exchanges 1 to 1 - // there is no need to calculate PSWAP-XST swap-rate - assert_eq!(xst_total_after, xst_total - (amount_to_mint / 10)); - }) - } - - #[test] - fn test_update_asset_info() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - // To be able to assert events - frame_system::Pallet::::set_block_number(1); - assert_ok!(Assets::register_asset_id( - ALICE, - XOR, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), + ); + + assert_ok!(Assets::update_info( + RuntimeOrigin::root(), + XOR, + Some(pswap_symbol.clone()), + None + )); + assert_last_event::( + Event::AssetUpdated(XOR, Some(pswap_symbol.clone()), None).into(), + ); + assert_eq!( + Assets::asset_infos(XOR), + ( + pswap_symbol, + pswap_name, DEFAULT_BALANCE_PRECISION, - Balance::zero(), true, None, - None, - )); + None + ) + ); - let val_symbol = AssetSymbol(b"VAL".to_vec()); - let val_name = AssetName(b"VAL Token".to_vec()); - assert_ok!(Assets::update_info( + assert_noop!( + Assets::update_info( RuntimeOrigin::root(), XOR, - Some(val_symbol.clone()), - Some(val_name.clone()) - )); - assert_last_event::( - Event::AssetUpdated(XOR, Some(val_symbol.clone()), Some(val_name.clone())).into(), - ); - assert_eq!( - Assets::asset_infos(XOR), - ( - val_symbol.clone(), - val_name.clone(), - DEFAULT_BALANCE_PRECISION, - true, - None, - None - ) - ); - - let pswap_symbol = AssetSymbol(b"PSWAP".to_vec()); - let pswap_name = AssetName(b"Polkaswap".to_vec()); - assert_ok!(Assets::update_info( + Some(AssetSymbol(b"Dai".to_vec())), + None + ), + Error::::InvalidAssetSymbol + ); + assert_noop!( + Assets::update_info( RuntimeOrigin::root(), XOR, None, - Some(pswap_name.clone()) - )); - assert_last_event::( - Event::AssetUpdated(XOR, None, Some(pswap_name.clone())).into(), - ); - assert_eq!( - Assets::asset_infos(XOR), - ( - val_symbol.clone(), - pswap_name.clone(), - DEFAULT_BALANCE_PRECISION, - true, - None, - None - ) - ); - - assert_ok!(Assets::update_info( + Some(AssetName(b"D@I".to_vec())) + ), + Error::::InvalidAssetName + ); + assert_noop!( + Assets::update_info( RuntimeOrigin::root(), XOR, - Some(pswap_symbol.clone()), + None, + Some(AssetName(b"".to_vec())) + ), + Error::::InvalidAssetName + ); + assert_noop!( + Assets::update_info( + RuntimeOrigin::root(), + XOR, + Some(AssetSymbol(b"".to_vec())), + None + ), + Error::::InvalidAssetSymbol + ); + assert_eq!( + Assets::asset_infos(XOR), + ( + AssetSymbol(b"PSWAP".to_vec()), + AssetName(b"Polkaswap".to_vec()), + DEFAULT_BALANCE_PRECISION, + true, + None, None - )); - assert_last_event::( - Event::AssetUpdated(XOR, Some(pswap_symbol.clone()), None).into(), - ); - assert_eq!( - Assets::asset_infos(XOR), - ( - pswap_symbol.clone(), - pswap_name.clone(), - DEFAULT_BALANCE_PRECISION, - true, - None, - None - ) - ); - - assert_noop!( - Assets::update_info( - RuntimeOrigin::root(), - XOR, - Some(AssetSymbol(b"Dai".to_vec())), - None - ), - Error::::InvalidAssetSymbol - ); - assert_noop!( - Assets::update_info( - RuntimeOrigin::root(), - XOR, - None, - Some(AssetName(b"D@I".to_vec())) - ), - Error::::InvalidAssetName - ); - assert_noop!( - Assets::update_info( - RuntimeOrigin::root(), - XOR, - None, - Some(AssetName(b"".to_vec())) - ), - Error::::InvalidAssetName - ); - assert_noop!( - Assets::update_info( - RuntimeOrigin::root(), - XOR, - Some(AssetSymbol(b"".to_vec())), - None - ), - Error::::InvalidAssetSymbol - ); - assert_eq!( - Assets::asset_infos(XOR), - ( - AssetSymbol(b"PSWAP".to_vec()), - AssetName(b"Polkaswap".to_vec()), - DEFAULT_BALANCE_PRECISION, - true, - None, - None - ) - ); - assert_noop!( - Assets::update_info( - RuntimeOrigin::root(), - DAI, - Some(AssetSymbol(b"DAI".to_vec())), - Some(AssetName(b"DAI stablecoin".to_vec())) - ), - Error::::AssetIdNotExists - ); - }); - } + ) + ); + assert_noop!( + Assets::update_info( + RuntimeOrigin::root(), + DAI, + Some(AssetSymbol(b"DAI".to_vec())), + Some(AssetName(b"DAI stablecoin".to_vec())) + ), + Error::::AssetIdNotExists + ); + }); } diff --git a/pallets/band/src/benchmarking.rs b/pallets/band/src/benchmarking.rs index 1e92db334c..903e89ef3e 100644 --- a/pallets/band/src/benchmarking.rs +++ b/pallets/band/src/benchmarking.rs @@ -84,7 +84,7 @@ benchmarks! { let relayer = relayer::(); }: _(RawOrigin::Root, vec![relayer.clone()]) verify { - assert_eq!(Band::::trusted_relayers().unwrap().contains(&relayer), true); + assert!(Band::::trusted_relayers().unwrap().contains(&relayer)); } remove_relayers { @@ -92,7 +92,7 @@ benchmarks! { Band::::add_relayers(RawOrigin::Root.into(), vec![relayer.clone()])?; }: _(RawOrigin::Root, vec![relayer.clone()]) verify { - assert_eq!(Band::::trusted_relayers().unwrap().contains(&relayer), false); + assert!(!Band::::trusted_relayers().unwrap().contains(&relayer)); } set_dynamic_fee_parameters { diff --git a/pallets/band/src/lib.rs b/pallets/band/src/lib.rs index 7fa0ad7988..a8d758d7db 100644 --- a/pallets/band/src/lib.rs +++ b/pallets/band/src/lib.rs @@ -29,8 +29,6 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #![cfg_attr(not(feature = "std"), no_std)] -// TODO #167: fix clippy warnings -#![allow(clippy::all)] use common::prelude::FixedWrapper; use common::{fixed, fixed_wrapper, Balance, DataFeed, Fixed, OnNewSymbolsRelayed, Oracle, Rate}; @@ -452,7 +450,7 @@ pub mod pallet { TrustedRelayers::::mutate(|option_relayers| match option_relayers { Some(relayers) => { let to_remove = BTreeSet::from_iter(account_ids); - if to_remove.is_subset(&relayers) { + if to_remove.is_subset(relayers) { for account in &to_remove { relayers.remove(account); } @@ -530,30 +528,26 @@ impl, I: 'static> Pallet { )) .collect()?; let now = frame_system::Pallet::::block_number(); - let new_symbols = fallible_iterator::convert( - converted_rates - .iter() - .map(|symbol_rate| Ok::<_, DispatchError>(symbol_rate)), - ) - .fold( - BTreeSet::new(), - |mut new_symbols_acc, (symbol, rate_value)| { - let new_rate = BandRate { - value: *rate_value, - last_updated: resolve_time, - request_id, - dynamic_fee: fixed!(0), - last_updated_block: now, - }; - SymbolRates::::mutate(symbol, |option_old_rate| { - if option_old_rate.is_none() { - new_symbols_acc.insert(symbol.clone()); - } - f(option_old_rate, new_rate, symbol) - })?; - Ok(new_symbols_acc) - }, - )?; + let new_symbols = + fallible_iterator::convert(converted_rates.iter().map(Ok::<_, DispatchError>)).fold( + BTreeSet::new(), + |mut new_symbols_acc, (symbol, rate_value)| { + let new_rate = BandRate { + value: *rate_value, + last_updated: resolve_time, + request_id, + dynamic_fee: fixed!(0), + last_updated_block: now, + }; + SymbolRates::::mutate(symbol, |option_old_rate| { + if option_old_rate.is_none() { + new_symbols_acc.insert(symbol.clone()); + } + f(option_old_rate, new_rate, symbol) + })?; + Ok(new_symbols_acc) + }, + )?; T::OnNewSymbolsRelayedHook::on_new_symbols_relayed(Oracle::BandChainFeed, new_symbols)?; diff --git a/pallets/band/src/migrations.rs b/pallets/band/src/migrations.rs index eae3920696..8be0fa189c 100644 --- a/pallets/band/src/migrations.rs +++ b/pallets/band/src/migrations.rs @@ -118,15 +118,14 @@ pub mod v1 { SymbolRatesV1::::translate::, _>(|_symbol, band_rate| { weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - match band_rate { - Some(band_rate) => Some(Some(BandRateV1 { + band_rate.map(|band_rate| { + Some(BandRateV1 { value: band_rate.value, last_updated: band_rate.last_updated, request_id: band_rate.request_id, dynamic_fee: fixed!(0), - })), - None => None, - } + }) + }) }); StorageVersion::new(1).put::>(); @@ -224,16 +223,15 @@ pub mod v2 { symbol, true, ); - match band_rate { - Some(band_rate) => Some(Some(BandRateV2 { + band_rate.map(|band_rate| { + Some(BandRateV2 { value: band_rate.value, last_updated: band_rate.last_updated, last_updated_block: now, request_id: band_rate.request_id, dynamic_fee: fixed!(0), - })), - None => None, - } + }) + }) }); StorageVersion::new(2).put::>(); @@ -281,13 +279,10 @@ pub mod v2 { let rates_vec = vec!["USD", "RUB"]; rates_vec.iter().cloned().for_each(|symbol| { assert_eq!(SymbolRatesV1::::get(symbol), None); - assert_eq!( - SymbolCheckBlock::::get( - 1 + GetRateStaleBlockPeriod::get(), - symbol - ), - false, - ); + assert!(!SymbolCheckBlock::::get( + 1 + GetRateStaleBlockPeriod::get(), + symbol + )); SymbolRatesV1::::insert(symbol, Some(sample_rate.clone())); }); @@ -299,13 +294,10 @@ pub mod v2 { .expect("Expected to get rate for the specified symbol") .last_updated_block; assert_eq!(last_updated_block, 1); - assert_eq!( - SymbolCheckBlock::::get( - 1 + GetRateStaleBlockPeriod::get(), - symbol - ), - true, - ); + assert!(SymbolCheckBlock::::get( + 1 + GetRateStaleBlockPeriod::get(), + symbol + )); } assert_eq!(Pallet::::on_chain_storage_version(), 2); }); diff --git a/pallets/band/src/tests.rs b/pallets/band/src/tests.rs index 2a52086e49..4c74f21508 100644 --- a/pallets/band/src/tests.rs +++ b/pallets/band/src/tests.rs @@ -81,7 +81,7 @@ fn add_and_remove_relayers_should_forbid_non_root_call() { ); assert_noop!( - Band::remove_relayers(RuntimeOrigin::signed(11), relayers.clone()), + Band::remove_relayers(RuntimeOrigin::signed(11), relayers), BadOrigin ); @@ -93,8 +93,7 @@ fn add_and_remove_relayers_should_forbid_non_root_call() { fn add_relayers_should_check_if_relayer_was_already_added() { new_test_ext().execute_with(|| { let relayers = vec![1, 2, 3, 4, 5]; - Band::add_relayers(RuntimeOrigin::root(), relayers.clone()) - .expect("Failed to add relayers"); + Band::add_relayers(RuntimeOrigin::root(), relayers).expect("Failed to add relayers"); assert_noop!( Band::add_relayers(RuntimeOrigin::root(), vec![1]), @@ -107,8 +106,7 @@ fn add_relayers_should_check_if_relayer_was_already_added() { fn remove_relayers_should_check_if_no_such_relayer_exists() { new_test_ext().execute_with(|| { let relayers = vec![1, 2, 3, 4, 5]; - Band::add_relayers(RuntimeOrigin::root(), relayers.clone()) - .expect("Failed to add relayers"); + Band::add_relayers(RuntimeOrigin::root(), relayers).expect("Failed to add relayers"); assert_noop!( Band::remove_relayers(RuntimeOrigin::root(), vec![6]), @@ -121,8 +119,7 @@ fn remove_relayers_should_check_if_no_such_relayer_exists() { fn add_relayers_should_ignore_duplicates() { new_test_ext().execute_with(|| { let relayers = vec![1, 2, 3, 4, 5, 3, 5, 4]; - Band::add_relayers(RuntimeOrigin::root(), relayers.clone()) - .expect("Failed to add relayers"); + Band::add_relayers(RuntimeOrigin::root(), relayers).expect("Failed to add relayers"); assert_eq!( Band::trusted_relayers().expect("Expected initialized relayers"), @@ -135,8 +132,7 @@ fn add_relayers_should_ignore_duplicates() { fn remove_relayers_should_ignore_duplicates() { new_test_ext().execute_with(|| { let relayers = vec![1, 2, 3, 4, 5]; - Band::add_relayers(RuntimeOrigin::root(), relayers.clone()) - .expect("Failed to add relayers"); + Band::add_relayers(RuntimeOrigin::root(), relayers).expect("Failed to add relayers"); Band::remove_relayers(RuntimeOrigin::root(), vec![1, 2, 3, 2, 1, 1, 3]) .expect("Failed to remove relayers"); @@ -524,17 +520,14 @@ fn check_block_symbol_should_work() { ) .expect("Failed to relay rates"); - assert_eq!( - SymbolCheckBlock::::get(1 + GetRateStaleBlockPeriod::get(), "USD".to_owned()), - true - ); + assert!(SymbolCheckBlock::::get( + 1 + GetRateStaleBlockPeriod::get(), + "USD".to_owned() + )); >>::on_initialize(601); - assert_eq!( - SymbolCheckBlock::::get(601u64, "USD".to_owned()), - false - ) + assert!(!SymbolCheckBlock::::get(601u64, "USD".to_owned())) }) } @@ -556,25 +549,25 @@ fn set_invalid_dynamic_fee_parameters_should_fail() { new_test_ext().execute_with(|| { let parameters = FeeCalculationParameters::new(fixed!(-0.1), fixed!(0), fixed!(0)); assert_eq!( - Band::set_dynamic_fee_parameters(RuntimeOrigin::root(), parameters.clone(),), + Band::set_dynamic_fee_parameters(RuntimeOrigin::root(), parameters), Err(Error::::InvalidDynamicFeeParameters.into()) ); let parameters = FeeCalculationParameters::new(fixed!(0), fixed!(-1), fixed!(0)); assert_eq!( - Band::set_dynamic_fee_parameters(RuntimeOrigin::root(), parameters.clone(),), + Band::set_dynamic_fee_parameters(RuntimeOrigin::root(), parameters), Err(Error::::InvalidDynamicFeeParameters.into()) ); let parameters = FeeCalculationParameters::new(fixed!(0), fixed!(0), fixed!(-1)); assert_eq!( - Band::set_dynamic_fee_parameters(RuntimeOrigin::root(), parameters.clone(),), + Band::set_dynamic_fee_parameters(RuntimeOrigin::root(), parameters), Err(Error::::InvalidDynamicFeeParameters.into()) ); let parameters = FeeCalculationParameters::new(fixed!(1), fixed!(0), fixed!(0)); assert_eq!( - Band::set_dynamic_fee_parameters(RuntimeOrigin::root(), parameters.clone(),), + Band::set_dynamic_fee_parameters(RuntimeOrigin::root(), parameters), Err(Error::::InvalidDynamicFeeParameters.into()) ); }) @@ -585,7 +578,7 @@ fn should_calculate_dynamic_fee() { new_test_ext().execute_with(|| { let parameters = FeeCalculationParameters::new(fixed!(0.1), fixed!(0.01), fixed!(0.05)); - Band::set_dynamic_fee_parameters(RuntimeOrigin::root(), parameters.clone()) + Band::set_dynamic_fee_parameters(RuntimeOrigin::root(), parameters) .expect("Expected to set the dynamic fee calculation parameters"); let relayer = 1; diff --git a/pallets/ceres-launchpad/src/benchmarking.rs b/pallets/ceres-launchpad/src/benchmarking.rs index 40bfa66750..4012505cac 100644 --- a/pallets/ceres-launchpad/src/benchmarking.rs +++ b/pallets/ceres-launchpad/src/benchmarking.rs @@ -45,10 +45,10 @@ benchmarks! { let current_timestamp = Timestamp::::get(); let asset_id = T::AssetId::from(CERES_ASSET_ID); - let asset_owner = Assets::::asset_owner(&asset_id).unwrap(); + let asset_owner = Assets::::asset_owner(asset_id).unwrap(); Assets::::mint( - RawOrigin::Signed(asset_owner.clone()).into(), + RawOrigin::Signed(asset_owner).into(), CERES_ASSET_ID.into(), caller.clone(), balance!(20000) @@ -83,7 +83,7 @@ benchmarks! { balance!(0.2) ) verify { - assert_last_event::(Event::::ILOCreated(caller.clone(), CERES_ASSET_ID.into()).into()); + assert_last_event::(Event::::ILOCreated(caller, CERES_ASSET_ID.into()).into()); } contribute { @@ -93,7 +93,7 @@ benchmarks! { let funds_to_contribute = balance!(800); let asset_id = T::AssetId::from(CERES_ASSET_ID); - let asset_owner = Assets::::asset_owner(&asset_id).unwrap(); + let asset_owner = Assets::::asset_owner(asset_id).unwrap(); Assets::::mint( RawOrigin::Signed(asset_owner.clone()).into(), @@ -103,7 +103,7 @@ benchmarks! { ).unwrap(); Assets::::mint( - RawOrigin::Signed(asset_owner.clone()).into(), + RawOrigin::Signed(asset_owner).into(), XOR.into(), caller.clone(), balance!(20000) @@ -159,7 +159,7 @@ benchmarks! { let funds_to_contribute = balance!(800); let asset_id = T::AssetId::from(CERES_ASSET_ID); - let asset_owner = Assets::::asset_owner(&asset_id).unwrap(); + let asset_owner = Assets::::asset_owner(asset_id).unwrap(); Assets::::mint( RawOrigin::Signed(asset_owner.clone()).into(), @@ -169,7 +169,7 @@ benchmarks! { ).unwrap(); Assets::::mint( - RawOrigin::Signed(asset_owner.clone()).into(), + RawOrigin::Signed(asset_owner).into(), XOR.into(), caller.clone(), balance!(20000) @@ -231,7 +231,7 @@ benchmarks! { let current_timestamp = Timestamp::::get(); let asset_id = T::AssetId::from(CERES_ASSET_ID); - let asset_owner = Assets::::asset_owner(&asset_id).unwrap(); + let asset_owner = Assets::::asset_owner(asset_id).unwrap(); Assets::::mint( RawOrigin::Signed(asset_owner.clone()).into(), @@ -241,7 +241,7 @@ benchmarks! { ).unwrap(); Assets::::mint( - RawOrigin::Signed(asset_owner.clone()).into(), + RawOrigin::Signed(asset_owner).into(), XOR.into(), caller.clone(), balance!(10000) @@ -298,7 +298,7 @@ benchmarks! { }: _(RawOrigin::Signed(caller.clone()), CERES_ASSET_ID.into()) verify { - assert_last_event::(Event::::ILOFinished(caller.clone(), CERES_ASSET_ID.into()).into()); + assert_last_event::(Event::::ILOFinished(caller, CERES_ASSET_ID.into()).into()); } claim_lp_tokens { @@ -309,7 +309,7 @@ benchmarks! { let funds_to_contribute = balance!(800); let asset_id = T::AssetId::from(CERES_ASSET_ID); - let asset_owner = Assets::::asset_owner(&asset_id).unwrap(); + let asset_owner = Assets::::asset_owner(asset_id).unwrap(); Assets::::mint( RawOrigin::Signed(asset_owner.clone()).into(), @@ -319,7 +319,7 @@ benchmarks! { ).unwrap(); Assets::::mint( - RawOrigin::Signed(asset_owner.clone()).into(), + RawOrigin::Signed(asset_owner).into(), XOR.into(), caller.clone(), balance!(20000) @@ -392,7 +392,7 @@ benchmarks! { let current_timestamp = Timestamp::::get(); let asset_id = T::AssetId::from(CERES_ASSET_ID); - let asset_owner = Assets::::asset_owner(&asset_id).unwrap(); + let asset_owner = Assets::::asset_owner(asset_id).unwrap(); Assets::::mint( RawOrigin::Signed(asset_owner.clone()).into(), @@ -402,7 +402,7 @@ benchmarks! { ).unwrap(); Assets::::mint( - RawOrigin::Signed(asset_owner.clone()).into(), + RawOrigin::Signed(asset_owner).into(), XOR.into(), caller.clone(), balance!(10000) @@ -464,7 +464,7 @@ benchmarks! { pallet_timestamp::Now::::put(current_timestamp + 44u32.into()); }: _(RawOrigin::Signed(caller.clone()), CERES_ASSET_ID.into()) verify { - assert_last_event::(Event::::Claimed(caller.clone(), CERES_ASSET_ID.into()).into()); + assert_last_event::(Event::::Claimed(caller, CERES_ASSET_ID.into()).into()); } change_ceres_burn_fee { @@ -489,7 +489,7 @@ benchmarks! { let current_timestamp = Timestamp::::get(); let asset_id = T::AssetId::from(CERES_ASSET_ID); - let asset_owner = Assets::::asset_owner(&asset_id).unwrap(); + let asset_owner = Assets::::asset_owner(asset_id).unwrap(); Assets::::mint( RawOrigin::Signed(asset_owner.clone()).into(), @@ -506,7 +506,7 @@ benchmarks! { ).unwrap(); Assets::::mint( - RawOrigin::Signed(asset_owner.clone()).into(), + RawOrigin::Signed(asset_owner).into(), PSWAP.into(), T::GetTechnicalAccountId::get(), balance!(10000) @@ -562,7 +562,7 @@ benchmarks! { pallet_timestamp::Now::::put(current_timestamp + 11u32.into()); CeresLaunchpad::::finish_ilo( - RawOrigin::Signed(caller.clone()).into(), + RawOrigin::Signed(caller).into(), CERES_ASSET_ID.into() ).unwrap(); diff --git a/pallets/ceres-launchpad/src/lib.rs b/pallets/ceres-launchpad/src/lib.rs index 89ced2d996..2e670014bb 100644 --- a/pallets/ceres-launchpad/src/lib.rs +++ b/pallets/ceres-launchpad/src/lib.rs @@ -1,6 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -// TODO #167: fix clippy warnings -#![allow(clippy::all)] +#![allow(clippy::large_enum_variant)] +#![allow(clippy::type_complexity)] pub mod weights; @@ -74,6 +74,7 @@ pub struct ContributionInfo { pub use pallet::*; #[frame_support::pallet] +#[allow(clippy::too_many_arguments)] pub mod pallet { use super::*; use crate::{ContributionInfo, ContributorsVesting, ILOInfo}; @@ -380,13 +381,13 @@ pub mod pallet { } ensure!( - !>::contains_key(&asset_id), + !>::contains_key(asset_id), Error::::ILOAlreadyExists ); // Check if ILO for token already exists ensure!( - !>::contains_key(&asset_id), + !>::contains_key(asset_id), Error::::ILOAlreadyExists ); @@ -397,13 +398,8 @@ pub mod pallet { }; ensure!( - TradingPair::::is_trading_pair_enabled( - &dex_id, - &base_asset.into(), - &asset_id.into() - ) - .unwrap_or(true) - == false, + !TradingPair::::is_trading_pair_enabled(&dex_id, &base_asset, &asset_id) + .unwrap_or(true), Error::::CantCreateILOForListedToken ); @@ -436,8 +432,7 @@ pub mod pallet { ensure!( CeresBurnFeeAmount::::get() - <= Assets::::free_balance(&CeresAssetIdOf::::get().into(), &user) - .unwrap_or(0), + <= Assets::::free_balance(&CeresAssetIdOf::::get(), &user).unwrap_or(0), Error::::NotEnoughCeres ); @@ -450,12 +445,12 @@ pub mod pallet { // Burn CERES as fee Assets::::burn( origin, - CeresAssetIdOf::::get().into(), + CeresAssetIdOf::::get(), CeresBurnFeeAmount::::get(), )?; // Transfer tokens to pallet - Assets::::transfer_from(&asset_id.into(), &user, &Self::account_id(), total_tokens)?; + Assets::::transfer_from(&asset_id, &user, &Self::account_id(), total_tokens)?; let ilo_info = ILOInfo { ilo_organizer: user.clone(), @@ -493,7 +488,7 @@ pub mod pallet { base_asset, }; - >::insert(&asset_id, &ilo_info); + >::insert(asset_id, &ilo_info); // Emit an event Self::deposit_event(Event::ILOCreated(user, asset_id)); @@ -520,16 +515,15 @@ pub mod pallet { ensure!( CeresForContributionInILO::::get() - <= Assets::::free_balance(&CeresAssetIdOf::::get().into(), &user) - .unwrap_or(0), + <= Assets::::free_balance(&CeresAssetIdOf::::get(), &user).unwrap_or(0), Error::::NotEnoughCeres ); // Get ILO info - let mut ilo_info = >::get(&asset_id).ok_or(Error::::ILODoesNotExist)?; + let mut ilo_info = >::get(asset_id).ok_or(Error::::ILODoesNotExist)?; // Get contribution info - let mut contribution_info = >::get(&asset_id, &user); + let mut contribution_info = >::get(asset_id, &user); ensure!( ilo_info.start_timestamp < current_timestamp, @@ -566,15 +560,15 @@ pub mod pallet { // Transfer base_asset to pallet Assets::::transfer_from( - &ilo_info.base_asset.into(), + &ilo_info.base_asset, &user, &Self::account_id(), funds_to_contribute, )?; // Update storage - >::insert(&asset_id, &ilo_info); - >::insert(&asset_id, &user, contribution_info); + >::insert(asset_id, &ilo_info); + >::insert(asset_id, &user, contribution_info); // Emit event Self::deposit_event(Event::::Contributed(user, asset_id, funds_to_contribute)); @@ -596,10 +590,10 @@ pub mod pallet { let current_timestamp = Timestamp::::get(); // Get ILO info - let mut ilo_info = >::get(&asset_id).ok_or(Error::::ILODoesNotExist)?; + let mut ilo_info = >::get(asset_id).ok_or(Error::::ILODoesNotExist)?; // Get contribution info - let contribution_info = >::get(&asset_id, &user); + let contribution_info = >::get(asset_id, &user); ensure!( ilo_info.start_timestamp < current_timestamp, @@ -622,7 +616,7 @@ pub mod pallet { let pallet_account = Self::account_id(); // Emergency withdraw funds Assets::::transfer_from( - &ilo_info.base_asset.into(), + &ilo_info.base_asset, &pallet_account, &user, funds_to_claim, @@ -631,7 +625,7 @@ pub mod pallet { let penalty = contribution_info.funds_contributed - funds_to_claim; Assets::::transfer_from( - &ilo_info.base_asset.into(), + &ilo_info.base_asset, &pallet_account, &PenaltiesAccount::::get(), penalty, @@ -641,8 +635,8 @@ pub mod pallet { ilo_info.sold_tokens -= contribution_info.tokens_bought; // Update map - >::insert(&asset_id, &ilo_info); - >::remove(&asset_id, &user); + >::insert(asset_id, &ilo_info); + >::remove(asset_id, &user); // Emit event Self::deposit_event(Event::::EmergencyWithdrawn( @@ -665,7 +659,7 @@ pub mod pallet { let user = ensure_signed(origin.clone())?; // Get ILO info of asset_id token - let mut ilo_info = >::get(&asset_id).ok_or(Error::::ILODoesNotExist)?; + let mut ilo_info = >::get(asset_id).ok_or(Error::::ILODoesNotExist)?; if user != ilo_info.ilo_organizer { return Err(Error::::Unauthorized.into()); @@ -689,19 +683,19 @@ pub mod pallet { if !ilo_info.refund_type { Assets::::burn( RawOrigin::Signed(pallet_account).into(), - asset_id.into(), + asset_id, total_tokens, )?; } else { Assets::::transfer_from( - &asset_id.into(), + &asset_id, &pallet_account, &ilo_info.ilo_organizer, total_tokens, )?; } - >::insert(&asset_id, &ilo_info); + >::insert(asset_id, &ilo_info); return Ok(().into()); } @@ -712,7 +706,7 @@ pub mod pallet { .try_into_balance() .unwrap_or(0); Assets::::transfer_from( - &ilo_info.base_asset.into(), + &ilo_info.base_asset, &pallet_account, &AuthorityAccount::::get(), funds_raised_fee, @@ -726,7 +720,7 @@ pub mod pallet { .unwrap_or(0); let funds_for_team = raised_funds_without_fee - funds_for_liquidity; Assets::::transfer_from( - &ilo_info.base_asset.into(), + &ilo_info.base_asset, &pallet_account, &ilo_info.ilo_organizer, funds_for_team, @@ -741,16 +735,16 @@ pub mod pallet { TradingPair::::register( RawOrigin::Signed(pallet_account.clone()).into(), dex_id, - ilo_info.base_asset.into(), - asset_id.into(), + ilo_info.base_asset, + asset_id, )?; // Initialize pool PoolXYK::::initialize_pool( RawOrigin::Signed(pallet_account.clone()).into(), dex_id, - ilo_info.base_asset.into(), - asset_id.into(), + ilo_info.base_asset, + asset_id, )?; // Deposit liquidity @@ -765,8 +759,8 @@ pub mod pallet { PoolXYK::::deposit_liquidity( RawOrigin::Signed(pallet_account.clone()).into(), dex_id, - ilo_info.base_asset.into(), - asset_id.into(), + ilo_info.base_asset, + asset_id, funds_for_liquidity, tokens_for_liquidity, funds_for_liquidity, @@ -776,40 +770,39 @@ pub mod pallet { // Burn unused tokens for liquidity Assets::::burn( RawOrigin::Signed(pallet_account.clone()).into(), - asset_id.into(), + asset_id, ilo_info.tokens_for_liquidity - tokens_for_liquidity, )?; // Burn unused tokens for ilo Assets::::burn( RawOrigin::Signed(pallet_account.clone()).into(), - asset_id.into(), + asset_id, ilo_info.tokens_for_ilo - ilo_info.sold_tokens, )?; // Lock liquidity let unlocking_liq_timestamp = current_timestamp - + (T::MILLISECONDS_PER_DAY.saturating_mul(ilo_info.lockup_days.into())).into(); + + (T::MILLISECONDS_PER_DAY.saturating_mul(ilo_info.lockup_days.into())); CeresLiquidityLocker::::lock_liquidity( RawOrigin::Signed(pallet_account.clone()).into(), - ilo_info.base_asset.into(), - asset_id.into(), + ilo_info.base_asset, + asset_id, unlocking_liq_timestamp, balance!(1), true, )?; // Calculate LP tokens - let pool_account = - PoolXYK::::properties_of_pool(ilo_info.base_asset.into(), asset_id) - .ok_or(Error::::PoolDoesNotExist)? - .0; + let pool_account = PoolXYK::::properties_of_pool(ilo_info.base_asset, asset_id) + .ok_or(Error::::PoolDoesNotExist)? + .0; ilo_info.lp_tokens = PoolXYK::::balance_of_pool_provider(pool_account, pallet_account).unwrap_or(0); ilo_info.succeeded = true; ilo_info.finish_timestamp = current_timestamp; - >::insert(&asset_id, &ilo_info); + >::insert(asset_id, &ilo_info); // Lock team tokens if ilo_info.team_vesting.team_vesting_total_tokens != balance!(0) { @@ -822,13 +815,12 @@ pub mod pallet { .unwrap_or(0); ensure!( - tokens_to_lock - <= Assets::::free_balance(&asset_id.into(), &user).unwrap_or(0), + tokens_to_lock <= Assets::::free_balance(&asset_id, &user).unwrap_or(0), Error::::NotEnoughTeamTokensToLock ); let mut unlocking_timestamp = - current_timestamp + ilo_info.team_vesting.team_vesting_period.into(); + current_timestamp + ilo_info.team_vesting.team_vesting_period; let tokens_to_lock_per_period = (FixedWrapper::from(ilo_info.team_vesting.team_vesting_total_tokens) * FixedWrapper::from(ilo_info.team_vesting.team_vesting_percent)) @@ -838,12 +830,12 @@ pub mod pallet { while vesting_amount > balance!(0) { TokenLocker::::lock_tokens( origin.clone(), - asset_id.clone(), - unlocking_timestamp.clone(), + asset_id, + unlocking_timestamp, tokens_to_lock_per_period, )?; - unlocking_timestamp += ilo_info.team_vesting.team_vesting_period.into(); + unlocking_timestamp += ilo_info.team_vesting.team_vesting_period; vesting_amount = vesting_amount .checked_sub(ilo_info.team_vesting.team_vesting_percent) .unwrap_or(balance!(0)); @@ -851,7 +843,7 @@ pub mod pallet { } // Emit an event - Self::deposit_event(Event::ILOFinished(user.clone(), asset_id)); + Self::deposit_event(Event::ILOFinished(user, asset_id)); // Return a successful DispatchResult Ok(().into()) @@ -868,7 +860,7 @@ pub mod pallet { let current_timestamp = Timestamp::::get(); // Get ILO info - let mut ilo_info = >::get(&asset_id).ok_or(Error::::ILODoesNotExist)?; + let mut ilo_info = >::get(asset_id).ok_or(Error::::ILODoesNotExist)?; if user != ilo_info.ilo_organizer { return Err(Error::::Unauthorized.into()); @@ -887,15 +879,14 @@ pub mod pallet { let pallet_account = Self::account_id(); // Get pool account - let pool_account = - PoolXYK::::properties_of_pool(ilo_info.base_asset.into(), asset_id) - .ok_or(Error::::PoolDoesNotExist)? - .0; + let pool_account = PoolXYK::::properties_of_pool(ilo_info.base_asset, asset_id) + .ok_or(Error::::PoolDoesNotExist)? + .0; // Transfer LP tokens PoolXYK::::transfer_lp_tokens( - pool_account.clone(), - ilo_info.base_asset.into(), + pool_account, + ilo_info.base_asset, asset_id, pallet_account, user.clone(), @@ -905,10 +896,10 @@ pub mod pallet { ilo_info.claimed_lp_tokens = true; // Update storage - >::insert(&asset_id, &ilo_info); + >::insert(asset_id, &ilo_info); // Emit an event - Self::deposit_event(Event::ClaimedLP(user.clone(), asset_id)); + Self::deposit_event(Event::ClaimedLP(user, asset_id)); // Return a successful DispatchResult Ok(().into()) @@ -921,16 +912,16 @@ pub mod pallet { let user = ensure_signed(origin)?; // Get ILO info - let ilo_info = >::get(&asset_id).ok_or(Error::::ILODoesNotExist)?; + let ilo_info = >::get(asset_id).ok_or(Error::::ILODoesNotExist)?; if !ilo_info.failed && !ilo_info.succeeded { return Err(Error::::ILOIsNotFinished.into()); } // Get contribution info - let mut contribution_info = >::get(&asset_id, &user); + let mut contribution_info = >::get(asset_id, &user); ensure!( - contribution_info.claiming_finished == false, + !contribution_info.claiming_finished, Error::::FundsAlreadyClaimed ); @@ -940,7 +931,7 @@ pub mod pallet { if ilo_info.failed { // Claim unused funds Assets::::transfer_from( - &ilo_info.base_asset.into(), + &ilo_info.base_asset, &pallet_account, &user, contribution_info.funds_contributed, @@ -954,12 +945,7 @@ pub mod pallet { .try_into_balance() .unwrap_or(0); // Claim first time - Assets::::transfer_from( - &asset_id.into(), - &pallet_account, - &user, - tokens_to_claim, - )?; + Assets::::transfer_from(&asset_id, &pallet_account, &user, tokens_to_claim)?; contribution_info.tokens_claimed += tokens_to_claim; if ilo_info.contributors_vesting.first_release_percent == balance!(1) { contribution_info.claiming_finished = true; @@ -997,12 +983,7 @@ pub mod pallet { } // Claim tokens - Assets::::transfer_from( - &asset_id.into(), - &pallet_account, - &user, - claimable, - )?; + Assets::::transfer_from(&asset_id, &pallet_account, &user, claimable)?; contribution_info.tokens_claimed += claimable; contribution_info.number_of_claims += (claimable / tokens_per_claim) as u32; @@ -1019,10 +1000,10 @@ pub mod pallet { } } - >::insert(&asset_id, &user, contribution_info); + >::insert(asset_id, &user, contribution_info); // Emit an event - Self::deposit_event(Event::Claimed(user.clone(), asset_id)); + Self::deposit_event(Event::Claimed(user, asset_id)); Ok(().into()) } @@ -1236,8 +1217,7 @@ pub mod pallet { && !ilo_info.succeeded { let finish_timestamp = ilo_info.end_timestamp - + (T::MILLISECONDS_PER_DAY.saturating_mul(days_to_finish_ilo.into())) - .into(); + + (T::MILLISECONDS_PER_DAY.saturating_mul(days_to_finish_ilo.into())); if current_timestamp >= finish_timestamp { ilo_info.failed = true; @@ -1246,19 +1226,19 @@ pub mod pallet { if !ilo_info.refund_type { let _ = Assets::::burn( RawOrigin::Signed(pallet_account.clone()).into(), - ilo_asset.into(), + ilo_asset, total_tokens, ); } else { let _ = Assets::::transfer_from( - &ilo_asset.into(), + &ilo_asset, &pallet_account, &ilo_info.ilo_organizer, total_tokens, ); } - >::insert(&ilo_asset, ilo_info); + >::insert(ilo_asset, ilo_info); counter += 1; } } @@ -1278,6 +1258,7 @@ pub mod pallet { } /// Check parameters + #[allow(clippy::too_many_arguments)] fn check_parameters( tokens_for_ilo: Balance, tokens_for_liquidity: Balance, @@ -1410,7 +1391,7 @@ pub mod pallet { return Err(Error::::InvalidVestingPeriod.into()); } - Ok(().into()) + Ok(()) } } } diff --git a/pallets/ceres-launchpad/src/mock.rs b/pallets/ceres-launchpad/src/mock.rs index e12e569087..1387c9f2d5 100644 --- a/pallets/ceres-launchpad/src/mock.rs +++ b/pallets/ceres-launchpad/src/mock.rs @@ -14,7 +14,6 @@ use currencies::BasicCurrencyAdapter; use frame_support::traits::{Everything, GenesisBuild, Hooks}; use frame_support::weights::Weight; use frame_support::{construct_runtime, parameter_types}; -use frame_system; use frame_system::pallet_prelude::BlockNumberFor; use permissions::{Scope, MANAGE_DEX}; use sp_core::H256; @@ -73,7 +72,7 @@ parameter_types! { pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); pub GetXykFee: Fixed = fixed!(0.003); - pub GetIncentiveAssetId: AssetId = common::PSWAP.into(); + pub GetIncentiveAssetId: AssetId = common::PSWAP; pub const GetDefaultSubscriptionFrequency: BlockNumber = 10; pub const GetBurnUpdateFrequency: BlockNumber = 14400; pub GetParliamentAccountId: AccountId = 100; @@ -304,6 +303,7 @@ impl pallet_balances::Config for Runtime { type ReserveIdentifier = (); } +#[allow(clippy::type_complexity)] pub struct ExtBuilder { pub endowed_assets: Vec<( AssetId, @@ -345,9 +345,9 @@ impl Default for ExtBuilder { ), ], endowed_accounts: vec![ - (ALICE, CERES_ASSET_ID.into(), balance!(15000)), - (BOB, CERES_ASSET_ID.into(), balance!(5)), - (CHARLES, CERES_ASSET_ID.into(), balance!(3000)), + (ALICE, CERES_ASSET_ID, balance!(15000)), + (BOB, CERES_ASSET_ID, balance!(5)), + (CHARLES, CERES_ASSET_ID, balance!(3000)), ], initial_permission_owners: vec![ (MANAGE_DEX, Scope::Limited(hash(&DEX_A_ID)), vec![BOB]), @@ -364,43 +364,44 @@ impl Default for ExtBuilder { impl ExtBuilder { #[cfg(feature = "runtime-benchmarks")] pub fn benchmarking() -> Self { - let mut res = Self::default(); - res.endowed_assets = vec![ - ( - CERES_ASSET_ID, - ALICE, - AssetSymbol(b"CERES".to_vec()), - AssetName(b"Ceres".to_vec()), - 18, - 0, - true, - None, - None, - ), - ( - XOR, - ALICE, - AssetSymbol(b"XOR".to_vec()), - AssetName(b"XOR".to_vec()), - 18, - 0, - true, - None, - None, - ), - ( - PSWAP, - ALICE, - AssetSymbol(b"PSWAP".to_vec()), - AssetName(b"PSWAP".to_vec()), - 18, - 0, - true, - None, - None, - ), - ]; - res + Self { + endowed_assets: vec![ + ( + CERES_ASSET_ID, + ALICE, + AssetSymbol(b"CERES".to_vec()), + AssetName(b"Ceres".to_vec()), + 18, + 0, + true, + None, + None, + ), + ( + XOR, + ALICE, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"XOR".to_vec()), + 18, + 0, + true, + None, + None, + ), + ( + PSWAP, + ALICE, + AssetSymbol(b"PSWAP".to_vec()), + AssetName(b"PSWAP".to_vec()), + 18, + 0, + true, + None, + None, + ), + ], + ..Default::default() + } } pub fn build(self) -> sp_io::TestExternalities { diff --git a/pallets/ceres-launchpad/src/tests.rs b/pallets/ceres-launchpad/src/tests.rs index 6a8daa5947..280fac6078 100644 --- a/pallets/ceres-launchpad/src/tests.rs +++ b/pallets/ceres-launchpad/src/tests.rs @@ -1,1081 +1,178 @@ -mod tests { - use crate::mock::*; - use crate::{pallet, Error, FeePercentOnRaisedFunds, Pallet as CeresLaunchpadPallet}; - use common::fixnum::ops::CheckedAdd; - use common::prelude::FixedWrapper; - use common::{ - balance, AssetInfoProvider, AssetName, AssetSymbol, Balance, PoolXykPallet, CERES_ASSET_ID, - DEFAULT_BALANCE_PRECISION, PSWAP, XOR, XSTUSD, - }; - use frame_support::{assert_err, assert_ok, PalletId}; - use pswap_distribution::{ClaimableShares, ShareholderAccounts}; - use sp_runtime::traits::AccountIdConversion; - - fn preset_initial(tests: Fun) - where - Fun: Fn(), - { - let mut ext = ExtBuilder::default().build(); - let xor: AssetId = XOR.into(); - let xstusd: AssetId = XSTUSD.into(); - let ceres: AssetId = CERES_ASSET_ID.into(); - - ext.execute_with(|| { - assert_ok!(assets::Pallet::::register_asset_id( - ALICE, - XOR.into(), - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::from(0u32), - true, - None, - None, - )); - - assert_ok!(assets::Pallet::::register_asset_id( - ALICE, - XSTUSD.into(), - AssetSymbol(b"XSTUSD".to_vec()), - AssetName(b"SORA Synthetic USD".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::from(0u32), - true, - None, - None, - )); - - assert_ok!(assets::Pallet::::register_asset_id( - ALICE, - CERES_ASSET_ID.into(), - AssetSymbol(b"CERES".to_vec()), - AssetName(b"Ceres".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::from(0u32), - true, - None, - None, - )); - - assert_ok!(assets::Pallet::::register_asset_id( - ALICE, - GetIncentiveAssetId::get().into(), - AssetSymbol(b"XOR".to_vec()), - AssetName(b"SORA".to_vec()), - DEFAULT_BALANCE_PRECISION, - Balance::from(0u32), - true, - None, - None, - )); - - assert_ok!(assets::Pallet::::mint_to( - &xor, - &ALICE, - &ALICE, - balance!(900000) - )); - - assert_ok!(assets::Pallet::::mint_to( - &ceres, - &ALICE, - &ALICE, - balance!(1000) - )); - - assert_ok!(assets::Pallet::::mint_to( - &xor, - &ALICE, - &CHARLES, - balance!(2000) - )); - - assert_ok!(assets::Pallet::::mint_to( - &xstusd, - &ALICE, - &CHARLES, - balance!(1000) - )); - - assert_ok!(assets::Pallet::::mint_to( - &ceres, - &ALICE, - &CHARLES, - balance!(2000) - )); - - assert_ok!(assets::Pallet::::mint_to( - &ceres, - &ALICE, - &DAN, - balance!(11000) - )); - - assert_ok!(assets::Pallet::::mint_to( - &PSWAP, - &ALICE, - &GetPswapDistributionAccountId::get(), - balance!(900000) - )); - - assert_eq!( - assets::Pallet::::free_balance(&xor, &ALICE).unwrap(), - balance!(900000) - ); - assert_eq!( - assets::Pallet::::free_balance(&ceres, &ALICE).unwrap(), - balance!(16000) - ); - - assert_eq!( - assets::Pallet::::free_balance(&xor, &CHARLES).unwrap(), - balance!(2000) - ); - assert_eq!( - assets::Pallet::::free_balance(&xstusd, &CHARLES).unwrap(), - balance!(1000) - ); - assert_eq!( - assets::Pallet::::free_balance(&ceres, &CHARLES).unwrap(), - balance!(5000) - ); - - pallet::WhitelistedIloOrganizers::::append(ALICE); - pallet::WhitelistedIloOrganizers::::append(BOB); - pallet::WhitelistedIloOrganizers::::append(CHARLES); - pallet::WhitelistedIloOrganizers::::append(DAN); - - pallet::WhitelistedContributors::::append(ALICE); - pallet::WhitelistedContributors::::append(BOB); - pallet::WhitelistedContributors::::append(CHARLES); - pallet::WhitelistedContributors::::append(DAN); - - tests(); - }); - } - - #[test] - fn create_ilo_ilo_price_zero() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(1), - balance!(2), - balance!(0), - balance!(100), - balance!(150), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.6), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::ParameterCantBeZero - ); - }); - } - - #[test] - fn create_ilo_hard_cap_zero() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(1), - balance!(2), - balance!(0.25), - balance!(100), - balance!(0), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.6), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::ParameterCantBeZero - ); - }); - } - - #[test] - fn create_ilo_invalid_soft_cap() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(1), - balance!(2), - balance!(0.25), - balance!(100), - balance!(220), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.6), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidSoftCap - ); - }); - } - - #[test] - fn create_ilo_invalid_minimum_contribution() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(1), - balance!(2), - balance!(0.25), - balance!(120), - balance!(220), - balance!(0.009), - balance!(0.25), - true, - balance!(0.75), - balance!(0.6), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidMinimumContribution - ); - }); - } - - #[test] - fn create_ilo_invalid_maximum_contribution() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(1), - balance!(2), - balance!(0.25), - balance!(120), - balance!(220), - balance!(0.25), - balance!(0.25), - true, - balance!(0.75), - balance!(0.6), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidMaximumContribution - ); - }); - } - - #[test] - fn create_ilo_invalid_liquidity_percent() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(1), - balance!(2), - balance!(0.25), - balance!(120), - balance!(220), - balance!(0.2), - balance!(0.25), - true, - balance!(0.50), - balance!(0.6), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidLiquidityPercent - ); - }); - } - - #[test] - fn create_ilo_invalid_lockup_days() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(1), - balance!(2), - balance!(0.25), - balance!(120), - balance!(220), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.6), - 29, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidLockupDays - ); - }); - } - - #[test] - fn create_ilo_invalid_start_timestamp() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(1), - balance!(2), - balance!(0.25), - balance!(120), - balance!(220), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.6), - 31, - current_timestamp, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidStartTimestamp - ); - }); - } - - #[test] - fn create_ilo_invalid_end_timestamp() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(1), - balance!(2), - balance!(0.25), - balance!(120), - balance!(220), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.6), - 31, - current_timestamp + 5, - current_timestamp + 2, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidEndTimestamp - ); - }); - } - - #[test] - fn create_ilo_invalid_price() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(1), - balance!(2), - balance!(0.25), - balance!(120), - balance!(220), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.1), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidPrice - ); - }); - } - - #[test] - fn create_ilo_invalid_number_of_tokens_for_ilo() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7692.30769231), - balance!(2), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.20), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidNumberOfTokensForILO - ); - }); - } - - #[test] - fn create_ilo_invalid_number_of_tokens_for_liquidity() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(1000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.20), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidNumberOfTokensForLiquidity - ); - }); - } - - #[test] - fn create_ilo_invalid_team_first_release_percent() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - ), - Error::::InvalidTeamFirstReleasePercent - ); - }); - } - - #[test] - fn create_ilo_invalid_team_vesting_percent() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - ), - Error::::InvalidTeamVestingPercent - ); - }); - } - - #[test] - fn create_ilo_invalid_team_vesting_percent_overflow() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.9), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - ), - Error::::InvalidTeamVestingPercent - ); - }); - } - - #[test] - fn create_ilo_invalid_team_vesting_percent_remainder() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.3), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - ), - Error::::InvalidTeamVestingPercent - ); - }); - } - - #[test] - fn create_ilo_invalid_team_vesting_period() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - 0u32.into(), - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - ), - Error::::InvalidTeamVestingPeriod - ); - }); - } - - #[test] - fn create_ilo_invalid_first_release_percent() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0), - current_timestamp + 3, - balance!(20) - ), - Error::::InvalidFirstReleasePercent - ); - }); - } - - #[test] - fn create_ilo_invalid_vesting_percent() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0) - ), - Error::::InvalidVestingPercent - ); - }); - } - - #[test] - fn create_ilo_invalid_vesting_percent_overflow() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.9) - ), - Error::::InvalidVestingPercent - ); - }); - } - - #[test] - fn create_ilo_invalid_vesting_percent_remainder() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(CHARLES), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.3) - ), - Error::::InvalidVestingPercent - ); - }); - } - - #[test] - fn create_ilo_invalid_vesting_period() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - 0u32.into(), - balance!(0.2) - ), - Error::::InvalidVestingPeriod - ); - }); - } - - #[test] - fn create_ilo_not_enough_ceres() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(BOB), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - ), - Error::::NotEnoughCeres - ); - }); - } - - #[test] - fn create_ilo_not_enough_tokens() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(CHARLES), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - ), - Error::::NotEnoughTokens - ); - }); - } - - #[test] - fn create_ilo_account_is_not_whitelisted() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - ), - Error::::AccountIsNotWhitelisted - ); - }); - } - - #[test] - fn create_ilo_ok() { - let mut ext = ExtBuilder::default().build(); - ext.execute_with(|| { - pallet::WhitelistedIloOrganizers::::append(ALICE); - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +use crate::mock::*; +use crate::{pallet, Error, FeePercentOnRaisedFunds, Pallet as CeresLaunchpadPallet}; +use common::fixnum::ops::CheckedAdd; +use common::prelude::FixedWrapper; +use common::{ + balance, AssetInfoProvider, AssetName, AssetSymbol, Balance, PoolXykPallet, CERES_ASSET_ID, + DEFAULT_BALANCE_PRECISION, PSWAP, XOR, XSTUSD, +}; +use frame_support::{assert_err, assert_ok, PalletId}; +use pswap_distribution::{ClaimableShares, ShareholderAccounts}; +use sp_runtime::traits::AccountIdConversion; + +fn preset_initial(tests: Fun) +where + Fun: Fn(), +{ + let mut ext = ExtBuilder::default().build(); + let xor: AssetId = XOR; + let xstusd: AssetId = XSTUSD; + let ceres: AssetId = CERES_ASSET_ID; + + ext.execute_with(|| { + assert_ok!(assets::Pallet::::register_asset_id( + ALICE, + XOR, + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(0u32), + true, + None, + None, + )); + + assert_ok!(assets::Pallet::::register_asset_id( + ALICE, + XSTUSD, + AssetSymbol(b"XSTUSD".to_vec()), + AssetName(b"SORA Synthetic USD".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(0u32), + true, + None, + None, + )); + + assert_ok!(assets::Pallet::::register_asset_id( + ALICE, + CERES_ASSET_ID, + AssetSymbol(b"CERES".to_vec()), + AssetName(b"Ceres".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(0u32), + true, + None, + None, + )); + + assert_ok!(assets::Pallet::::register_asset_id( + ALICE, + GetIncentiveAssetId::get(), + AssetSymbol(b"XOR".to_vec()), + AssetName(b"SORA".to_vec()), + DEFAULT_BALANCE_PRECISION, + Balance::from(0u32), + true, + None, + None, + )); + + assert_ok!(assets::Pallet::::mint_to( + &xor, + &ALICE, + &ALICE, + balance!(900000) + )); + + assert_ok!(assets::Pallet::::mint_to( + &ceres, + &ALICE, + &ALICE, + balance!(1000) + )); + + assert_ok!(assets::Pallet::::mint_to( + &xor, + &ALICE, + &CHARLES, + balance!(2000) + )); + + assert_ok!(assets::Pallet::::mint_to( + &xstusd, + &ALICE, + &CHARLES, + balance!(1000) + )); + + assert_ok!(assets::Pallet::::mint_to( + &ceres, + &ALICE, + &CHARLES, + balance!(2000) + )); + + assert_ok!(assets::Pallet::::mint_to( + &ceres, + &ALICE, + &DAN, + balance!(11000) + )); + + assert_ok!(assets::Pallet::::mint_to( + &PSWAP, + &ALICE, + &GetPswapDistributionAccountId::get(), + balance!(900000) + )); + + assert_eq!( + assets::Pallet::::free_balance(&xor, &ALICE).unwrap(), + balance!(900000) + ); + assert_eq!( + assets::Pallet::::free_balance(&ceres, &ALICE).unwrap(), + balance!(16000) + ); + + assert_eq!( + assets::Pallet::::free_balance(&xor, &CHARLES).unwrap(), + balance!(2000) + ); + assert_eq!( + assets::Pallet::::free_balance(&xstusd, &CHARLES).unwrap(), + balance!(1000) + ); + assert_eq!( + assets::Pallet::::free_balance(&ceres, &CHARLES).unwrap(), + balance!(5000) + ); + + pallet::WhitelistedIloOrganizers::::append(ALICE); + pallet::WhitelistedIloOrganizers::::append(BOB); + pallet::WhitelistedIloOrganizers::::append(CHARLES); + pallet::WhitelistedIloOrganizers::::append(DAN); + + pallet::WhitelistedContributors::::append(ALICE); + pallet::WhitelistedContributors::::append(BOB); + pallet::WhitelistedContributors::::append(CHARLES); + pallet::WhitelistedContributors::::append(DAN); + + tests(); + }); +} + +#[test] +fn create_ilo_ilo_price_zero() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), + base_asset, + CERES_ASSET_ID, + balance!(1), + balance!(2), + balance!(0), + balance!(100), + balance!(150), balance!(0.2), balance!(0.25), true, balance!(0.75), - balance!(0.25), + balance!(0.6), 31, current_timestamp + 5, current_timestamp + 10, @@ -1083,49 +180,35 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &ALICE) - .expect("Failed to query free balance."), - balance!(4297) - ); - - let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &pallet_account) - .expect("Failed to query free balance."), - balance!(10693) - ); - - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - assert_ne!(ilo_info.ilo_price, balance!(0)); - assert_eq!(ilo_info.ilo_organizer, ALICE); - }); - } - - #[test] - fn create_ilo_ilo_already_exists() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + balance!(20) + ), + Error::::ParameterCantBeZero + ); + }); +} + +#[test] +fn create_ilo_hard_cap_zero() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), + base_asset, + CERES_ASSET_ID, + balance!(1), + balance!(2), + balance!(0.25), + balance!(100), + balance!(0), balance!(0.2), balance!(0.25), true, balance!(0.75), - balance!(0.25), + balance!(0.6), 31, current_timestamp + 5, current_timestamp + 10, @@ -1133,75 +216,35 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - ), - Error::::ILOAlreadyExists - ); - }); - } - - #[test] - fn contribute_ilo_does_not_exist() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into(), - balance!(0.21) - ), - Error::::ILODoesNotExist - ); - }); - } - - #[test] - fn contribute_not_enough_ceres() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + balance!(20) + ), + Error::::ParameterCantBeZero + ); + }); +} + +#[test] +fn create_ilo_invalid_soft_cap() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.5), - balance!(10), + base_asset, + CERES_ASSET_ID, + balance!(1), + balance!(2), + balance!(0.25), + balance!(100), + balance!(220), + balance!(0.2), + balance!(0.25), true, balance!(0.75), - balance!(0.25), + balance!(0.6), 31, current_timestamp + 5, current_timestamp + 10, @@ -1209,50 +252,35 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - assert_ok!(Assets::transfer_from( - &CERES_ASSET_ID.into(), - &DAN, - &BOB, - balance!(11000) - )); - - assert_err!( - CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(DAN), - CERES_ASSET_ID.into(), - balance!(0.6) - ), - Error::::NotEnoughCeres - ); - }); - } - - #[test] - fn contribute_ilo_not_started() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + balance!(20) + ), + Error::::InvalidSoftCap + ); + }); +} + +#[test] +fn create_ilo_invalid_minimum_contribution() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), + base_asset, + CERES_ASSET_ID, + balance!(1), + balance!(2), + balance!(0.25), + balance!(120), + balance!(220), + balance!(0.009), balance!(0.25), true, balance!(0.75), - balance!(0.25), + balance!(0.6), 31, current_timestamp + 5, current_timestamp + 10, @@ -1260,40 +288,35 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - assert_err!( - CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into(), - balance!(0.21) - ), - Error::::ILONotStarted - ); - }); - } - - #[test] - fn contribute_ilo_is_finished() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + balance!(20) + ), + Error::::InvalidMinimumContribution + ); + }); +} + +#[test] +fn create_ilo_invalid_maximum_contribution() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), + base_asset, + CERES_ASSET_ID, + balance!(1), + balance!(2), + balance!(0.25), + balance!(120), + balance!(220), + balance!(0.25), balance!(0.25), true, balance!(0.75), - balance!(0.25), + balance!(0.6), 31, current_timestamp + 5, current_timestamp + 10, @@ -1301,44 +324,35 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); - - assert_err!( - CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into(), - balance!(0.21) - ), - Error::::ILOIsFinished - ); - }); - } - - #[test] - fn contribute_contribution_is_lower_then_min() { - preset_initial(|| { - let asset_id = CERES_ASSET_ID; - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + balance!(20) + ), + Error::::InvalidMaximumContribution + ); + }); +} + +#[test] +fn create_ilo_invalid_liquidity_percent() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - asset_id.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), + base_asset, + CERES_ASSET_ID, + balance!(1), + balance!(2), + balance!(0.25), + balance!(120), + balance!(220), balance!(0.2), balance!(0.25), true, - balance!(0.75), - balance!(0.25), + balance!(0.50), + balance!(0.6), 31, current_timestamp + 5, current_timestamp + 10, @@ -1346,188 +360,143 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - assert_err!( - CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(ALICE), - asset_id.into(), - balance!(0.1) - ), - Error::::ContributionIsLowerThenMin - ); - }); - } - - #[test] - fn contribute_contribution_is_bigger_then_max() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + balance!(20) + ), + Error::::InvalidLiquidityPercent + ); + }); +} + +#[test] +fn create_ilo_invalid_lockup_days() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), + base_asset, + CERES_ASSET_ID, + balance!(1), + balance!(2), + balance!(0.25), + balance!(120), + balance!(220), balance!(0.2), balance!(0.25), true, balance!(0.75), - balance!(0.25), - 31, + balance!(0.6), + 29, current_timestamp + 5, current_timestamp + 10, balance!(1000), balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + balance!(20) + ), + Error::::InvalidLockupDays + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into(), - balance!(0.2) - )); - assert_err!( - CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into(), - balance!(0.2) - ), - Error::::ContributionIsBiggerThenMax - ); - }); - } - - #[test] - fn contribute_hard_cap_is_hit() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let asset_id = CERES_ASSET_ID; - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_start_timestamp() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - asset_id.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), + base_asset, + CERES_ASSET_ID, + balance!(1), + balance!(2), + balance!(0.25), + balance!(120), + balance!(220), balance!(0.2), - balance!(2000), + balance!(0.25), true, balance!(0.75), - balance!(0.25), + balance!(0.6), 31, - current_timestamp + 5, + current_timestamp, current_timestamp + 10, balance!(1000), balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + balance!(20) + ), + Error::::InvalidStartTimestamp + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into(), - balance!(100) - )); - - assert_err!( - CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(BOB), - CERES_ASSET_ID.into(), - balance!(901) - ), - Error::::HardCapIsHit - ); - }); - } - - #[test] - fn contribute_account_is_not_whitelisted() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_end_timestamp() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.5), - balance!(10), + base_asset, + CERES_ASSET_ID, + balance!(1), + balance!(2), + balance!(0.25), + balance!(120), + balance!(220), + balance!(0.2), + balance!(0.25), true, balance!(0.75), - balance!(0.25), + balance!(0.6), 31, current_timestamp + 5, - current_timestamp + 10, + current_timestamp + 2, balance!(1000), balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - assert_err!( - CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(EMILY), - CERES_ASSET_ID.into(), - balance!(0.6) - ), - Error::::AccountIsNotWhitelisted - ); - }); - } - - #[test] - fn contribute_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let asset_id = CERES_ASSET_ID; - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + balance!(20) + ), + Error::::InvalidEndTimestamp + ); + }); +} + +#[test] +fn create_ilo_invalid_price() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - asset_id.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), + base_asset, + CERES_ASSET_ID, + balance!(1), + balance!(2), + balance!(0.25), + balance!(120), + balance!(220), balance!(0.2), balance!(0.25), true, balance!(0.75), - balance!(0.25), + balance!(0.1), 31, current_timestamp + 5, current_timestamp + 10, @@ -1535,54 +504,27 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - let funds_to_contribute = balance!(0.21); + balance!(20) + ), + Error::::InvalidPrice + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); - - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - assert_eq!(ilo_info.funds_raised, funds_to_contribute); - let tokens_bought = (FixedWrapper::from(funds_to_contribute) - / FixedWrapper::from(ilo_info.ilo_price)) - .try_into_balance() - .unwrap_or(0); - - let contribution_info = - pallet::Contributions::::get(&CERES_ASSET_ID, &CHARLES); - - assert_eq!(ilo_info.sold_tokens, tokens_bought); - assert_eq!(contribution_info.funds_contributed, funds_to_contribute); - assert_eq!(contribution_info.tokens_bought, tokens_bought); - - assert_eq!( - Assets::free_balance(&base_asset.into(), &CHARLES) - .expect("Failed to query free balance."), - balance!(1999.79) - ); - }); - } - - #[test] - fn contribute_base_asset_xstusd_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let asset_id = CERES_ASSET_ID; - let base_asset = XSTUSD; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_number_of_tokens_for_ilo() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - asset_id.into(), - balance!(7693), - balance!(3000), + base_asset, + CERES_ASSET_ID, + balance!(7692.30769231), + balance!(2), balance!(0.13), balance!(600), balance!(1000), @@ -1590,7 +532,7 @@ mod tests { balance!(0.25), true, balance!(0.75), - balance!(0.25), + balance!(0.20), 31, current_timestamp + 5, current_timestamp + 10, @@ -1598,66 +540,27 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - let funds_to_contribute = balance!(0.21); + balance!(20) + ), + Error::::InvalidNumberOfTokensForILO + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); - - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - assert_eq!(ilo_info.funds_raised, funds_to_contribute); - let tokens_bought = (FixedWrapper::from(funds_to_contribute) - / FixedWrapper::from(ilo_info.ilo_price)) - .try_into_balance() - .unwrap_or(0); - - let contribution_info = - pallet::Contributions::::get(&CERES_ASSET_ID, &CHARLES); - - assert_eq!(ilo_info.sold_tokens, tokens_bought); - assert_eq!(contribution_info.funds_contributed, funds_to_contribute); - assert_eq!(contribution_info.tokens_bought, tokens_bought); - - assert_eq!( - Assets::free_balance(&base_asset.into(), &CHARLES) - .expect("Failed to query free balance."), - balance!(999.79) - ); - }); - } - - #[test] - fn emergency_withdraw_ilo_does_not_exist() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::emergency_withdraw( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ), - Error::::ILODoesNotExist - ); - }); - } - - #[test] - fn emergency_withdraw_ilo_not_started() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_number_of_tokens_for_liquidity() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), - balance!(3000), + balance!(1000), balance!(0.13), balance!(600), balance!(1000), @@ -1665,7 +568,7 @@ mod tests { balance!(0.25), true, balance!(0.75), - balance!(0.25), + balance!(0.20), 31, current_timestamp + 5, current_timestamp + 10, @@ -1673,30 +576,25 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0.1), current_timestamp + 3, - balance!(0.2) - )); - - assert_err!( - CeresLaunchpadPallet::::emergency_withdraw( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ), - Error::::ILONotStarted - ); - }); - } - - #[test] - fn emergency_withdraw_ilo_is_finished() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + balance!(20) + ), + Error::::InvalidNumberOfTokensForLiquidity + ); + }); +} + +#[test] +fn create_ilo_invalid_team_first_release_percent() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -1711,35 +609,28 @@ mod tests { current_timestamp + 5, current_timestamp + 10, balance!(1000), - balance!(0.2), + balance!(0), current_timestamp + 3, balance!(0.2), balance!(0.2), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + balance!(0.2), + ), + Error::::InvalidTeamFirstReleasePercent + ); + }); +} - assert_err!( - CeresLaunchpadPallet::::emergency_withdraw( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ), - Error::::ILOIsFinished - ); - }); - } - - #[test] - fn emergency_withdraw_not_enough_funds() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_team_vesting_percent() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -1756,33 +647,26 @@ mod tests { balance!(1000), balance!(0.2), current_timestamp + 3, - balance!(0.2), + balance!(0), balance!(0.2), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + balance!(0.2), + ), + Error::::InvalidTeamVestingPercent + ); + }); +} - assert_err!( - CeresLaunchpadPallet::::emergency_withdraw( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ), - Error::::NotEnoughFunds - ); - }); - } - - #[test] - fn emergency_withdraw_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_team_vesting_percent_overflow() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -1799,71 +683,26 @@ mod tests { balance!(1000), balance!(0.2), current_timestamp + 3, - balance!(0.2), + balance!(0.9), balance!(0.2), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - let funds_to_contribute = balance!(0.21); - - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); - - let mut contribution_info = - pallet::Contributions::::get(&CERES_ASSET_ID, &CHARLES); - - assert_ok!(CeresLaunchpadPallet::::emergency_withdraw( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - )); - - let funds_to_claim = (FixedWrapper::from(contribution_info.funds_contributed) - * FixedWrapper::from(0.8)) - .try_into_balance() - .unwrap_or(0); - - let penalty = contribution_info.funds_contributed - funds_to_claim; - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - - assert_eq!( - Assets::free_balance(&base_asset.into(), &CHARLES) - .expect("Failed to query free balance."), - balance!(1999.958) - ); - - assert_eq!( - Assets::free_balance( - &base_asset.into(), - &pallet::PenaltiesAccount::::get() - ) - .expect("Failed to query free balance."), - penalty - ); - - assert_eq!(ilo_info.funds_raised, balance!(0)); - assert_eq!(ilo_info.sold_tokens, balance!(0)); - - contribution_info = pallet::Contributions::::get(&CERES_ASSET_ID, &CHARLES); + balance!(0.2), + ), + Error::::InvalidTeamVestingPercent + ); + }); +} - assert_eq!(contribution_info.funds_contributed, balance!(0)); - }); - } - - #[test] - fn emergency_withdraw_base_asset_xstusd_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XSTUSD; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_team_vesting_percent_remainder() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -1880,84 +719,26 @@ mod tests { balance!(1000), balance!(0.2), current_timestamp + 3, - balance!(0.2), + balance!(0.3), balance!(0.2), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - let funds_to_contribute = balance!(0.21); - - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); - - let mut contribution_info = - pallet::Contributions::::get(&CERES_ASSET_ID, &CHARLES); - - assert_ok!(CeresLaunchpadPallet::::emergency_withdraw( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - )); - - let funds_to_claim = (FixedWrapper::from(contribution_info.funds_contributed) - * FixedWrapper::from(0.8)) - .try_into_balance() - .unwrap_or(0); - - let penalty = contribution_info.funds_contributed - funds_to_claim; - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - - assert_eq!( - Assets::free_balance(&base_asset.into(), &CHARLES) - .expect("Failed to query free balance."), - balance!(999.958) - ); - - assert_eq!( - Assets::free_balance( - &base_asset.into(), - &pallet::PenaltiesAccount::::get() - ) - .expect("Failed to query free balance."), - penalty - ); - - assert_eq!(ilo_info.funds_raised, balance!(0)); - assert_eq!(ilo_info.sold_tokens, balance!(0)); - - contribution_info = pallet::Contributions::::get(&CERES_ASSET_ID, &CHARLES); + balance!(0.2), + ), + Error::::InvalidTeamVestingPercent + ); + }); +} - assert_eq!(contribution_info.funds_contributed, balance!(0)); - }); - } - - #[test] - fn finish_ilo_ilo_does_not_exist() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ), - Error::::ILODoesNotExist - ); - }); - } - - #[test] - fn finish_ilo_ilo_is_not_finished() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_team_vesting_period() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -1973,32 +754,27 @@ mod tests { current_timestamp + 10, balance!(1000), balance!(0.2), - current_timestamp + 3, + 0u32.into(), balance!(0.2), balance!(0.2), current_timestamp + 3, - balance!(0.2) - )); - - assert_err!( - CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ), - Error::::ILOIsNotFinished - ); - }); - } - - #[test] - fn finish_ilo_unauthorized() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + balance!(0.2), + ), + Error::::InvalidTeamVestingPeriod + ); + }); +} + +#[test] +fn create_ilo_invalid_first_release_percent() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -2016,32 +792,25 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2), - balance!(0.2), + balance!(0), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + balance!(20) + ), + Error::::InvalidFirstReleasePercent + ); + }); +} - assert_err!( - CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into() - ), - Error::::Unauthorized - ); - }); - } - - #[test] - fn finish_ilo_ilo_failed_refunded_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_vesting_percent() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -2061,43 +830,23 @@ mod tests { balance!(0.2), balance!(0.2), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); - - assert_ok!(CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); - - let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &pallet_account) - .expect("Failed to query free balance."), balance!(0) - ); - - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &ALICE) - .expect("Failed to query free balance."), - balance!(15990) - ); + ), + Error::::InvalidVestingPercent + ); + }); +} - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - assert_eq!(ilo_info.failed, true); - }); - } - - #[test] - fn finish_ilo_ilo_failed_burned_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_vesting_percent_overflow() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -2105,7 +854,7 @@ mod tests { balance!(1000), balance!(0.2), balance!(0.25), - false, + true, balance!(0.75), balance!(0.25), 31, @@ -2117,43 +866,23 @@ mod tests { balance!(0.2), balance!(0.2), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); - - assert_ok!(CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); - - let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &pallet_account) - .expect("Failed to query free balance."), - balance!(0) - ); - - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &ALICE) - .expect("Failed to query free balance."), - balance!(5297) - ); + balance!(0.9) + ), + Error::::InvalidVestingPercent + ); + }); +} - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - assert_eq!(ilo_info.failed, true); - }); - } - - #[test] - fn finish_ilo_ilo_is_failed() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), +#[test] +fn create_ilo_invalid_vesting_percent_remainder() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(CHARLES), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -2173,149 +902,31 @@ mod tests { balance!(0.2), balance!(0.2), current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + balance!(0.3) + ), + Error::::InvalidVestingPercent + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); - - assert_err!( - CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ), - Error::::ILOIsFailed - ); - }); - } - - #[test] - fn finish_ilo_not_filled_hard_cap_ok() { - preset_initial(|| { - let mut current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_invalid_vesting_period() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), balance!(600), balance!(1000), balance!(0.2), - balance!(1500), - false, - balance!(0.75), balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - let funds_to_contribute = balance!(800); - - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); - - current_timestamp = pallet_timestamp::Pallet::::get(); - assert_ok!(CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); - - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - - let funds_raised_fee = (FixedWrapper::from(ilo_info.funds_raised) - * FixedWrapper::from(FeePercentOnRaisedFunds::::get())) - .try_into_balance() - .unwrap_or(0); - let raised_funds_without_fee = ilo_info.funds_raised - funds_raised_fee; - let funds_for_liquidity = (FixedWrapper::from(raised_funds_without_fee) - * FixedWrapper::from(ilo_info.liquidity_percent)) - .try_into_balance() - .unwrap_or(0); - let funds_for_team = raised_funds_without_fee - funds_for_liquidity; - - assert_eq!( - Assets::free_balance(&base_asset, &pallet::AuthorityAccount::::get()) - .expect("Failed to query free balance."), - funds_raised_fee - ); - assert_eq!( - Assets::free_balance(&base_asset, &ALICE).expect("Failed to query free balance."), - funds_for_team + balance!(900000) - ); - - let (xor_liq, ceres_liq) = - pool_xyk::Reserves::::get(base_asset, CERES_ASSET_ID); - assert_eq!(xor_liq, funds_for_liquidity); - - let tokens_for_liquidity = (FixedWrapper::from(funds_for_liquidity) - / FixedWrapper::from(ilo_info.listing_price)) - .try_into_balance() - .unwrap_or(0); - assert_eq!(ceres_liq, tokens_for_liquidity); - - let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &pallet_account) - .expect("Failed to query free balance."), - ilo_info.sold_tokens - ); - - assert_err!( - pool_xyk::Pallet::::withdraw_liquidity( - RuntimeOrigin::signed(pallet_account), - DEX_A_ID, - base_asset.into(), - CERES_ASSET_ID.into(), - ilo_info.lp_tokens, - balance!(1), - balance!(1) - ), - pool_xyk::Error::::NotEnoughUnlockedLiquidity - ); - - assert_eq!(ilo_info.finish_timestamp, current_timestamp); - }); - } - - #[test] - fn finish_ilo_not_enough_team_tokens_to_lock() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(DAN), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(1500), - false, + true, balance!(0.75), balance!(0.25), 31, @@ -2326,164 +937,32 @@ mod tests { current_timestamp + 3, balance!(0.2), balance!(0.2), - current_timestamp + 3, + 0u32.into(), balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - let funds_to_contribute = balance!(1000); + ), + Error::::InvalidVestingPeriod + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); - - assert_err!( - CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(DAN), - CERES_ASSET_ID.into() - ), - Error::::NotEnoughTeamTokensToLock - ); - }); - } - - #[test] - fn finish_ilo_filled_hard_cap_ok() { - preset_initial(|| { - let mut current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), +#[test] +fn create_ilo_not_enough_ceres() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(BOB), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), balance!(600), balance!(1000), balance!(0.2), - balance!(1500), - false, - balance!(0.75), balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - let funds_to_contribute = balance!(1000); - - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); - - current_timestamp = pallet_timestamp::Pallet::::get(); - assert_ok!(CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); - - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - - let funds_raised_fee = (FixedWrapper::from(ilo_info.funds_raised) - * FixedWrapper::from(FeePercentOnRaisedFunds::::get())) - .try_into_balance() - .unwrap_or(0); - let raised_funds_without_fee = ilo_info.funds_raised - funds_raised_fee; - let funds_for_liquidity = (FixedWrapper::from(raised_funds_without_fee) - * FixedWrapper::from(ilo_info.liquidity_percent)) - .try_into_balance() - .unwrap_or(0); - let funds_for_team = raised_funds_without_fee - funds_for_liquidity; - - assert_eq!( - Assets::free_balance(&base_asset, &pallet::AuthorityAccount::::get()) - .expect("Failed to query free balance."), - funds_raised_fee - ); - assert_eq!( - Assets::free_balance(&base_asset, &ALICE).expect("Failed to query free balance."), - funds_for_team + balance!(900000) - ); - - let (xor_liq, ceres_liq) = - pool_xyk::Reserves::::get(base_asset, CERES_ASSET_ID); - assert_eq!(xor_liq, funds_for_liquidity); - - let tokens_for_liquidity = (FixedWrapper::from(funds_for_liquidity) - / FixedWrapper::from(ilo_info.listing_price)) - .try_into_balance() - .unwrap_or(0); - assert_eq!(ceres_liq, tokens_for_liquidity); - - let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &pallet_account) - .expect("Failed to query free balance."), - ilo_info.sold_tokens - ); - - assert_err!( - pool_xyk::Pallet::::withdraw_liquidity( - RuntimeOrigin::signed(pallet_account), - DEX_A_ID, - base_asset.into(), - CERES_ASSET_ID.into(), - ilo_info.lp_tokens, - balance!(1), - balance!(1) - ), - pool_xyk::Error::::NotEnoughUnlockedLiquidity - ); - - let token_locker_data = ceres_token_locker::TokenLockerData::::get(ALICE); - assert_eq!(token_locker_data.len(), 4 as usize); - let mut unlocking_timestamp = - current_timestamp + ilo_info.team_vesting.team_vesting_period; - for token_lock_info in token_locker_data { - assert_eq!(token_lock_info.asset_id, CERES_ASSET_ID.into()); - assert_eq!(token_lock_info.tokens, balance!(200)); - assert_eq!(token_lock_info.unlocking_timestamp, unlocking_timestamp); - unlocking_timestamp += ilo_info.team_vesting.team_vesting_period; - } - - assert_eq!(ilo_info.finish_timestamp, current_timestamp); - }); - } - - #[test] - fn finish_ilo_filled_hard_cap_base_asset_xstusd_ok() { - preset_initial(|| { - let mut current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XSTUSD; - assert_ok!(CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(1500), - false, + true, balance!(0.75), balance!(0.25), 31, @@ -2496,114 +975,22 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - let funds_to_contribute = balance!(1000); + ), + Error::::NotEnoughCeres + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( +#[test] +fn create_ilo_not_enough_tokens() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); - - current_timestamp = pallet_timestamp::Pallet::::get(); - assert_ok!(CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); - - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - - let funds_raised_fee = (FixedWrapper::from(ilo_info.funds_raised) - * FixedWrapper::from(FeePercentOnRaisedFunds::::get())) - .try_into_balance() - .unwrap_or(0); - let raised_funds_without_fee = ilo_info.funds_raised - funds_raised_fee; - let funds_for_liquidity = (FixedWrapper::from(raised_funds_without_fee) - * FixedWrapper::from(ilo_info.liquidity_percent)) - .try_into_balance() - .unwrap_or(0); - let funds_for_team = raised_funds_without_fee - funds_for_liquidity; - - assert_eq!( - Assets::free_balance(&base_asset, &pallet::AuthorityAccount::::get()) - .expect("Failed to query free balance."), - funds_raised_fee - ); - assert_eq!( - Assets::free_balance(&base_asset, &ALICE).expect("Failed to query free balance."), - funds_for_team - ); - - let (xor_liq, ceres_liq) = - pool_xyk::Reserves::::get(base_asset, CERES_ASSET_ID); - assert_eq!(xor_liq, funds_for_liquidity); - - let tokens_for_liquidity = (FixedWrapper::from(funds_for_liquidity) - / FixedWrapper::from(ilo_info.listing_price)) - .try_into_balance() - .unwrap_or(0); - assert_eq!(ceres_liq, tokens_for_liquidity); - - let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &pallet_account) - .expect("Failed to query free balance."), - ilo_info.sold_tokens - ); - - assert_err!( - pool_xyk::Pallet::::withdraw_liquidity( - RuntimeOrigin::signed(pallet_account), - DEX_B_ID, - base_asset.into(), - CERES_ASSET_ID.into(), - ilo_info.lp_tokens, - balance!(1), - balance!(1) - ), - pool_xyk::Error::::NotEnoughUnlockedLiquidity - ); - - let token_locker_data = ceres_token_locker::TokenLockerData::::get(ALICE); - assert_eq!(token_locker_data.len(), 4 as usize); - let mut unlocking_timestamp = - current_timestamp + ilo_info.team_vesting.team_vesting_period; - for token_lock_info in token_locker_data { - assert_eq!(token_lock_info.asset_id, CERES_ASSET_ID.into()); - assert_eq!(token_lock_info.tokens, balance!(200)); - assert_eq!(token_lock_info.unlocking_timestamp, unlocking_timestamp); - unlocking_timestamp += ilo_info.team_vesting.team_vesting_period; - } - - assert_eq!(ilo_info.finish_timestamp, current_timestamp); - }); - } - - #[test] - fn claim_ilo_does_not_exist() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - ), - Error::::ILODoesNotExist - ); - }); - } - - #[test] - fn claim_ilo_is_not_finished() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -2624,27 +1011,23 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2) - )); - - assert_err!( - CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - ), - Error::::ILOIsNotFinished - ); - }); - } - - #[test] - fn claim_ilo_failed_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + ), + Error::::NotEnoughTokens + ); + }); +} + +#[test] +fn create_ilo_account_is_not_whitelisted() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -2665,51 +1048,99 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - let funds_to_contribute = balance!(0.21); - - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); + ), + Error::::AccountIsNotWhitelisted + ); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); +#[test] +fn create_ilo_ok() { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + pallet::WhitelistedIloOrganizers::::append(ALICE); + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &ALICE).expect("Failed to query free balance."), + balance!(4297) + ); + + let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &pallet_account) + .expect("Failed to query free balance."), + balance!(10693) + ); - assert_ok!(CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + assert_ne!(ilo_info.ilo_price, balance!(0)); + assert_eq!(ilo_info.ilo_organizer, ALICE); + }); +} - assert_ok!(CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - )); - - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &CHARLES) - .expect("Failed to query free balance."), - balance!(5000) - ); - - let contribution_info = - pallet::Contributions::::get(&CERES_ASSET_ID, &CHARLES); - assert_eq!(contribution_info.claiming_finished, true); - }); - } - - #[test] - fn claim_funds_already_claimed() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn create_ilo_ilo_already_exists() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), @@ -2730,812 +1161,2532 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2) - )); + ), + Error::::ILOAlreadyExists + ); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); +#[test] +fn contribute_ilo_does_not_exist() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID, + balance!(0.21) + ), + Error::::ILODoesNotExist + ); + }); +} - let funds_to_contribute = balance!(0.21); +#[test] +fn contribute_not_enough_ceres() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.5), + balance!(10), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + assert_ok!(Assets::transfer_from( + &CERES_ASSET_ID, + &DAN, + &BOB, + balance!(11000) + )); + + assert_err!( + CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(DAN), + CERES_ASSET_ID, + balance!(0.6) + ), + Error::::NotEnoughCeres + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); +#[test] +fn contribute_ilo_not_started() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + assert_err!( + CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID, + balance!(0.21) + ), + Error::::ILONotStarted + ); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); +#[test] +fn contribute_ilo_is_finished() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_err!( + CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID, + balance!(0.21) + ), + Error::::ILOIsFinished + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::finish_ilo( +#[test] +fn contribute_contribution_is_lower_then_min() { + preset_initial(|| { + let asset_id = CERES_ASSET_ID; + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + asset_id, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + assert_err!( + CeresLaunchpadPallet::::contribute( RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); + asset_id, + balance!(0.1) + ), + Error::::ContributionIsLowerThenMin + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - )); - - assert_err!( - CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - ), - Error::::FundsAlreadyClaimed - ); - }); - } - - #[test] - fn claim_first_release_claim_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn contribute_contribution_is_bigger_then_max() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID, + balance!(0.2) + )); + assert_err!( + CeresLaunchpadPallet::::contribute( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(1500), - false, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, + CERES_ASSET_ID, balance!(0.2) - )); + ), + Error::::ContributionIsBiggerThenMax + ); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); +#[test] +fn contribute_hard_cap_is_hit() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let asset_id = CERES_ASSET_ID; + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + asset_id, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(2000), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID, + balance!(100) + )); + + assert_err!( + CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(BOB), + CERES_ASSET_ID, + balance!(901) + ), + Error::::HardCapIsHit + ); + }); +} - let funds_to_contribute = balance!(1000); +#[test] +fn contribute_account_is_not_whitelisted() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.5), + balance!(10), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + assert_err!( + CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(EMILY), + CERES_ASSET_ID, + balance!(0.6) + ), + Error::::AccountIsNotWhitelisted + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); +#[test] +fn contribute_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let asset_id = CERES_ASSET_ID; + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + asset_id, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + let funds_to_contribute = balance!(0.21); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + assert_eq!(ilo_info.funds_raised, funds_to_contribute); + let tokens_bought = (FixedWrapper::from(funds_to_contribute) + / FixedWrapper::from(ilo_info.ilo_price)) + .try_into_balance() + .unwrap_or(0); + + let contribution_info = pallet::Contributions::::get(CERES_ASSET_ID, CHARLES); + + assert_eq!(ilo_info.sold_tokens, tokens_bought); + assert_eq!(contribution_info.funds_contributed, funds_to_contribute); + assert_eq!(contribution_info.tokens_bought, tokens_bought); + + assert_eq!( + Assets::free_balance(&base_asset, &CHARLES).expect("Failed to query free balance."), + balance!(1999.79) + ); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); +#[test] +fn contribute_base_asset_xstusd_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let asset_id = CERES_ASSET_ID; + let base_asset = XSTUSD; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + asset_id, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + let funds_to_contribute = balance!(0.21); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + assert_eq!(ilo_info.funds_raised, funds_to_contribute); + let tokens_bought = (FixedWrapper::from(funds_to_contribute) + / FixedWrapper::from(ilo_info.ilo_price)) + .try_into_balance() + .unwrap_or(0); + + let contribution_info = pallet::Contributions::::get(CERES_ASSET_ID, CHARLES); + + assert_eq!(ilo_info.sold_tokens, tokens_bought); + assert_eq!(contribution_info.funds_contributed, funds_to_contribute); + assert_eq!(contribution_info.tokens_bought, tokens_bought); + + assert_eq!( + Assets::free_balance(&base_asset, &CHARLES).expect("Failed to query free balance."), + balance!(999.79) + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::finish_ilo( +#[test] +fn emergency_withdraw_ilo_does_not_exist() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::emergency_withdraw( RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); - - assert_ok!(CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - )); - - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - let contribution_info = - pallet::Contributions::::get(&CERES_ASSET_ID, &CHARLES); - - let tokens_to_claim = (FixedWrapper::from(contribution_info.tokens_bought) - * FixedWrapper::from(ilo_info.contributors_vesting.first_release_percent)) - .try_into_balance() - .unwrap_or(0); - - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &CHARLES) - .expect("Failed to query free balance."), - balance!(5000) + tokens_to_claim - ); + CERES_ASSET_ID + ), + Error::::ILODoesNotExist + ); + }); +} - assert_eq!(contribution_info.tokens_claimed, tokens_to_claim); - }); - } - - #[test] - fn claim_no_potential_claims() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn emergency_withdraw_ilo_not_started() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + assert_err!( + CeresLaunchpadPallet::::emergency_withdraw( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(1500), - false, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 50, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - let funds_to_contribute = balance!(1000); - - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + CERES_ASSET_ID + ), + Error::::ILONotStarted + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::finish_ilo( +#[test] +fn emergency_withdraw_ilo_is_finished() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_err!( + CeresLaunchpadPallet::::emergency_withdraw( RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); + CERES_ASSET_ID + ), + Error::::ILOIsFinished + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 12); - - assert_err!( - CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - ), - Error::::NothingToClaim - ); - }); - } - - #[test] - fn claim_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn emergency_withdraw_not_enough_funds() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + assert_err!( + CeresLaunchpadPallet::::emergency_withdraw( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(1500), - false, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.1), - 30u32.into(), - balance!(0.18) - )); + CERES_ASSET_ID + ), + Error::::NotEnoughFunds + ); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); +#[test] +fn emergency_withdraw_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(0.21); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + let mut contribution_info = pallet::Contributions::::get(CERES_ASSET_ID, CHARLES); + + assert_ok!(CeresLaunchpadPallet::::emergency_withdraw( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + )); + + let funds_to_claim = (FixedWrapper::from(contribution_info.funds_contributed) + * FixedWrapper::from(0.8)) + .try_into_balance() + .unwrap_or(0); + + let penalty = contribution_info.funds_contributed - funds_to_claim; + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + + assert_eq!( + Assets::free_balance(&base_asset, &CHARLES).expect("Failed to query free balance."), + balance!(1999.958) + ); + + assert_eq!( + Assets::free_balance(&base_asset, &pallet::PenaltiesAccount::::get()) + .expect("Failed to query free balance."), + penalty + ); - let funds_to_contribute = balance!(1000); + assert_eq!(ilo_info.funds_raised, balance!(0)); + assert_eq!(ilo_info.sold_tokens, balance!(0)); - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); + contribution_info = pallet::Contributions::::get(CERES_ASSET_ID, CHARLES); - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + assert_eq!(contribution_info.funds_contributed, balance!(0)); + }); +} - assert_ok!(CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); +#[test] +fn emergency_withdraw_base_asset_xstusd_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XSTUSD; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(0.21); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + let mut contribution_info = pallet::Contributions::::get(CERES_ASSET_ID, CHARLES); + + assert_ok!(CeresLaunchpadPallet::::emergency_withdraw( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + )); + + let funds_to_claim = (FixedWrapper::from(contribution_info.funds_contributed) + * FixedWrapper::from(0.8)) + .try_into_balance() + .unwrap_or(0); + + let penalty = contribution_info.funds_contributed - funds_to_claim; + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + + assert_eq!( + Assets::free_balance(&base_asset, &CHARLES).expect("Failed to query free balance."), + balance!(999.958) + ); + + assert_eq!( + Assets::free_balance(&base_asset, &pallet::PenaltiesAccount::::get()) + .expect("Failed to query free balance."), + penalty + ); - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - let mut contribution_info = - pallet::Contributions::::get(&CERES_ASSET_ID, &CHARLES); + assert_eq!(ilo_info.funds_raised, balance!(0)); + assert_eq!(ilo_info.sold_tokens, balance!(0)); - let first_release = (FixedWrapper::from(contribution_info.tokens_bought) - * FixedWrapper::from(ilo_info.contributors_vesting.first_release_percent)) - .try_into_balance() - .unwrap_or(0); + contribution_info = pallet::Contributions::::get(CERES_ASSET_ID, CHARLES); - let tokens_per_claim = (FixedWrapper::from(contribution_info.tokens_bought) - * FixedWrapper::from(ilo_info.contributors_vesting.vesting_percent)) - .try_into_balance() - .unwrap_or(0); + assert_eq!(contribution_info.funds_contributed, balance!(0)); + }); +} - assert_ok!(CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - )); +#[test] +fn finish_ilo_ilo_does_not_exist() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ), + Error::::ILODoesNotExist + ); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 43); +#[test] +fn finish_ilo_ilo_is_not_finished() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + assert_err!( + CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ), + Error::::ILOIsNotFinished + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::claim( +#[test] +fn finish_ilo_unauthorized() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_err!( + CeresLaunchpadPallet::::finish_ilo( RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - )); + CERES_ASSET_ID + ), + Error::::Unauthorized + ); + }); +} - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &CHARLES) - .expect("Failed to query free balance."), - balance!(5000) + first_release + tokens_per_claim - ); +#[test] +fn finish_ilo_ilo_failed_refunded_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &pallet_account) + .expect("Failed to query free balance."), + balance!(0) + ); - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 103); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &ALICE).expect("Failed to query free balance."), + balance!(15990) + ); - assert_ok!(CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - )); - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &CHARLES) - .expect("Failed to query free balance."), - balance!(5000) + first_release + tokens_per_claim * 3 - ); + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + assert!(ilo_info.failed); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 163); +#[test] +fn finish_ilo_ilo_failed_burned_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + false, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &pallet_account) + .expect("Failed to query free balance."), + balance!(0) + ); - assert_ok!(CeresLaunchpadPallet::::claim( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - )); - contribution_info = pallet::Contributions::::get(&CERES_ASSET_ID, &CHARLES); - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &CHARLES) - .expect("Failed to query free balance."), - balance!(5000) + first_release + tokens_per_claim * 5 - ); - assert_eq!(contribution_info.claiming_finished, true); - }); - } - - #[test] - fn change_fee_percent_for_raised_funds_unauthorized() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::change_fee_percent_for_raised_funds( - RuntimeOrigin::signed(ALICE), - balance!(0.02) - ), - Error::::Unauthorized - ); - }); - } - - #[test] - fn change_fee_percent_for_raised_funds_invalid_fee_percent() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::change_fee_percent_for_raised_funds( - RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), - balance!(1.2) - ), - Error::::InvalidFeePercent - ); - }); - } - - #[test] - fn change_fee_percent_for_raised_funds_ok() { - preset_initial(|| { - assert_ok!( - CeresLaunchpadPallet::::change_fee_percent_for_raised_funds( - RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), - balance!(0.02) - ) - ); - - assert_eq!( - pallet::FeePercentOnRaisedFunds::::get(), - balance!(0.02) - ); - }); - } - - #[test] - fn change_ceres_burn_fee_unauthorized() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::change_ceres_burn_fee( - RuntimeOrigin::signed(ALICE), - balance!(100) - ), - Error::::Unauthorized - ); - }); - } + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &ALICE).expect("Failed to query free balance."), + balance!(5297) + ); - #[test] - fn change_ceres_burn_fee_ok() { - preset_initial(|| { - assert_ok!(CeresLaunchpadPallet::::change_ceres_burn_fee( - RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), - balance!(100) - )); + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + assert!(ilo_info.failed); + }); +} - assert_eq!(pallet::CeresBurnFeeAmount::::get(), balance!(100)); - }); - } - - #[test] - fn claim_lp_tokens_ilo_does_not_exist() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::claim_lp_tokens( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ), - Error::::ILODoesNotExist - ); - }); - } - - #[test] - fn claim_lp_tokens_cant_claim_lp_tokens() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn finish_ilo_ilo_is_failed() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + assert_err!( + CeresLaunchpadPallet::::finish_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - )); - - assert_err!( - CeresLaunchpadPallet::::claim_lp_tokens( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ), - Error::::CantClaimLPTokens - ); - }); - } - - #[test] - fn claim_lp_tokens_unauthorized() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(850), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + CERES_ASSET_ID + ), + Error::::ILOIsFailed + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - balance!(800) - ),); +#[test] +fn finish_ilo_not_filled_hard_cap_ok() { + preset_initial(|| { + let mut current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(1500), + false, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(800); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + current_timestamp = pallet_timestamp::Pallet::::get(); + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + + let funds_raised_fee = (FixedWrapper::from(ilo_info.funds_raised) + * FixedWrapper::from(FeePercentOnRaisedFunds::::get())) + .try_into_balance() + .unwrap_or(0); + let raised_funds_without_fee = ilo_info.funds_raised - funds_raised_fee; + let funds_for_liquidity = (FixedWrapper::from(raised_funds_without_fee) + * FixedWrapper::from(ilo_info.liquidity_percent)) + .try_into_balance() + .unwrap_or(0); + let funds_for_team = raised_funds_without_fee - funds_for_liquidity; + + assert_eq!( + Assets::free_balance(&base_asset, &pallet::AuthorityAccount::::get()) + .expect("Failed to query free balance."), + funds_raised_fee + ); + assert_eq!( + Assets::free_balance(&base_asset, &ALICE).expect("Failed to query free balance."), + funds_for_team + balance!(900000) + ); + + let (xor_liq, ceres_liq) = pool_xyk::Reserves::::get(base_asset, CERES_ASSET_ID); + assert_eq!(xor_liq, funds_for_liquidity); + + let tokens_for_liquidity = (FixedWrapper::from(funds_for_liquidity) + / FixedWrapper::from(ilo_info.listing_price)) + .try_into_balance() + .unwrap_or(0); + assert_eq!(ceres_liq, tokens_for_liquidity); + + let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &pallet_account) + .expect("Failed to query free balance."), + ilo_info.sold_tokens + ); + + assert_err!( + pool_xyk::Pallet::::withdraw_liquidity( + RuntimeOrigin::signed(pallet_account), + DEX_A_ID, + base_asset, + CERES_ASSET_ID, + ilo_info.lp_tokens, + balance!(1), + balance!(1) + ), + pool_xyk::Error::::NotEnoughUnlockedLiquidity + ); + + assert_eq!(ilo_info.finish_timestamp, current_timestamp); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); +#[test] +fn finish_ilo_not_enough_team_tokens_to_lock() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(DAN), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(1500), + false, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(1000); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_err!( + CeresLaunchpadPallet::::finish_ilo(RuntimeOrigin::signed(DAN), CERES_ASSET_ID), + Error::::NotEnoughTeamTokensToLock + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); - - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - let unlocking_timestamp = ilo_info - .finish_timestamp - .saturating_add(86_400_000u64.saturating_mul(ilo_info.lockup_days.into())); - - pallet_timestamp::Pallet::::set_timestamp(unlocking_timestamp + 1); - - assert_err!( - CeresLaunchpadPallet::::claim_lp_tokens( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into() - ), - Error::::Unauthorized - ); - }); - } - - #[test] - fn claim_lp_tokens_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(850), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - )); +#[test] +fn finish_ilo_filled_hard_cap_ok() { + preset_initial(|| { + let mut current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(1500), + false, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(1000); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + current_timestamp = pallet_timestamp::Pallet::::get(); + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + + let funds_raised_fee = (FixedWrapper::from(ilo_info.funds_raised) + * FixedWrapper::from(FeePercentOnRaisedFunds::::get())) + .try_into_balance() + .unwrap_or(0); + let raised_funds_without_fee = ilo_info.funds_raised - funds_raised_fee; + let funds_for_liquidity = (FixedWrapper::from(raised_funds_without_fee) + * FixedWrapper::from(ilo_info.liquidity_percent)) + .try_into_balance() + .unwrap_or(0); + let funds_for_team = raised_funds_without_fee - funds_for_liquidity; + + assert_eq!( + Assets::free_balance(&base_asset, &pallet::AuthorityAccount::::get()) + .expect("Failed to query free balance."), + funds_raised_fee + ); + assert_eq!( + Assets::free_balance(&base_asset, &ALICE).expect("Failed to query free balance."), + funds_for_team + balance!(900000) + ); + + let (xor_liq, ceres_liq) = pool_xyk::Reserves::::get(base_asset, CERES_ASSET_ID); + assert_eq!(xor_liq, funds_for_liquidity); + + let tokens_for_liquidity = (FixedWrapper::from(funds_for_liquidity) + / FixedWrapper::from(ilo_info.listing_price)) + .try_into_balance() + .unwrap_or(0); + assert_eq!(ceres_liq, tokens_for_liquidity); + + let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &pallet_account) + .expect("Failed to query free balance."), + ilo_info.sold_tokens + ); + + assert_err!( + pool_xyk::Pallet::::withdraw_liquidity( + RuntimeOrigin::signed(pallet_account), + DEX_A_ID, + base_asset, + CERES_ASSET_ID, + ilo_info.lp_tokens, + balance!(1), + balance!(1) + ), + pool_xyk::Error::::NotEnoughUnlockedLiquidity + ); + + let token_locker_data = ceres_token_locker::TokenLockerData::::get(ALICE); + assert_eq!(token_locker_data.len(), 4_usize); + let mut unlocking_timestamp = current_timestamp + ilo_info.team_vesting.team_vesting_period; + for token_lock_info in token_locker_data { + assert_eq!(token_lock_info.asset_id, CERES_ASSET_ID); + assert_eq!(token_lock_info.tokens, balance!(200)); + assert_eq!(token_lock_info.unlocking_timestamp, unlocking_timestamp); + unlocking_timestamp += ilo_info.team_vesting.team_vesting_period; + } + + assert_eq!(ilo_info.finish_timestamp, current_timestamp); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); +#[test] +fn finish_ilo_filled_hard_cap_base_asset_xstusd_ok() { + preset_initial(|| { + let mut current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XSTUSD; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(1500), + false, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(1000); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + current_timestamp = pallet_timestamp::Pallet::::get(); + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + + let funds_raised_fee = (FixedWrapper::from(ilo_info.funds_raised) + * FixedWrapper::from(FeePercentOnRaisedFunds::::get())) + .try_into_balance() + .unwrap_or(0); + let raised_funds_without_fee = ilo_info.funds_raised - funds_raised_fee; + let funds_for_liquidity = (FixedWrapper::from(raised_funds_without_fee) + * FixedWrapper::from(ilo_info.liquidity_percent)) + .try_into_balance() + .unwrap_or(0); + let funds_for_team = raised_funds_without_fee - funds_for_liquidity; + + assert_eq!( + Assets::free_balance(&base_asset, &pallet::AuthorityAccount::::get()) + .expect("Failed to query free balance."), + funds_raised_fee + ); + assert_eq!( + Assets::free_balance(&base_asset, &ALICE).expect("Failed to query free balance."), + funds_for_team + ); + + let (xor_liq, ceres_liq) = pool_xyk::Reserves::::get(base_asset, CERES_ASSET_ID); + assert_eq!(xor_liq, funds_for_liquidity); + + let tokens_for_liquidity = (FixedWrapper::from(funds_for_liquidity) + / FixedWrapper::from(ilo_info.listing_price)) + .try_into_balance() + .unwrap_or(0); + assert_eq!(ceres_liq, tokens_for_liquidity); + + let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &pallet_account) + .expect("Failed to query free balance."), + ilo_info.sold_tokens + ); + + assert_err!( + pool_xyk::Pallet::::withdraw_liquidity( + RuntimeOrigin::signed(pallet_account), + DEX_B_ID, + base_asset, + CERES_ASSET_ID, + ilo_info.lp_tokens, + balance!(1), + balance!(1) + ), + pool_xyk::Error::::NotEnoughUnlockedLiquidity + ); + + let token_locker_data = ceres_token_locker::TokenLockerData::::get(ALICE); + assert_eq!(token_locker_data.len(), 4_usize); + let mut unlocking_timestamp = current_timestamp + ilo_info.team_vesting.team_vesting_period; + for token_lock_info in token_locker_data { + assert_eq!(token_lock_info.asset_id, CERES_ASSET_ID); + assert_eq!(token_lock_info.tokens, balance!(200)); + assert_eq!(token_lock_info.unlocking_timestamp, unlocking_timestamp); + unlocking_timestamp += ilo_info.team_vesting.team_vesting_period; + } + + assert_eq!(ilo_info.finish_timestamp, current_timestamp); + }); +} - let funds_to_contribute = balance!(800); +#[test] +fn claim_ilo_does_not_exist() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::claim(RuntimeOrigin::signed(CHARLES), CERES_ASSET_ID,), + Error::::ILODoesNotExist + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - ),); +#[test] +fn claim_ilo_is_not_finished() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + assert_err!( + CeresLaunchpadPallet::::claim(RuntimeOrigin::signed(CHARLES), CERES_ASSET_ID,), + Error::::ILOIsNotFinished + ); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); +#[test] +fn claim_ilo_failed_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(0.21); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + assert_ok!(CeresLaunchpadPallet::::claim( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + )); + + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &CHARLES).expect("Failed to query free balance."), + balance!(5000) + ); + + let contribution_info = pallet::Contributions::::get(CERES_ASSET_ID, CHARLES); + assert!(contribution_info.claiming_finished); + }); +} - assert_ok!(CeresLaunchpadPallet::::finish_ilo( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); +#[test] +fn claim_funds_already_claimed() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(0.21); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + assert_ok!(CeresLaunchpadPallet::::claim( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + )); + + assert_err!( + CeresLaunchpadPallet::::claim(RuntimeOrigin::signed(CHARLES), CERES_ASSET_ID,), + Error::::FundsAlreadyClaimed + ); + }); +} - let mut ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); +#[test] +fn claim_first_release_claim_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(1500), + false, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(1000); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + assert_ok!(CeresLaunchpadPallet::::claim( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + )); + + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + let contribution_info = pallet::Contributions::::get(CERES_ASSET_ID, CHARLES); + + let tokens_to_claim = (FixedWrapper::from(contribution_info.tokens_bought) + * FixedWrapper::from(ilo_info.contributors_vesting.first_release_percent)) + .try_into_balance() + .unwrap_or(0); + + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &CHARLES).expect("Failed to query free balance."), + balance!(5000) + tokens_to_claim + ); + + assert_eq!(contribution_info.tokens_claimed, tokens_to_claim); + }); +} - let unlocking_timestamp = ilo_info - .finish_timestamp - .saturating_add(86_400_000u64.saturating_mul(ilo_info.lockup_days.into())); +#[test] +fn claim_no_potential_claims() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(1500), + false, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 50, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(1000); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + assert_ok!(CeresLaunchpadPallet::::claim( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 12); + + assert_err!( + CeresLaunchpadPallet::::claim(RuntimeOrigin::signed(CHARLES), CERES_ASSET_ID,), + Error::::NothingToClaim + ); + }); +} - pallet_timestamp::Pallet::::set_timestamp(unlocking_timestamp + 1); +#[test] +fn claim_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(1500), + false, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.1), + 30u32.into(), + balance!(0.18) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(1000); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + let mut contribution_info = pallet::Contributions::::get(CERES_ASSET_ID, CHARLES); + + let first_release = (FixedWrapper::from(contribution_info.tokens_bought) + * FixedWrapper::from(ilo_info.contributors_vesting.first_release_percent)) + .try_into_balance() + .unwrap_or(0); + + let tokens_per_claim = (FixedWrapper::from(contribution_info.tokens_bought) + * FixedWrapper::from(ilo_info.contributors_vesting.vesting_percent)) + .try_into_balance() + .unwrap_or(0); + + assert_ok!(CeresLaunchpadPallet::::claim( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 43); + + assert_ok!(CeresLaunchpadPallet::::claim( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + )); + + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &CHARLES).expect("Failed to query free balance."), + balance!(5000) + first_release + tokens_per_claim + ); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 103); + + assert_ok!(CeresLaunchpadPallet::::claim( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + )); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &CHARLES).expect("Failed to query free balance."), + balance!(5000) + first_release + tokens_per_claim * 3 + ); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 163); + + assert_ok!(CeresLaunchpadPallet::::claim( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + )); + contribution_info = pallet::Contributions::::get(CERES_ASSET_ID, CHARLES); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &CHARLES).expect("Failed to query free balance."), + balance!(5000) + first_release + tokens_per_claim * 5 + ); + assert!(contribution_info.claiming_finished); + }); +} - assert_ok!(CeresLaunchpadPallet::::claim_lp_tokens( +#[test] +fn change_fee_percent_for_raised_funds_unauthorized() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::change_fee_percent_for_raised_funds( RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID - )); + balance!(0.02) + ), + Error::::Unauthorized + ); + }); +} - ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); +#[test] +fn change_fee_percent_for_raised_funds_invalid_fee_percent() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::change_fee_percent_for_raised_funds( + RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), + balance!(1.2) + ), + Error::::InvalidFeePercent + ); + }); +} - let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); - let pool_account = pool_xyk::Pallet::::properties_of_pool( - base_asset.into(), - CERES_ASSET_ID.into(), +#[test] +fn change_fee_percent_for_raised_funds_ok() { + preset_initial(|| { + assert_ok!( + CeresLaunchpadPallet::::change_fee_percent_for_raised_funds( + RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), + balance!(0.02) ) - .expect("Pool doesn't exist") - .0; - let lp_tokens = - pool_xyk::Pallet::::balance_of_pool_provider(pool_account, pallet_account) - .unwrap_or(0); - - assert_eq!(lp_tokens, balance!(0)); + ); - let lp_tokens_alice = - pool_xyk::Pallet::::balance_of_pool_provider(pool_account, ALICE) - .unwrap_or(0); + assert_eq!( + pallet::FeePercentOnRaisedFunds::::get(), + balance!(0.02) + ); + }); +} - assert_eq!(lp_tokens_alice, ilo_info.lp_tokens); - assert_eq!(ilo_info.claimed_lp_tokens, true); - }); - } - - #[test] - fn claim_lp_tokens_base_asset_xstusd_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XSTUSD; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn change_ceres_burn_fee_unauthorized() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::change_ceres_burn_fee( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(850), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + balance!(100) + ), + Error::::Unauthorized + ); + }); +} - let funds_to_contribute = balance!(800); +#[test] +fn change_ceres_burn_fee_ok() { + preset_initial(|| { + assert_ok!(CeresLaunchpadPallet::::change_ceres_burn_fee( + RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), + balance!(100) + )); - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - ),); + assert_eq!(pallet::CeresBurnFeeAmount::::get(), balance!(100)); + }); +} - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); +#[test] +fn claim_lp_tokens_ilo_does_not_exist() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::claim_lp_tokens( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ), + Error::::ILODoesNotExist + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::finish_ilo( +#[test] +fn claim_lp_tokens_cant_claim_lp_tokens() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(0.25), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + assert_err!( + CeresLaunchpadPallet::::claim_lp_tokens( RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); + CERES_ASSET_ID + ), + Error::::CantClaimLPTokens + ); + }); +} - let mut ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); +#[test] +fn claim_lp_tokens_unauthorized() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(850), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + balance!(800) + ),); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + let unlocking_timestamp = ilo_info + .finish_timestamp + .saturating_add(86_400_000u64.saturating_mul(ilo_info.lockup_days.into())); + + pallet_timestamp::Pallet::::set_timestamp(unlocking_timestamp + 1); + + assert_err!( + CeresLaunchpadPallet::::claim_lp_tokens( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID + ), + Error::::Unauthorized + ); + }); +} - let unlocking_timestamp = ilo_info - .finish_timestamp - .saturating_add(86_400_000u64.saturating_mul(ilo_info.lockup_days.into())); +#[test] +fn claim_lp_tokens_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(850), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(800); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + ),); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + let mut ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + + let unlocking_timestamp = ilo_info + .finish_timestamp + .saturating_add(86_400_000u64.saturating_mul(ilo_info.lockup_days.into())); + + pallet_timestamp::Pallet::::set_timestamp(unlocking_timestamp + 1); + + assert_ok!(CeresLaunchpadPallet::::claim_lp_tokens( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + )); + + ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + + let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); + let pool_account = + pool_xyk::Pallet::::properties_of_pool(base_asset, CERES_ASSET_ID) + .expect("Pool doesn't exist") + .0; + let lp_tokens = + pool_xyk::Pallet::::balance_of_pool_provider(pool_account, pallet_account) + .unwrap_or(0); + + assert_eq!(lp_tokens, balance!(0)); + + let lp_tokens_alice = + pool_xyk::Pallet::::balance_of_pool_provider(pool_account, ALICE).unwrap_or(0); + + assert_eq!(lp_tokens_alice, ilo_info.lp_tokens); + assert!(ilo_info.claimed_lp_tokens); + }); +} - pallet_timestamp::Pallet::::set_timestamp(unlocking_timestamp + 1); +#[test] +fn claim_lp_tokens_base_asset_xstusd_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XSTUSD; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(850), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(800); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + ),); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + let mut ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + + let unlocking_timestamp = ilo_info + .finish_timestamp + .saturating_add(86_400_000u64.saturating_mul(ilo_info.lockup_days.into())); + + pallet_timestamp::Pallet::::set_timestamp(unlocking_timestamp + 1); + + assert_ok!(CeresLaunchpadPallet::::claim_lp_tokens( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + )); + + ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + + let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); + let pool_account = + pool_xyk::Pallet::::properties_of_pool(base_asset, CERES_ASSET_ID) + .expect("Pool doesn't exist") + .0; + let lp_tokens = + pool_xyk::Pallet::::balance_of_pool_provider(pool_account, pallet_account) + .unwrap_or(0); + + assert_eq!(lp_tokens, balance!(0)); + + let lp_tokens_alice = + pool_xyk::Pallet::::balance_of_pool_provider(pool_account, ALICE).unwrap_or(0); + + assert_eq!(lp_tokens_alice, ilo_info.lp_tokens); + assert!(ilo_info.claimed_lp_tokens); + }); +} - assert_ok!(CeresLaunchpadPallet::::claim_lp_tokens( +#[test] +fn claim_lp_tokens_cant_claim_lp_tokens_already_claimed() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(850), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(800); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + ),); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + ),); + + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + + let unlocking_timestamp = ilo_info + .finish_timestamp + .saturating_add(86_400_000u64.saturating_mul(ilo_info.lockup_days.into())); + + pallet_timestamp::Pallet::::set_timestamp(unlocking_timestamp + 1); + + assert_ok!(CeresLaunchpadPallet::::claim_lp_tokens( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + )); + + assert_err!( + CeresLaunchpadPallet::::claim_lp_tokens( RuntimeOrigin::signed(ALICE), CERES_ASSET_ID - )); + ), + Error::::CantClaimLPTokens + ); + }); +} - ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); +#[test] +fn claim_pswap_rewards_unauthorized() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::claim_pswap_rewards(RuntimeOrigin::signed(ALICE)), + Error::::Unauthorized + ); + }); +} - let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); - let pool_account = pool_xyk::Pallet::::properties_of_pool( - base_asset.into(), - CERES_ASSET_ID.into(), - ) - .expect("Pool doesn't exist") - .0; - let lp_tokens = - pool_xyk::Pallet::::balance_of_pool_provider(pool_account, pallet_account) - .unwrap_or(0); +#[test] +fn claim_pswap_rewards_ok() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(1500), + false, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(1000); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + + assert_ok!(CeresLaunchpadPallet::::finish_ilo( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID + )); + + run_to_block(20000); + + let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); + let share = FixedWrapper::from(1.00).get().unwrap(); + ShareholderAccounts::::mutate(pallet_account, |current| { + *current = current.saturating_add(share) + }); + ClaimableShares::::mutate(|current| *current = current.saturating_add(share)); + + assert_ok!(CeresLaunchpadPallet::::claim_pswap_rewards( + RuntimeOrigin::signed(pallet::AuthorityAccount::::get()) + )); + + assert_eq!( + Assets::free_balance(&PSWAP, &pallet_account).expect("Failed to query free balance."), + balance!(0) + ); + + assert_eq!( + Assets::free_balance(&PSWAP, &pallet::AuthorityAccount::::get()) + .expect("Failed to query free balance."), + balance!(share) + ); + }); +} - assert_eq!(lp_tokens, balance!(0)); +#[test] +fn on_initialize_fail_ilo() { + preset_initial(|| { + let current_timestamp = pallet_timestamp::Pallet::::get(); + let base_asset = XOR; + assert_ok!(CeresLaunchpadPallet::::create_ilo( + RuntimeOrigin::signed(ALICE), + base_asset, + CERES_ASSET_ID, + balance!(7693), + balance!(3000), + balance!(0.13), + balance!(600), + balance!(1000), + balance!(0.2), + balance!(1500), + true, + balance!(0.75), + balance!(0.25), + 31, + current_timestamp + 5, + current_timestamp + 10, + balance!(1000), + balance!(0.2), + current_timestamp + 3, + balance!(0.2), + balance!(0.2), + current_timestamp + 3, + balance!(0.2) + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + + let funds_to_contribute = balance!(1000); + + assert_ok!(CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(CHARLES), + CERES_ASSET_ID, + funds_to_contribute + )); + + pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 15 * 86_400_000u64); + run_to_block(300000); + + let ilo_info = pallet::ILOs::::get(CERES_ASSET_ID).unwrap(); + let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &pallet_account) + .expect("Failed to query free balance."), + balance!(0) + ); - let lp_tokens_alice = - pool_xyk::Pallet::::balance_of_pool_provider(pool_account, ALICE) - .unwrap_or(0); + assert_eq!( + Assets::free_balance(&CERES_ASSET_ID, &ALICE).expect("Failed to query free balance."), + balance!(15990) + ); - assert_eq!(lp_tokens_alice, ilo_info.lp_tokens); - assert_eq!(ilo_info.claimed_lp_tokens, true); - }); - } - - #[test] - fn claim_lp_tokens_cant_claim_lp_tokens_already_claimed() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(850), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + assert!(ilo_info.failed); + }); +} - let funds_to_contribute = balance!(800); +#[test] +fn change_ceres_contribution_fee_unauthorized() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::change_ceres_contribution_fee( + RuntimeOrigin::signed(ALICE), + balance!(100) + ), + Error::::Unauthorized + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - ),); +#[test] +fn change_ceres_contribution_fee_ok() { + preset_initial(|| { + assert_ok!( + CeresLaunchpadPallet::::change_ceres_contribution_fee( + RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), + balance!(100) + ) + ); - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + assert_eq!( + pallet::CeresForContributionInILO::::get(), + balance!(100) + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::finish_ilo( +#[test] +fn add_whitelisted_contributor_unauthorized() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::add_whitelisted_contributor( RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - ),); - - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); + EMILY + ), + Error::::Unauthorized + ); + }); +} - let unlocking_timestamp = ilo_info - .finish_timestamp - .saturating_add(86_400_000u64.saturating_mul(ilo_info.lockup_days.into())); +#[test] +fn add_whitelisted_contributor_ok() { + preset_initial(|| { + assert_ok!( + CeresLaunchpadPallet::::add_whitelisted_contributor( + RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), + EMILY + ) + ); - pallet_timestamp::Pallet::::set_timestamp(unlocking_timestamp + 1); + assert!(pallet::WhitelistedContributors::::get().contains(&EMILY)); + }); +} - assert_ok!(CeresLaunchpadPallet::::claim_lp_tokens( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID - )); - - assert_err!( - CeresLaunchpadPallet::::claim_lp_tokens( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID - ), - Error::::CantClaimLPTokens - ); - }); - } - - #[test] - fn claim_pswap_rewards_unauthorized() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::claim_pswap_rewards(RuntimeOrigin::signed(ALICE)), - Error::::Unauthorized - ); - }); - } - - #[test] - fn claim_pswap_rewards_ok() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( +#[test] +fn remove_whitelisted_contributor_unauthorized() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::remove_whitelisted_contributor( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(1500), - false, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); + EMILY + ), + Error::::Unauthorized + ); + }); +} - let funds_to_contribute = balance!(1000); +#[test] +fn remove_whitelisted_contributor_ok() { + preset_initial(|| { + assert_ok!( + CeresLaunchpadPallet::::remove_whitelisted_contributor( + RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), + ALICE + ) + ); - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); + assert!(pallet::WhitelistedContributors::::get().contains(&BOB)); - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 11); + assert_err!( + CeresLaunchpadPallet::::contribute( + RuntimeOrigin::signed(ALICE), + CERES_ASSET_ID, + balance!(0.21) + ), + Error::::AccountIsNotWhitelisted + ); + }); +} - assert_ok!(CeresLaunchpadPallet::::finish_ilo( +#[test] +fn add_whitelisted_ilo_organizer_unauthorized() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::add_whitelisted_ilo_organizer( RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into() - )); + DAN + ), + Error::::Unauthorized + ); + }); +} - run_to_block(20000); +#[test] +fn add_whitelisted_ilo_organizer_ok() { + preset_initial(|| { + assert_ok!( + CeresLaunchpadPallet::::add_whitelisted_ilo_organizer( + RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), + DAN + ) + ); - let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); - let share = FixedWrapper::from(1.00).get().unwrap(); - ShareholderAccounts::::mutate(&pallet_account, |current| { - *current = current.saturating_add(share) - }); - ClaimableShares::::mutate(|current| *current = current.saturating_add(share)); + assert!(pallet::WhitelistedIloOrganizers::::get().contains(&DAN)); + }); +} - assert_ok!(CeresLaunchpadPallet::::claim_pswap_rewards( - RuntimeOrigin::signed(pallet::AuthorityAccount::::get()) - )); +#[test] +fn remove_whitelisted_ilo_organizer_unauthorized() { + preset_initial(|| { + assert_err!( + CeresLaunchpadPallet::::remove_whitelisted_ilo_organizer( + RuntimeOrigin::signed(ALICE), + EMILY + ), + Error::::Unauthorized + ); + }); +} - assert_eq!( - Assets::free_balance(&PSWAP, &pallet_account) - .expect("Failed to query free balance."), - balance!(0) - ); +#[test] +fn remove_whitelisted_ilo_organizer_ok() { + preset_initial(|| { + let base_asset = XOR; + assert_ok!( + CeresLaunchpadPallet::::remove_whitelisted_ilo_organizer( + RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), + ALICE + ) + ); - assert_eq!( - Assets::free_balance(&PSWAP, &pallet::AuthorityAccount::::get()) - .expect("Failed to query free balance."), - balance!(share) - ); - }); - } - - #[test] - fn on_initialize_fail_ilo() { - preset_initial(|| { - let current_timestamp = pallet_timestamp::Pallet::::get(); - let base_asset = XOR; - assert_ok!(CeresLaunchpadPallet::::create_ilo( + assert!(pallet::WhitelistedIloOrganizers::::get().contains(&BOB)); + + let current_timestamp = pallet_timestamp::Pallet::::get(); + assert_err!( + CeresLaunchpadPallet::::create_ilo( RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), + base_asset, + CERES_ASSET_ID, balance!(7693), balance!(3000), balance!(0.13), balance!(600), balance!(1000), balance!(0.2), - balance!(1500), + balance!(0.25), true, balance!(0.75), balance!(0.25), @@ -3549,228 +3700,8 @@ mod tests { balance!(0.2), current_timestamp + 3, balance!(0.2) - )); - - pallet_timestamp::Pallet::::set_timestamp(current_timestamp + 6); - - let funds_to_contribute = balance!(1000); - - assert_ok!(CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(CHARLES), - CERES_ASSET_ID.into(), - funds_to_contribute - )); - - pallet_timestamp::Pallet::::set_timestamp( - current_timestamp + 15 * 86_400_000u64, - ); - run_to_block(300000); - - let ilo_info = pallet::ILOs::::get(&CERES_ASSET_ID).unwrap(); - let pallet_account = PalletId(*b"crslaunc").into_account_truncating(); - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &pallet_account) - .expect("Failed to query free balance."), - balance!(0) - ); - - assert_eq!( - Assets::free_balance(&CERES_ASSET_ID, &ALICE) - .expect("Failed to query free balance."), - balance!(15990) - ); - - assert_eq!(ilo_info.failed, true); - }); - } - - #[test] - fn change_ceres_contribution_fee_unauthorized() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::change_ceres_contribution_fee( - RuntimeOrigin::signed(ALICE), - balance!(100) - ), - Error::::Unauthorized - ); - }); - } - - #[test] - fn change_ceres_contribution_fee_ok() { - preset_initial(|| { - assert_ok!( - CeresLaunchpadPallet::::change_ceres_contribution_fee( - RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), - balance!(100) - ) - ); - - assert_eq!( - pallet::CeresForContributionInILO::::get(), - balance!(100) - ); - }); - } - - #[test] - fn add_whitelisted_contributor_unauthorized() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::add_whitelisted_contributor( - RuntimeOrigin::signed(ALICE), - EMILY - ), - Error::::Unauthorized - ); - }); - } - - #[test] - fn add_whitelisted_contributor_ok() { - preset_initial(|| { - assert_ok!( - CeresLaunchpadPallet::::add_whitelisted_contributor( - RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), - EMILY - ) - ); - - assert_eq!( - pallet::WhitelistedContributors::::get().contains(&EMILY), - true - ); - }); - } - - #[test] - fn remove_whitelisted_contributor_unauthorized() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::remove_whitelisted_contributor( - RuntimeOrigin::signed(ALICE), - EMILY - ), - Error::::Unauthorized - ); - }); - } - - #[test] - fn remove_whitelisted_contributor_ok() { - preset_initial(|| { - assert_ok!( - CeresLaunchpadPallet::::remove_whitelisted_contributor( - RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), - ALICE - ) - ); - - assert_eq!( - pallet::WhitelistedContributors::::get().contains(&BOB), - true - ); - - assert_err!( - CeresLaunchpadPallet::::contribute( - RuntimeOrigin::signed(ALICE), - CERES_ASSET_ID.into(), - balance!(0.21) - ), - Error::::AccountIsNotWhitelisted - ); - }); - } - - #[test] - fn add_whitelisted_ilo_organizer_unauthorized() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::add_whitelisted_ilo_organizer( - RuntimeOrigin::signed(ALICE), - DAN - ), - Error::::Unauthorized - ); - }); - } - - #[test] - fn add_whitelisted_ilo_organizer_ok() { - preset_initial(|| { - assert_ok!( - CeresLaunchpadPallet::::add_whitelisted_ilo_organizer( - RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), - DAN - ) - ); - - assert_eq!( - pallet::WhitelistedIloOrganizers::::get().contains(&DAN), - true - ); - }); - } - - #[test] - fn remove_whitelisted_ilo_organizer_unauthorized() { - preset_initial(|| { - assert_err!( - CeresLaunchpadPallet::::remove_whitelisted_ilo_organizer( - RuntimeOrigin::signed(ALICE), - EMILY - ), - Error::::Unauthorized - ); - }); - } - - #[test] - fn remove_whitelisted_ilo_organizer_ok() { - preset_initial(|| { - let base_asset = XOR; - assert_ok!( - CeresLaunchpadPallet::::remove_whitelisted_ilo_organizer( - RuntimeOrigin::signed(pallet::AuthorityAccount::::get()), - ALICE - ) - ); - - assert_eq!( - pallet::WhitelistedIloOrganizers::::get().contains(&BOB), - true - ); - - let current_timestamp = pallet_timestamp::Pallet::::get(); - assert_err!( - CeresLaunchpadPallet::::create_ilo( - RuntimeOrigin::signed(ALICE), - base_asset.into(), - CERES_ASSET_ID.into(), - balance!(7693), - balance!(3000), - balance!(0.13), - balance!(600), - balance!(1000), - balance!(0.2), - balance!(0.25), - true, - balance!(0.75), - balance!(0.25), - 31, - current_timestamp + 5, - current_timestamp + 10, - balance!(1000), - balance!(0.2), - current_timestamp + 3, - balance!(0.2), - balance!(0.2), - current_timestamp + 3, - balance!(0.2) - ), - Error::::AccountIsNotWhitelisted - ); - }); - } + ), + Error::::AccountIsNotWhitelisted + ); + }); } diff --git a/pallets/liquidity-proxy/benchmarking/src/mock.rs b/pallets/liquidity-proxy/benchmarking/src/mock.rs index a346bc6fdb..ce9b6e4a3e 100644 --- a/pallets/liquidity-proxy/benchmarking/src/mock.rs +++ b/pallets/liquidity-proxy/benchmarking/src/mock.rs @@ -49,7 +49,7 @@ use multicollateral_bonding_curve_pool::{ DistributionAccount, DistributionAccountData, DistributionAccounts, }; use permissions::{Scope, BURN, MANAGE_DEX, MINT}; -use sp_core::H256; +use sp_core::{ConstU32, H256}; use sp_runtime::testing::Header; use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; use sp_runtime::{AccountId32, DispatchError, DispatchResult, Percent}; @@ -170,6 +170,7 @@ impl liquidity_proxy::Config for Runtime { type VestedRewardsPallet = vested_rewards::Pallet; type GetADARAccountId = GetADARAccountId; type ADARCommissionRatioUpdateOrigin = EnsureRoot; + type MaxAdditionalDataLength = ConstU32<128>; } impl tokens::Config for Runtime { diff --git a/pallets/liquidity-proxy/src/lib.rs b/pallets/liquidity-proxy/src/lib.rs index 8b6e57d3d4..ad6d4786e9 100644 --- a/pallets/liquidity-proxy/src/lib.rs +++ b/pallets/liquidity-proxy/src/lib.rs @@ -2219,6 +2219,7 @@ pub mod pallet { type VestedRewardsPallet: VestedRewardsPallet; type GetADARAccountId: Get; type ADARCommissionRatioUpdateOrigin: EnsureOrigin; + type MaxAdditionalDataLength: Get; /// Weight information for the extrinsics in this Pallet. type WeightInfo: WeightInfo; } @@ -2441,6 +2442,72 @@ pub mod pallet { ADARCommissionRatio::::put(commission_ratio); Ok(().into()) } + + /// Extrinsic which is enable XORless transfers. + /// Internally it's swaps `asset_id` to `desired_xor_amount` of `XOR` and transfers remaining amount of `asset_id` to `receiver`. + /// Client apps should specify the XOR amount which should be paid as a fee in `desired_xor_amount` parameter. + /// If sender will not have enough XOR to pay fees after execution, transaction will be rejected. + /// This extrinsic is done as temporary solution for XORless transfers, in future it would be removed + /// and logic for XORless extrinsics should be moved to xor-fee pallet. + #[pallet::call_index(6)] + #[pallet::weight({ + let mut weight = ::WeightInfo::transfer(); + if asset_id != &common::XOR.into() + && max_amount_in > &Balance::zero() + && desired_xor_amount > &Balance::zero() + { + weight = weight.saturating_add(Pallet::::swap_weight(dex_id, asset_id, &common::XOR.into(), SwapVariant::WithDesiredOutput)); + } + weight + })] + pub fn xorless_transfer( + origin: OriginFor, + dex_id: T::DEXId, + asset_id: T::AssetId, + receiver: T::AccountId, + amount: Balance, + desired_xor_amount: Balance, + max_amount_in: Balance, + selected_source_types: Vec, + filter_mode: FilterMode, + additional_data: Option>, + ) -> DispatchResultWithPostInfo { + let sender = ensure_signed(origin)?; + ensure!(sender != receiver, Error::::TheSameSenderAndReceiver); + + let mut weight = Weight::default(); + if asset_id != common::XOR.into() + && max_amount_in > Balance::zero() + && desired_xor_amount > Balance::zero() + { + weight = weight.saturating_add(Self::inner_swap( + sender.clone(), + sender.clone(), + dex_id, + asset_id, + common::XOR.into(), + SwapAmount::with_desired_output(desired_xor_amount, max_amount_in), + selected_source_types, + filter_mode, + )?); + } + + assets::Pallet::::transfer_from(&asset_id, &sender, &receiver, amount)?; + weight = weight.saturating_add(::WeightInfo::transfer()); + + Self::deposit_event(Event::::XorlessTransfer( + asset_id, + sender, + receiver, + amount, + additional_data, + )); + + Ok(PostDispatchInfo { + actual_weight: Some(weight), + pays_fee: Pays::Yes, + }) + } } #[pallet::event] @@ -2465,6 +2532,15 @@ pub mod pallet { /// Batch of swap transfers has been performed /// [ADAR Fee, Input amount] BatchSwapExecuted(Balance, Balance), + /// XORless transfer has been performed + /// [Asset Id, Caller Account, Receiver Account, Amount, Additional Data] + XorlessTransfer( + AssetIdOf, + AccountIdOf, + AccountIdOf, + Balance, + Option>, + ), } #[pallet::error] @@ -2505,6 +2581,8 @@ pub mod pallet { InvalidADARCommissionRatio, // Sender don't have enough asset balance InsufficientBalance, + // Sender and receiver should not be the same + TheSameSenderAndReceiver, } #[pallet::type_value] diff --git a/pallets/liquidity-proxy/src/mock.rs b/pallets/liquidity-proxy/src/mock.rs index f09f6211e7..fdfe5199cc 100644 --- a/pallets/liquidity-proxy/src/mock.rs +++ b/pallets/liquidity-proxy/src/mock.rs @@ -48,7 +48,7 @@ use common::prelude::{Balance, FixedWrapper, QuoteAmount, SwapAmount, SwapOutcom use frame_system::{pallet_prelude::BlockNumberFor, EnsureRoot}; use hex_literal::hex; use permissions::{Scope, INIT_DEX, MANAGE_DEX}; -use sp_core::H256; +use sp_core::{ConstU32, H256}; use sp_runtime::testing::Header; use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; use sp_runtime::{AccountId32, DispatchError, Perbill, Percent}; @@ -201,6 +201,7 @@ impl Config for Runtime { type VestedRewardsPallet = vested_rewards::Pallet; type GetADARAccountId = GetADARAccountId; type ADARCommissionRatioUpdateOrigin = EnsureRoot; + type MaxAdditionalDataLength = ConstU32<128>; } impl tokens::Config for Runtime { diff --git a/pallets/liquidity-proxy/src/tests.rs b/pallets/liquidity-proxy/src/tests.rs index 604e787a07..7cecd02e88 100644 --- a/pallets/liquidity-proxy/src/tests.rs +++ b/pallets/liquidity-proxy/src/tests.rs @@ -3763,3 +3763,161 @@ fn test_batch_swap_asset_reuse_fails() { ); }); } + +#[test] +fn test_xorless_transfer_works() { + let mut ext = ExtBuilder::default().with_xyk_pool().build(); + ext.execute_with(|| { + assert_eq!(Assets::free_balance(&USDT, &bob()).unwrap(), balance!(0)); + assert_eq!( + Assets::free_balance(&USDT, &alice()).unwrap(), + balance!(12000) + ); + assert_eq!( + Assets::free_balance(&XOR, &alice()).unwrap(), + balance!(356400) + ); + + let filter_mode = FilterMode::AllowSelected; + let sources = [LiquiditySourceType::XYKPool].to_vec(); + + assert_ok!(LiquidityProxy::xorless_transfer( + RuntimeOrigin::signed(alice()), + 0, + USDT, + bob(), + balance!(1), + balance!(1), + balance!(10), + sources, + filter_mode, + Default::default(), + )); + + assert_approx_eq!( + Assets::free_balance(&USDT, &alice()).unwrap(), + // 12000 USDT - 1 USDT for swap - 1 USDT for transfer + balance!(11998), + balance!(0.01) + ); + assert_approx_eq!( + Assets::free_balance(&XOR, &alice()).unwrap(), + balance!(356401), + balance!(0.01) + ); + assert_approx_eq!( + Assets::free_balance(&USDT, &bob()).unwrap(), + balance!(1), + balance!(0.01) + ); + }); +} + +#[test] +fn test_xorless_transfer_without_swap_works() { + let mut ext = ExtBuilder::default().with_xyk_pool().build(); + ext.execute_with(|| { + assert_eq!(Assets::free_balance(&USDT, &bob()).unwrap(), balance!(0)); + assert_eq!( + Assets::free_balance(&USDT, &alice()).unwrap(), + balance!(12000) + ); + assert_eq!( + Assets::free_balance(&XOR, &alice()).unwrap(), + balance!(356400) + ); + + let filter_mode = FilterMode::AllowSelected; + let sources = [LiquiditySourceType::XYKPool].to_vec(); + + assert_ok!(LiquidityProxy::xorless_transfer( + RuntimeOrigin::signed(alice()), + 0, + USDT, + bob(), + balance!(1), + balance!(0), + balance!(0), + sources, + filter_mode, + Default::default(), + )); + + assert_approx_eq!( + Assets::free_balance(&USDT, &alice()).unwrap(), + // 12000 USDT - 1 USDT for swap - 1 USDT for transfer + balance!(11999), + balance!(0.01) + ); + assert_approx_eq!( + Assets::free_balance(&XOR, &alice()).unwrap(), + balance!(356400), + balance!(0.01) + ); + assert_approx_eq!( + Assets::free_balance(&USDT, &bob()).unwrap(), + balance!(1), + balance!(0.01) + ); + }); +} + +#[test] +fn test_xorless_transfer_fails_on_swap() { + let mut ext = ExtBuilder::default().with_xyk_pool().build(); + ext.execute_with(|| { + assert_eq!( + Assets::free_balance(&USDT, &alice()).unwrap(), + balance!(12000) + ); + + let filter_mode = FilterMode::AllowSelected; + let sources = [LiquiditySourceType::XYKPool].to_vec(); + + assert_noop!( + LiquidityProxy::xorless_transfer( + RuntimeOrigin::signed(alice()), + 0, + USDT, + bob(), + balance!(1), + balance!(1), + balance!(0.5), + sources, + filter_mode, + Default::default(), + ), + Error::::SlippageNotTolerated + ); + }); +} + +#[test] +fn test_xorless_transfer_fails_on_transfer() { + let mut ext = ExtBuilder::default().with_xyk_pool().build(); + ext.execute_with(|| { + assert_eq!( + Assets::free_balance(&USDT, &alice()).unwrap(), + balance!(12000) + ); + + let filter_mode = FilterMode::AllowSelected; + let sources = [LiquiditySourceType::XYKPool].to_vec(); + + assert_noop!( + LiquidityProxy::xorless_transfer( + RuntimeOrigin::signed(alice()), + 0, + USDT, + bob(), + balance!(12000), + balance!(1), + balance!(2), + sources, + filter_mode, + Default::default(), + ), + tokens::Error::::BalanceTooLow + ); + }); +} diff --git a/pallets/order-book/src/benchmarking.rs b/pallets/order-book/src/benchmarking.rs index 7a30c5795b..5fad1a7e82 100644 --- a/pallets/order-book/src/benchmarking.rs +++ b/pallets/order-book/src/benchmarking.rs @@ -325,7 +325,8 @@ benchmarks! { quote: XOR.into(), }; - create_and_fill_order_book::(order_book_id); + OrderBookPallet::::create_orderbook(RawOrigin::Signed(bob::()).into(), order_book_id).unwrap(); + OrderBookPallet::::change_orderbook_status(RawOrigin::Root.into(), order_book_id, OrderBookStatus::Stop).unwrap(); }: { OrderBookPallet::::delete_orderbook( RawOrigin::Root.into(), @@ -336,7 +337,6 @@ benchmarks! { assert_last_event::( Event::::OrderBookDeleted { order_book_id, - count_of_canceled_orders: 12, } .into(), ); diff --git a/pallets/order-book/src/fee_calculator.rs b/pallets/order-book/src/fee_calculator.rs new file mode 100644 index 0000000000..09339a4e28 --- /dev/null +++ b/pallets/order-book/src/fee_calculator.rs @@ -0,0 +1,83 @@ +// This file is part of the SORA network and Polkaswap app. + +// Copyright (c) 2020, 2021, Polka Biome Ltd. All rights reserved. +// SPDX-License-Identifier: BSD-4-Clause + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// All advertising materials mentioning features or use of this software must display +// the following acknowledgement: This product includes software developed by Polka Biome +// Ltd., SORA, and Polkaswap. +// +// Neither the name of the Polka Biome Ltd. nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY Polka Biome Ltd. AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Polka Biome Ltd. BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::Config; +use common::prelude::FixedWrapper; +use common::Balance; +use sp_std::marker::PhantomData; + +use common::prelude::constants::SMALL_FEE as BASE_FEE; + +/// Calculator for order book custom fees +pub struct FeeCalculator(PhantomData); + +impl FeeCalculator { + /// Calculates the fee for `place_limit_order` extrinsic. + /// + /// The idea is to provide the reduced fee for market maker. + /// If extrinsic is successfull, user pays maximum half network fee. + /// If extrinsic failed, user pays full network fee. + /// + /// The actual fee contains two parts: constant and dynamic. + /// Dynamic part depends on limit order lifetime. The longer the lifetime, the higher the dynamic fee. + /// + /// const part = (4 / 7) * (base fee / 2) + /// dynamic part = (3 / 7) * (base fee / 2) * (lifetime / max_lifetime) + pub fn place_limit_order_fee( + lifetime: Option, + has_weight: bool, + is_err: bool, + ) -> Option { + // if place_limit_order() doesn't return a weight, it means that the limit order was converted into market order and the exchange fee should be taken + // if some error occurred, it means we don't prvide the reduced market maker fee for this call + if !has_weight || is_err { + return Some(BASE_FEE); + } + + let market_maker_max_fee = BASE_FEE.checked_div(2)?; + + // Maximum lifetime is used if `None` was supplied + let Some(lifetime) = lifetime else { + return Some(market_maker_max_fee); + }; + + let max_lifetime: u64 = T::MAX_ORDER_LIFESPAN.try_into().ok()?; + + let life_ratio = FixedWrapper::from(lifetime) / FixedWrapper::from(max_lifetime); + + let const_part = (FixedWrapper::from(market_maker_max_fee) / FixedWrapper::from(7)) + * FixedWrapper::from(4); + let dynamic_part = (FixedWrapper::from(market_maker_max_fee) / FixedWrapper::from(7)) + * FixedWrapper::from(3) + * life_ratio; + + (const_part + dynamic_part).try_into_balance().ok() + } +} diff --git a/pallets/order-book/src/lib.rs b/pallets/order-book/src/lib.rs index 3907fb03c5..800b0d4568 100644 --- a/pallets/order-book/src/lib.rs +++ b/pallets/order-book/src/lib.rs @@ -66,6 +66,7 @@ mod tests; mod benchmarking; pub mod cache_data_layer; +pub mod fee_calculator; mod limit_order; mod market_order; mod order_book; @@ -284,7 +285,6 @@ pub mod pallet { /// Order book is deleted OrderBookDeleted { order_book_id: OrderBookId, T::DEXId>, - count_of_canceled_orders: u32, }, /// Order book status is changed @@ -458,6 +458,10 @@ pub mod pallet { SlippageLimitExceeded, /// Market orders are allowed only for indivisible assets MarketOrdersAllowedOnlyForIndivisibleAssets, + /// It is possible to delete an order-book only with the statuses: OnlyCancel or Stop + ForbiddenStatusToDeleteOrderBook, + // It is possible to delete only empty order-book + OrderBookIsNotEmpty, } #[pallet::hooks] @@ -508,10 +512,16 @@ pub mod pallet { let order_book = >::get(order_book_id).ok_or(Error::::UnknownOrderBook)?; - let mut data = CacheDataLayer::::new(); - let count_of_canceled_orders = order_book.cancel_all_limit_orders(&mut data)? as u32; + ensure!( + order_book.status == OrderBookStatus::OnlyCancel + || order_book.status == OrderBookStatus::Stop, + Error::::ForbiddenStatusToDeleteOrderBook + ); - data.commit(); + let is_empty = >::iter_prefix_values(order_book_id) + .next() + .is_none(); + ensure!(is_empty, Error::::OrderBookIsNotEmpty); #[cfg(feature = "wip")] // order-book { @@ -526,10 +536,7 @@ pub mod pallet { Self::deregister_tech_account(order_book_id)?; >::remove(order_book_id); - Self::deposit_event(Event::::OrderBookDeleted { - order_book_id, - count_of_canceled_orders, - }); + Self::deposit_event(Event::::OrderBookDeleted { order_book_id }); Ok(().into()) } @@ -1012,12 +1019,6 @@ impl Pallet { Error::::NotAllowedQuoteAsset ); - // synthetic asset are forbidden - ensure!( - !T::SyntheticInfoProvider::is_synthetic(&order_book_id.base), - Error::::SyntheticAssetIsForbidden - ); - T::AssetInfoProvider::ensure_asset_exists(&order_book_id.base)?; T::EnsureTradingPairExists::ensure_trading_pair_exists( &order_book_id.dex_id, @@ -1093,7 +1094,8 @@ impl LiquiditySource::PriceCalculationFailed); - let fee = 0; // todo (m.tagirov) + // order-book doesn't take fee + let fee = Balance::zero(); match amount { QuoteAmount::WithDesiredInput { .. } => Ok(( @@ -1161,7 +1163,8 @@ impl LiquiditySource { @@ -1277,7 +1280,8 @@ impl LiquiditySource::InvalidOrderAmount ); - let fee = 0; // todo (m.tagirov) + // order-book doesn't take fee + let fee = Balance::zero(); Ok(SwapOutcome::new(*target_amount.balance(), fee)) } diff --git a/pallets/order-book/src/tests/extrinsics.rs b/pallets/order-book/src/tests/extrinsics.rs index 2a1350285a..c17081dd38 100644 --- a/pallets/order-book/src/tests/extrinsics.rs +++ b/pallets/order-book/src/tests/extrinsics.rs @@ -135,7 +135,7 @@ fn should_not_create_order_book_with_wrong_quote_asset() { } #[test] -fn should_not_create_order_book_with_synthetic_base_asset() { +fn should_create_order_book_with_synthetic_base_asset() { ext().execute_with(|| { let xstusd_order_book_id = OrderBookId::, DEXId> { dex_id: DEX.into(), @@ -143,15 +143,27 @@ fn should_not_create_order_book_with_synthetic_base_asset() { quote: XOR.into(), }; - assert_err!( - OrderBookPallet::create_orderbook( + if !TradingPair::is_trading_pair_enabled( + &xstusd_order_book_id.dex_id, + &xstusd_order_book_id.quote, + &xstusd_order_book_id.base, + ) + .unwrap() + { + assert_ok!(TradingPair::register( RawOrigin::Signed(alice()).into(), - xstusd_order_book_id - ), - E::SyntheticAssetIsForbidden - ); + xstusd_order_book_id.dex_id, + xstusd_order_book_id.quote, + xstusd_order_book_id.base + )); + } - // but should create with synthetic base asset - XST + assert_ok!(OrderBookPallet::create_orderbook( + RawOrigin::Signed(alice()).into(), + xstusd_order_book_id + )); + + // it should work with synthetic base asset - XST as well let xst_order_book_id = OrderBookId::, DEXId> { dex_id: DEX.into(), base: XST.into(), @@ -411,6 +423,12 @@ fn should_check_permissions_for_delete_order_book() { }; create_empty_order_book(order_book_id); + assert_ok!(OrderBookPallet::change_orderbook_status( + RuntimeOrigin::root(), + order_book_id, + OrderBookStatus::OnlyCancel + )); + assert_err!( OrderBookPallet::delete_orderbook(RawOrigin::Signed(alice()).into(), order_book_id), BadOrigin @@ -426,6 +444,11 @@ fn should_check_permissions_for_delete_order_book() { // Only more than half approvals from technical commitee are accepted create_empty_order_book(order_book_id); + assert_ok!(OrderBookPallet::change_orderbook_status( + RuntimeOrigin::root(), + order_book_id, + OrderBookStatus::Stop + )); if pallet_collective::Pallet::::members() .len() > 1 @@ -475,7 +498,7 @@ fn should_not_delete_unknown_order_book() { } #[test] -fn should_delete_order_book() { +fn should_not_delete_order_book_with_wrong_status() { ext().execute_with(|| { let order_book_id = OrderBookId::, DEXId> { dex_id: DEX.into(), @@ -483,62 +506,34 @@ fn should_delete_order_book() { quote: XOR.into(), }; - create_and_fill_order_book(order_book_id); - let owner = bob(); - - let tech_account = technical::Pallet::::tech_account_id_to_account_id( - &OrderBookPallet::tech_account_for_order_book(order_book_id.clone()), - ) - .unwrap(); - - // not empty at the beginning - assert!(!OrderBookPallet::aggregated_bids(&order_book_id).is_empty()); - assert!(!OrderBookPallet::aggregated_asks(&order_book_id).is_empty()); - assert!(!OrderBookPallet::user_limit_orders(&owner, &order_book_id) - .unwrap() - .is_empty()); - - // some balance is locked in limit orders - assert_ne!(free_balance(&order_book_id.base, &owner), INIT_BALANCE); - assert_ne!(free_balance(&order_book_id.quote, &owner), INIT_BALANCE); - - // tech account keeps the locked assets - assert!(free_balance(&order_book_id.base, &tech_account) > balance!(0)); - assert!(free_balance(&order_book_id.quote, &tech_account) > balance!(0)); + create_empty_order_book(order_book_id); - // delete the order book - assert_ok!(OrderBookPallet::delete_orderbook( + assert_ok!(OrderBookPallet::change_orderbook_status( RuntimeOrigin::root(), - order_book_id + order_book_id, + OrderBookStatus::Trade )); - // empty after canceling of all limit orders - assert!(OrderBookPallet::aggregated_bids(&order_book_id).is_empty()); - assert!(OrderBookPallet::aggregated_asks(&order_book_id).is_empty()); - assert_eq!( - OrderBookPallet::user_limit_orders(&owner, &order_book_id), - None + assert_err!( + OrderBookPallet::delete_orderbook(RuntimeOrigin::root(), order_book_id), + E::ForbiddenStatusToDeleteOrderBook ); - // locked balance is unlocked - assert_eq!(free_balance(&order_book_id.base, &owner), INIT_BALANCE); - assert_eq!(free_balance(&order_book_id.quote, &owner), INIT_BALANCE); + assert_ok!(OrderBookPallet::change_orderbook_status( + RuntimeOrigin::root(), + order_book_id, + OrderBookStatus::PlaceAndCancel + )); - // tech account balance is empty after canceling of all limit orders - assert_eq!( - free_balance(&order_book_id.base, &tech_account), - balance!(0) - ); - assert_eq!( - free_balance(&order_book_id.quote, &tech_account), - balance!(0) + assert_err!( + OrderBookPallet::delete_orderbook(RuntimeOrigin::root(), order_book_id), + E::ForbiddenStatusToDeleteOrderBook ); }); } #[test] -#[ignore] // it works, but takes a lot of time -fn should_delete_order_book_with_a_lot_of_orders() { +fn should_not_delete_order_book_with_orders() { ext().execute_with(|| { let order_book_id = OrderBookId::, DEXId> { dex_id: DEX.into(), @@ -546,55 +541,65 @@ fn should_delete_order_book_with_a_lot_of_orders() { quote: XOR.into(), }; - assert_ok!(OrderBookPallet::create_orderbook( - RawOrigin::Signed(alice()).into(), - order_book_id - )); - - let order_book = OrderBookPallet::order_books(order_book_id).unwrap(); - - let mut buy_price: OrderPrice = balance!(1000).into(); - let mut buy_lifespan = 10000; // ms - let mut sell_price: OrderPrice = balance!(1001).into(); - let mut sell_lifespan = 10000; // ms + create_and_fill_order_book(order_book_id); - let max_prices_for_side: u32 = ::MaxSidePriceCount::get(); + assert_ok!(OrderBookPallet::change_orderbook_status( + RuntimeOrigin::root(), + order_book_id, + OrderBookStatus::Stop + )); - for i in 0..max_prices_for_side { - // get new owner for each order to not get UserHasMaxCountOfOpenedOrders error - let account = generate_account(i); + assert_err!( + OrderBookPallet::delete_orderbook(RuntimeOrigin::root(), order_book_id), + E::OrderBookIsNotEmpty + ); + }); +} - fill_balance(account.clone(), order_book_id); +#[test] +fn should_delete_order_book() { + ext().execute_with(|| { + let order_book_id = OrderBookId::, DEXId> { + dex_id: DEX.into(), + base: VAL.into(), + quote: XOR.into(), + }; - buy_price -= order_book.tick_size; - sell_price += order_book.tick_size; - buy_lifespan += 5000; - sell_lifespan += 5000; + create_empty_order_book(order_book_id); + let owner = bob(); - assert_ok!(OrderBookPallet::place_limit_order( - RawOrigin::Signed(account.clone()).into(), - order_book_id, - *buy_price.balance(), - balance!(10), - PriceVariant::Buy, - Some(buy_lifespan) - )); + let tech_account = technical::Pallet::::tech_account_id_to_account_id( + &OrderBookPallet::tech_account_for_order_book(order_book_id.clone()), + ) + .unwrap(); - assert_ok!(OrderBookPallet::place_limit_order( - RawOrigin::Signed(account).into(), - order_book_id, - *sell_price.balance(), - balance!(10), - PriceVariant::Sell, - Some(sell_lifespan) - )); - } + assert_ok!(OrderBookPallet::change_orderbook_status( + RuntimeOrigin::root(), + order_book_id, + OrderBookStatus::Stop + )); // delete the order book assert_ok!(OrderBookPallet::delete_orderbook( RuntimeOrigin::root(), order_book_id )); + + assert!(OrderBookPallet::aggregated_bids(&order_book_id).is_empty()); + assert!(OrderBookPallet::aggregated_asks(&order_book_id).is_empty()); + assert_eq!( + OrderBookPallet::user_limit_orders(&owner, &order_book_id), + None + ); + + assert_eq!( + free_balance(&order_book_id.base, &tech_account), + balance!(0) + ); + assert_eq!( + free_balance(&order_book_id.quote, &tech_account), + balance!(0) + ); }); } diff --git a/pallets/order-book/src/tests/fee_calculator.rs b/pallets/order-book/src/tests/fee_calculator.rs new file mode 100644 index 0000000000..5a187b7531 --- /dev/null +++ b/pallets/order-book/src/tests/fee_calculator.rs @@ -0,0 +1,112 @@ +// This file is part of the SORA network and Polkaswap app. + +// Copyright (c) 2020, 2021, Polka Biome Ltd. All rights reserved. +// SPDX-License-Identifier: BSD-4-Clause + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// All advertising materials mentioning features or use of this software must display +// the following acknowledgement: This product includes software developed by Polka Biome +// Ltd., SORA, and Polkaswap. +// +// Neither the name of the Polka Biome Ltd. nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY Polka Biome Ltd. AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Polka Biome Ltd. BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#![cfg(feature = "wip")] // order-book + +use common::balance; +use common::prelude::constants::SMALL_FEE; +use framenode_runtime::order_book::fee_calculator::FeeCalculator; +use framenode_runtime::order_book::Config; +use framenode_runtime::Runtime; + +#[test] +fn should_calculate_place_limit_order_fee() { + let max_lifetime = ::MAX_ORDER_LIFESPAN; + + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(0), true, false).unwrap(), + balance!(0.0002) + ); + + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(max_lifetime / 10), true, false) + .unwrap(), + balance!(0.000215) + ); + + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(max_lifetime / 5), true, false) + .unwrap(), + balance!(0.00023) + ); + + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(max_lifetime / 2), true, false) + .unwrap(), + balance!(0.000275) + ); + + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(max_lifetime), true, false).unwrap(), + SMALL_FEE / 2 + ); +} + +#[test] +fn should_calculate_place_limit_order_fee_without_weight() { + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(1000), false, false).unwrap(), + SMALL_FEE + ); + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(0), false, false).unwrap(), + SMALL_FEE + ); + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(1000000000000000), false, false) + .unwrap(), + SMALL_FEE + ); + assert_eq!( + FeeCalculator::::place_limit_order_fee(None, false, false).unwrap(), + SMALL_FEE + ); +} + +#[test] +fn should_calculate_place_limit_order_fee_with_error() { + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(1000), true, true).unwrap(), + SMALL_FEE + ); + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(0), true, true).unwrap(), + SMALL_FEE + ); + assert_eq!( + FeeCalculator::::place_limit_order_fee(Some(1000000000000000), true, true) + .unwrap(), + SMALL_FEE + ); + assert_eq!( + FeeCalculator::::place_limit_order_fee(None, true, true).unwrap(), + SMALL_FEE + ); +} diff --git a/pallets/order-book/src/tests/mod.rs b/pallets/order-book/src/tests/mod.rs index 445a6a5149..020cbdf766 100644 --- a/pallets/order-book/src/tests/mod.rs +++ b/pallets/order-book/src/tests/mod.rs @@ -30,6 +30,7 @@ mod data_layer; mod extrinsics; +mod fee_calculator; mod order_book; mod orders; mod pallet; diff --git a/pallets/order-book/src/tests/pallet.rs b/pallets/order-book/src/tests/pallet.rs index 7a695b43e6..3950cae99b 100644 --- a/pallets/order-book/src/tests/pallet.rs +++ b/pallets/order-book/src/tests/pallet.rs @@ -1120,7 +1120,6 @@ fn should_quote() { SwapOutcome::new(balance!(251.66326).into(), 0) ); - // todo (m.tagirov) remake when fee introduced // with fee assert_eq!( OrderBookPallet::quote( @@ -1390,7 +1389,6 @@ fn should_quote_without_impact() { SwapOutcome::new(balance!(250).into(), 0) ); - // todo (m.tagirov) remake when fee introduced // with fee assert_eq!( OrderBookPallet::quote_without_impact( diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 81334c11ef..134512b50e 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -5,7 +5,7 @@ license = "BSD-4-Clause" homepage = "https://sora.org" repository = "https://github.com/sora-xor/sora2-network" name = "framenode-runtime" -version = "3.0.1" +version = "3.1.0" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 8b000d6697..006a563103 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -256,10 +256,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("sora-substrate"), impl_name: create_runtime_str!("sora-substrate"), authoring_version: 1, - spec_version: 65, + spec_version: 66, impl_version: 1, apis: RUNTIME_API_VERSIONS, - transaction_version: 65, + transaction_version: 66, state_version: 0, }; @@ -1031,6 +1031,7 @@ parameter_types! { pub const MaxSubAccounts: u32 = 100; pub const MaxAdditionalFields: u32 = 100; pub const MaxRegistrars: u32 = 20; + pub const MaxAdditionalDataLength: u32 = 128; pub ReferralsReservesAcc: AccountId = { let tech_account_id = TechAccountId::from_generic_pair( b"referrals".to_vec(), @@ -1058,6 +1059,7 @@ impl liquidity_proxy::Config for Runtime { pallet_collective::EnsureProportionMoreThan, EnsureRoot, >; + type MaxAdditionalDataLength = MaxAdditionalDataLength; } impl mock_liquidity_source::Config for Runtime { diff --git a/runtime/src/migrations.rs b/runtime/src/migrations.rs index 4e560a9c69..e18cc6e832 100644 --- a/runtime/src/migrations.rs +++ b/runtime/src/migrations.rs @@ -1 +1,31 @@ +// This file is part of the SORA network and Polkaswap app. + +// Copyright (c) 2020, 2021, Polka Biome Ltd. All rights reserved. +// SPDX-License-Identifier: BSD-4-Clause + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// All advertising materials mentioning features or use of this software must display +// the following acknowledgement: This product includes software developed by Polka Biome +// Ltd., SORA, and Polkaswap. +// +// Neither the name of the Polka Biome Ltd. nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY Polka Biome Ltd. AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Polka Biome Ltd. BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + pub type Migrations = (); diff --git a/runtime/src/mock.rs b/runtime/src/mock.rs index e997aff237..89993da19d 100644 --- a/runtime/src/mock.rs +++ b/runtime/src/mock.rs @@ -1,3 +1,33 @@ +// This file is part of the SORA network and Polkaswap app. + +// Copyright (c) 2020, 2021, Polka Biome Ltd. All rights reserved. +// SPDX-License-Identifier: BSD-4-Clause + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// All advertising materials mentioning features or use of this software must display +// the following acknowledgement: This product includes software developed by Polka Biome +// Ltd., SORA, and Polkaswap. +// +// Neither the name of the Polka Biome Ltd. nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY Polka Biome Ltd. AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Polka Biome Ltd. BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + use common::mock::alice; use common::PriceVariant; use price_tools::AVG_BLOCK_SPAN; diff --git a/runtime/src/tests/xor_fee.rs b/runtime/src/tests/xor_fee.rs index ee640f72e8..4c81d960be 100644 --- a/runtime/src/tests/xor_fee.rs +++ b/runtime/src/tests/xor_fee.rs @@ -29,6 +29,13 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::mock::{ensure_pool_initialized, fill_spot_price}; + +#[cfg(feature = "wip")] // order-book +use { + crate::order_book::OrderBookId, + common::{DEXId, PriceVariant}, +}; + use crate::xor_fee_impls::{CustomFeeDetails, CustomFees}; use crate::{ AccountId, AssetId, Assets, Balance, Balances, Currencies, GetXorFeeAccountId, PoolXYK, @@ -39,19 +46,19 @@ use common::mock::{alice, bob, charlie}; use common::prelude::constants::{BIG_FEE, SMALL_FEE}; use common::prelude::{AssetName, AssetSymbol, FixedWrapper, SwapAmount}; use common::{balance, fixed_wrapper, AssetInfoProvider, FilterMode, VAL, XOR}; -use frame_support::assert_ok; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; use frame_support::pallet_prelude::{InvalidTransaction, Pays}; use frame_support::traits::{OnFinalize, OnInitialize}; use frame_support::unsigned::TransactionValidityError; use frame_support::weights::WeightToFee as WeightToFeeTrait; +use frame_support::{assert_err, assert_ok}; use frame_system::EventRecord; use framenode_chain_spec::ext; use log::LevelFilter; use pallet_balances::NegativeImbalance; use pallet_transaction_payment::OnChargeTransaction; use referrals::ReferrerBalances; -use sp_runtime::traits::SignedExtension; +use sp_runtime::traits::{Dispatchable, SignedExtension}; use sp_runtime::{AccountId32, FixedPointNumber, FixedU128}; use traits::MultiCurrency; use xor_fee::extension::ChargeTransactionPayment; @@ -917,3 +924,391 @@ fn it_works_eth_bridge_pays_no() { ); }); } + +#[cfg(feature = "wip")] // order-book +#[test] +fn fee_not_postponed_place_limit_order() { + ext().execute_with(|| { + set_weight_to_fee_multiplier(1); + give_xor_initial_balance(alice()); + + let order_book_id = OrderBookId { + dex_id: DEXId::Polkaswap.into(), + base: VAL.into(), + quote: XOR.into(), + }; + + let dispatch_info = info_from_weight(Weight::from_parts(100_000_000, 0)); + + let call = RuntimeCall::OrderBook(order_book::Call::place_limit_order { + order_book_id, + price: balance!(11), + amount: balance!(100), + side: PriceVariant::Sell, + lifespan: None, + }); + + let quoted_fee = + xor_fee::Pallet::::withdraw_fee(&alice(), &call, &dispatch_info, SMALL_FEE, 0) + .unwrap(); + + assert_eq!( + quoted_fee, + LiquidityInfo::Paid(alice(), Some(NegativeImbalance::new(SMALL_FEE))) + ); + }); +} + +#[cfg(feature = "wip")] // order-book +#[test] +fn withdraw_fee_place_limit_order_with_default_lifetime() { + ext().execute_with(|| { + set_weight_to_fee_multiplier(1); + give_xor_initial_balance(alice()); + + let order_book_id = OrderBookId { + dex_id: DEXId::Polkaswap.into(), + base: VAL.into(), + quote: XOR.into(), + }; + + let initial_balance = Assets::free_balance(&XOR.into(), &alice()).unwrap(); + + let len: usize = 10; + let dispatch_info = info_from_weight(Weight::from_parts(100_000_000, 0)); + let call = RuntimeCall::OrderBook(order_book::Call::place_limit_order { + order_book_id, + price: balance!(11), + amount: balance!(100), + side: PriceVariant::Sell, + lifespan: None, + }); + + let pre = ChargeTransactionPayment::::new() + .pre_dispatch(&alice(), &call, &dispatch_info, len) + .unwrap(); + assert!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &dispatch_info, + &post_info_from_weight(MOCK_WEIGHT), + len, + &Ok(()) + ) + .is_ok()); + + let fee = SMALL_FEE / 2; + + assert_eq!( + Assets::free_balance(&XOR.into(), &alice()).unwrap(), + initial_balance - fee + ); + }); +} + +#[cfg(feature = "wip")] // order-book +#[test] +fn withdraw_fee_place_limit_order_with_some_lifetime() { + ext().execute_with(|| { + set_weight_to_fee_multiplier(1); + give_xor_initial_balance(alice()); + + let order_book_id = OrderBookId { + dex_id: DEXId::Polkaswap.into(), + base: VAL.into(), + quote: XOR.into(), + }; + + let initial_balance = Assets::free_balance(&XOR.into(), &alice()).unwrap(); + + let len: usize = 10; + let dispatch_info = info_from_weight(Weight::from_parts(100_000_000, 0)); + let call = RuntimeCall::OrderBook(order_book::Call::place_limit_order { + order_book_id, + price: balance!(11), + amount: balance!(100), + side: PriceVariant::Sell, + lifespan: Some(259200000), + }); + + let pre = ChargeTransactionPayment::::new() + .pre_dispatch(&alice(), &call, &dispatch_info, len) + .unwrap(); + assert!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &dispatch_info, + &post_info_from_weight(MOCK_WEIGHT), + len, + &Ok(()) + ) + .is_ok()); + + let fee = balance!(0.000215); + + assert_eq!( + Assets::free_balance(&XOR.into(), &alice()).unwrap(), + initial_balance - fee + ); + }); +} + +#[cfg(feature = "wip")] // order-book +#[test] +fn withdraw_fee_place_limit_order_with_error() { + ext().execute_with(|| { + set_weight_to_fee_multiplier(1); + give_xor_initial_balance(alice()); + + let order_book_id = OrderBookId { + dex_id: DEXId::Polkaswap.into(), + base: VAL.into(), + quote: XOR.into(), + }; + + let initial_balance = Assets::free_balance(&XOR.into(), &alice()).unwrap(); + + let len: usize = 10; + let dispatch_info = info_from_weight(Weight::from_parts(100_000_000, 0)); + let call = RuntimeCall::OrderBook(order_book::Call::place_limit_order { + order_book_id, + price: balance!(11), + amount: balance!(100), + side: PriceVariant::Sell, + lifespan: None, + }); + + let pre = ChargeTransactionPayment::::new() + .pre_dispatch(&alice(), &call, &dispatch_info, len) + .unwrap(); + assert!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &dispatch_info, + &post_info_from_weight(MOCK_WEIGHT), + len, + &Err(order_book::Error::::InvalidLimitOrderPrice.into()) + ) + .is_ok()); + + let fee = SMALL_FEE; + + assert_eq!( + Assets::free_balance(&XOR.into(), &alice()).unwrap(), + initial_balance - fee + ); + }); +} + +#[cfg(feature = "wip")] // order-book +#[test] +fn withdraw_fee_place_limit_order_with_crossing_spread() { + ext().execute_with(|| { + set_weight_to_fee_multiplier(1); + give_xor_initial_balance(alice()); + + let order_book_id = OrderBookId { + dex_id: DEXId::Polkaswap.into(), + base: VAL.into(), + quote: XOR.into(), + }; + + let initial_balance = Assets::free_balance(&XOR.into(), &alice()).unwrap(); + + let len: usize = 10; + let dispatch_info = info_from_weight(Weight::from_parts(100_000_000, 0)); + let call = RuntimeCall::OrderBook(order_book::Call::place_limit_order { + order_book_id, + price: balance!(11), + amount: balance!(100), + side: PriceVariant::Sell, + lifespan: None, + }); + + let pre = ChargeTransactionPayment::::new() + .pre_dispatch(&alice(), &call, &dispatch_info, len) + .unwrap(); + assert!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &dispatch_info, + &default_post_info(), // none weight means that the limit was converted into market order + len, + &Ok(()) + ) + .is_ok()); + + let fee = SMALL_FEE; + + assert_eq!( + Assets::free_balance(&XOR.into(), &alice()).unwrap(), + initial_balance - fee + ); + }); +} + +/// Fee should be postponed until after the transaction +#[test] +fn fee_payment_postponed_xorless_transfer() { + ext().execute_with(|| { + set_weight_to_fee_multiplier(1); + increase_balance(alice(), VAL.into(), balance!(1000)); + + increase_balance(bob(), XOR.into(), balance!(1000)); + increase_balance(bob(), VAL.into(), balance!(1000)); + + ensure_pool_initialized(XOR.into(), VAL.into()); + PoolXYK::deposit_liquidity( + RuntimeOrigin::signed(bob()), + 0, + XOR.into(), + VAL.into(), + balance!(500), + balance!(500), + balance!(450), + balance!(450), + ) + .unwrap(); + + fill_spot_price(); + + let dispatch_info = info_from_weight(Weight::from_parts(100_000_000, 0)); + + let call = RuntimeCall::LiquidityProxy(liquidity_proxy::Call::xorless_transfer { + dex_id: 0, + asset_id: VAL, + // Swap with desired output may return less tokens than requested + desired_xor_amount: 0, + max_amount_in: 0, + amount: balance!(500), + selected_source_types: vec![], + filter_mode: FilterMode::Disabled, + receiver: alice(), + additional_data: Default::default(), + }); + + let quoted_fee = + xor_fee::Pallet::::withdraw_fee(&bob(), &call, &dispatch_info, SMALL_FEE, 0) + .unwrap(); + + assert_eq!( + quoted_fee, + LiquidityInfo::Paid(bob(), Some(NegativeImbalance::new(SMALL_FEE))) + ); + + let call = RuntimeCall::LiquidityProxy(liquidity_proxy::Call::xorless_transfer { + dex_id: 0, + asset_id: VAL, + // Swap with desired output may return less tokens than requested + desired_xor_amount: SMALL_FEE + 1, + max_amount_in: balance!(1), + amount: balance!(10), + selected_source_types: vec![], + filter_mode: FilterMode::Disabled, + receiver: bob(), + additional_data: Default::default(), + }); + + let quoted_fee = + xor_fee::Pallet::::withdraw_fee(&alice(), &call, &dispatch_info, SMALL_FEE, 0) + .unwrap(); + + assert_eq!(quoted_fee, LiquidityInfo::Postponed(alice())); + + assert_eq!( + Assets::total_balance(&XOR.into(), &alice()).unwrap(), + balance!(0) + ); + assert_eq!( + Assets::total_balance(&VAL.into(), &alice()).unwrap(), + balance!(1000) + ); + + let post_info = call.dispatch(RuntimeOrigin::signed(alice())).unwrap(); + + assert_eq!( + Assets::total_balance(&XOR.into(), &alice()).unwrap(), + SMALL_FEE + ); + assert_eq!( + Assets::total_balance(&VAL.into(), &alice()).unwrap(), + balance!(989.999297892695135178) + ); + assert_eq!( + Assets::total_balance(&VAL.into(), &bob()).unwrap(), + balance!(510) + ); + + assert_ok!(xor_fee::Pallet::::correct_and_deposit_fee( + &alice(), + &dispatch_info, + &post_info, + SMALL_FEE, + 0, + quoted_fee + )); + + assert_eq!(Assets::total_balance(&XOR.into(), &alice()).unwrap(), 0); + }); +} + +/// Fee should be postponed until after the transaction +#[test] +fn fee_payment_postpone_failed_xorless_transfer() { + ext().execute_with(|| { + set_weight_to_fee_multiplier(1); + increase_balance(alice(), VAL.into(), balance!(1000)); + + increase_balance(bob(), XOR.into(), balance!(1000)); + increase_balance(bob(), VAL.into(), balance!(1000)); + + ensure_pool_initialized(XOR.into(), VAL.into()); + PoolXYK::deposit_liquidity( + RuntimeOrigin::signed(bob()), + 0, + XOR.into(), + VAL.into(), + balance!(500), + balance!(500), + balance!(450), + balance!(450), + ) + .unwrap(); + + fill_spot_price(); + + let dispatch_info = info_from_weight(Weight::from_parts(100_000_000, 0)); + + let call = RuntimeCall::LiquidityProxy(liquidity_proxy::Call::xorless_transfer { + dex_id: 0, + asset_id: VAL, + // Swap with desired output may return less tokens than requested + desired_xor_amount: SMALL_FEE + 1, + max_amount_in: 1, + amount: balance!(10), + selected_source_types: vec![], + filter_mode: FilterMode::Disabled, + receiver: bob(), + additional_data: Default::default(), + }); + + assert_err!( + xor_fee::Pallet::::withdraw_fee(&alice(), &call, &dispatch_info, SMALL_FEE, 0), + TransactionValidityError::Invalid(InvalidTransaction::Payment) + ); + + let call = RuntimeCall::LiquidityProxy(liquidity_proxy::Call::xorless_transfer { + dex_id: 0, + asset_id: VAL, + // Swap with desired output may return less tokens than requested + desired_xor_amount: 0, + max_amount_in: 0, + amount: balance!(500), + selected_source_types: vec![], + filter_mode: FilterMode::Disabled, + receiver: bob(), + additional_data: Default::default(), + }); + + assert_err!( + xor_fee::Pallet::::withdraw_fee(&alice(), &call, &dispatch_info, SMALL_FEE, 0), + TransactionValidityError::Invalid(InvalidTransaction::Payment) + ); + }); +} diff --git a/runtime/src/xor_fee_impls.rs b/runtime/src/xor_fee_impls.rs index 7372fb9bbb..807975da6b 100644 --- a/runtime/src/xor_fee_impls.rs +++ b/runtime/src/xor_fee_impls.rs @@ -1,3 +1,33 @@ +// This file is part of the SORA network and Polkaswap app. + +// Copyright (c) 2020, 2021, Polka Biome Ltd. All rights reserved. +// SPDX-License-Identifier: BSD-4-Clause + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// All advertising materials mentioning features or use of this software must display +// the following acknowledgement: This product includes software developed by Polka Biome +// Ltd., SORA, and Polkaswap. +// +// Neither the name of the Polka Biome Ltd. nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY Polka Biome Ltd. AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Polka Biome Ltd. BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + use crate::*; use common::LiquidityProxyTrait; use pallet_utility::Call as UtilityCall; @@ -87,6 +117,9 @@ impl CustomFees { | RuntimeCall::VestedRewards(vested_rewards::Call::claim_rewards { .. }) => { Some(BIG_FEE) } + + #[cfg(feature = "wip")] // order-book + RuntimeCall::OrderBook(order_book::Call::update_orderbook { .. }) => Some(BIG_FEE), RuntimeCall::Assets(..) | RuntimeCall::EthBridge(..) | RuntimeCall::LiquidityProxy(..) @@ -97,6 +130,9 @@ impl CustomFees { | RuntimeCall::TradingPair(..) | RuntimeCall::Band(..) | RuntimeCall::Referrals(..) => Some(SMALL_FEE), + + #[cfg(feature = "wip")] // order-book + RuntimeCall::OrderBook(..) => Some(SMALL_FEE), _ => None, } } @@ -106,6 +142,10 @@ impl CustomFees { pub enum CustomFeeDetails { /// Regular call with custom fee without any additional logic Regular(Balance), + + #[cfg(feature = "wip")] // order-book + /// OrderBook::place_limit_order custom fee depends on limit order lifetime + LimitOrderLifetime(Option), } // Flat fees implementation for the selected extrinsics. @@ -116,7 +156,16 @@ impl xor_fee::ApplyCustomFees for CustomFees { fn compute_fee(call: &RuntimeCall) -> Option<(Balance, CustomFeeDetails)> { let fee = Self::base_fee(call)?; - Some((fee, CustomFeeDetails::Regular(fee))) + + let details = match call { + #[cfg(feature = "wip")] // order-book + RuntimeCall::OrderBook(order_book::Call::place_limit_order { lifespan, .. }) => { + CustomFeeDetails::LimitOrderLifetime(*lifespan) + } + _ => CustomFeeDetails::Regular(fee), + }; + + Some((fee, details)) } fn should_be_postponed( @@ -193,6 +242,55 @@ impl xor_fee::ApplyCustomFees for CustomFees { return false; } } + RuntimeCall::LiquidityProxy(liquidity_proxy::Call::xorless_transfer { + dex_id, + asset_id, + amount, + selected_source_types, + filter_mode, + desired_xor_amount, + max_amount_in, + .. + }) => { + // Pay fee as usual + if balance > fee { + return false; + } + + // Check how much user has input asset + let user_input_balance = Currencies::free_balance(*asset_id, who); + + // The amount of input asset needed for this swap is more than the user has, so error + if amount.saturating_add(*max_amount_in) > user_input_balance { + return false; + } + + let filter = LiquiditySourceFilter::with_mode( + *dex_id, + filter_mode.clone(), + selected_source_types.clone(), + ); + let Ok(swap_result) = LiquidityProxy::quote( + *dex_id, + asset_id, + &XOR, + QuoteAmount::with_desired_output(*desired_xor_amount), + filter, + true, + ) else { + return false; + }; + if swap_result.amount <= *max_amount_in + && balance + .saturating_add(*desired_xor_amount) + .saturating_sub(Balances::minimum_balance()) + >= fee + { + return true; + } else { + return false; + } + } _ => return false, } } @@ -213,6 +311,15 @@ impl xor_fee::ApplyCustomFees for CustomFees { let fee_details = fee_details?; match fee_details { CustomFeeDetails::Regular(fee) => Some(fee), + + #[cfg(feature = "wip")] // order-book + CustomFeeDetails::LimitOrderLifetime(lifetime) => { + order_book::fee_calculator::FeeCalculator::::place_limit_order_fee( + lifetime, + _post_info.actual_weight.is_some(), + _result.is_err(), + ) + } } }