diff --git a/Cargo.lock b/Cargo.lock index f48c82e40..050ff5c73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6998,11 +6998,14 @@ dependencies = [ "frame-system", "function_name", "jsonrpsee", - "lazy_static", "log", "manta-collator-selection", "manta-primitives", + "orml-traits", + "pallet-asset-manager", + "pallet-assets", "pallet-balances", + "pallet-farming", "pallet-parachain-staking", "pallet-preimage", "pallet-randomness", @@ -7012,6 +7015,7 @@ dependencies = [ "rand 0.8.5", "runtime-common", "scale-info", + "serde", "session-key-primitives", "similar-asserts", "sp-api", @@ -7022,6 +7026,7 @@ dependencies = [ "sp-runtime", "sp-staking", "sp-std", + "xcm", ] [[package]] diff --git a/node/src/chain_specs/calamari.rs b/node/src/chain_specs/calamari.rs index f8ad38a72..3f7685d4e 100644 --- a/node/src/chain_specs/calamari.rs +++ b/node/src/chain_specs/calamari.rs @@ -238,6 +238,7 @@ fn calamari_dev_genesis( min_deposit: 5_000 * KMA, min_withdraw: 5_000 * KMA, gas_reserve: 10_000 * KMA, + farming_pool_params: Default::default(), }, } } diff --git a/node/src/chain_specs/manta/mod.rs b/node/src/chain_specs/manta/mod.rs index e26fa7601..1211d32f9 100644 --- a/node/src/chain_specs/manta/mod.rs +++ b/node/src/chain_specs/manta/mod.rs @@ -130,6 +130,7 @@ fn manta_devnet_genesis(genesis_collators: Vec) -> GenesisConfig { min_deposit: 500 * MANTA, min_withdraw: 10 * MANTA, gas_reserve: 1_000 * MANTA, + farming_pool_params: Default::default(), }, parachain_info: manta_runtime::ParachainInfoConfig { parachain_id: MANTA_PARACHAIN_ID.into(), diff --git a/pallets/farming/src/lib.rs b/pallets/farming/src/lib.rs index 5ea4ee9ca..bfdfa4a8d 100644 --- a/pallets/farming/src/lib.rs +++ b/pallets/farming/src/lib.rs @@ -430,51 +430,7 @@ pub mod pallet { ) -> DispatchResult { let exchanger = ensure_signed(origin)?; - let mut pool_info = Self::pool_infos(pool_id).ok_or(Error::::PoolDoesNotExist)?; - ensure!( - PoolState::state_valid(Action::Deposit, pool_info.state), - Error::::InvalidPoolState - ); - - if let PoolState::Charged = pool_info.state { - let n: BlockNumberFor = frame_system::Pallet::::block_number(); - ensure!( - n >= pool_info.after_block_to_start, - Error::::CanNotDeposit - ); - } - - // basic token proportion * add_value * token_proportion - // if basic token proportion and token_proportion both equals to 100%, then the final amount to transfer is equal to add_value - let native_amount = pool_info.basic_token.1.saturating_reciprocal_mul(add_value); - pool_info.tokens_proportion.iter().try_for_each( - |(token, proportion)| -> DispatchResult { - T::MultiCurrency::transfer( - *token, - &exchanger, - &pool_info.keeper, - *proportion * native_amount, - ) - }, - )?; - Self::add_share(&exchanger, pool_id, &mut pool_info, add_value); - - if let Some((gauge_value, gauge_block)) = gauge_info { - Self::gauge_add( - &exchanger, - pool_info.gauge.ok_or(Error::::GaugePoolNotExist)?, - gauge_value, - gauge_block, - )?; - } - - Self::deposit_event(Event::Deposited { - who: exchanger, - pid: pool_id, - add_value, - gauge_info, - }); - Ok(()) + Self::deposit_farming(exchanger, pool_id, add_value, gauge_info) } /// `Withdraw` operation only remove share and get claim rewards, then update `withdraw_list` of user share info. @@ -488,32 +444,7 @@ pub mod pallet { ) -> DispatchResult { let exchanger = ensure_signed(origin)?; - let pool_info = Self::pool_infos(pool_id).ok_or(Error::::PoolDoesNotExist)?; - ensure!( - PoolState::state_valid(Action::Withdraw, pool_info.state), - Error::::InvalidPoolState - ); - - let share_info = Self::shares_and_withdrawn_rewards(pool_id, &exchanger) - .ok_or(Error::::ShareInfoNotExists)?; - ensure!( - share_info.withdraw_list.len() < pool_info.withdraw_limit_count.into(), - Error::::WithdrawLimitCountExceeded - ); - - Self::remove_share( - &exchanger, - pool_id, - remove_value, - pool_info.withdraw_limit_time, - )?; - - Self::deposit_event(Event::Withdrawn { - who: exchanger, - pid: pool_id, - remove_value, - }); - Ok(()) + Self::withdraw_amount(exchanger, pool_id, remove_value) } /// `claim` operation can claim rewards and also un-stake if user share info has `withdraw_list`. @@ -555,14 +486,7 @@ pub mod pallet { pub fn withdraw_claim(origin: OriginFor, pool_id: PoolId) -> DispatchResult { let exchanger = ensure_signed(origin)?; - let pool_info = Self::pool_infos(pool_id).ok_or(Error::::PoolDoesNotExist)?; - Self::process_withdraw_list(&exchanger, pool_id, &pool_info, false)?; - - Self::deposit_event(Event::WithdrawClaimed { - who: exchanger, - pid: pool_id, - }); - Ok(()) + Self::withdraw_farming(exchanger, pool_id) } #[pallet::call_index(6)] @@ -942,4 +866,110 @@ impl Pallet { .saturated_into(); total_reward_proportion } + + pub fn deposit_farming( + exchanger: T::AccountId, + pool_id: PoolId, + add_value: BalanceOf, + gauge_info: Option<(BalanceOf, BlockNumberFor)>, + ) -> DispatchResult { + let mut pool_info = Self::pool_infos(pool_id).ok_or(Error::::PoolDoesNotExist)?; + ensure!( + PoolState::state_valid(Action::Deposit, pool_info.state), + Error::::InvalidPoolState + ); + + if let PoolState::Charged = pool_info.state { + let n: BlockNumberFor = frame_system::Pallet::::block_number(); + ensure!( + n >= pool_info.after_block_to_start, + Error::::CanNotDeposit + ); + } + + // basic token proportion * add_value * token_proportion + // if basic token proportion and token_proportion both equals to 100%, then the final amount to transfer is equal to add_value + let native_amount = pool_info.basic_token.1.saturating_reciprocal_mul(add_value); + pool_info.tokens_proportion.iter().try_for_each( + |(token, proportion)| -> DispatchResult { + T::MultiCurrency::transfer( + *token, + &exchanger, + &pool_info.keeper, + *proportion * native_amount, + ) + }, + )?; + Self::add_share(&exchanger, pool_id, &mut pool_info, add_value); + + if let Some((gauge_value, gauge_block)) = gauge_info { + Self::gauge_add( + &exchanger, + pool_info.gauge.ok_or(Error::::GaugePoolNotExist)?, + gauge_value, + gauge_block, + )?; + } + + Self::deposit_event(Event::Deposited { + who: exchanger, + pid: pool_id, + add_value, + gauge_info, + }); + Ok(()) + } + + pub fn withdraw_farming(exchanger: T::AccountId, pool_id: PoolId) -> DispatchResult { + let pool_info = Self::pool_infos(pool_id).ok_or(Error::::PoolDoesNotExist)?; + Self::process_withdraw_list(&exchanger, pool_id, &pool_info, false)?; + + Self::deposit_event(Event::WithdrawClaimed { + who: exchanger, + pid: pool_id, + }); + Ok(()) + } + + pub fn withdraw_amount( + exchanger: T::AccountId, + pool_id: PoolId, + remove_value: Option>, + ) -> DispatchResult { + let pool_info = Self::pool_infos(pool_id).ok_or(Error::::PoolDoesNotExist)?; + ensure!( + PoolState::state_valid(Action::Withdraw, pool_info.state), + Error::::InvalidPoolState + ); + + let share_info = Self::shares_and_withdrawn_rewards(pool_id, &exchanger) + .ok_or(Error::::ShareInfoNotExists)?; + ensure!( + share_info.withdraw_list.len() < pool_info.withdraw_limit_count.into(), + Error::::WithdrawLimitCountExceeded + ); + + Self::remove_share( + &exchanger, + pool_id, + remove_value, + pool_info.withdraw_limit_time, + )?; + + Self::deposit_event(Event::Withdrawn { + who: exchanger, + pid: pool_id, + remove_value, + }); + Ok(()) + } + + pub fn withdraw_and_unstake( + exchanger: T::AccountId, + pool_id: PoolId, + remove_value: Option>, + ) -> DispatchResult { + Self::withdraw_amount(exchanger.clone(), pool_id, remove_value)?; + Self::withdraw_farming(exchanger, pool_id) + } } diff --git a/pallets/pallet-lottery/Cargo.toml b/pallets/pallet-lottery/Cargo.toml index 3e04c15e2..f27b6df4d 100644 --- a/pallets/pallet-lottery/Cargo.toml +++ b/pallets/pallet-lottery/Cargo.toml @@ -14,10 +14,12 @@ function_name = "0.3" jsonrpsee = { version = "0.16.2", features = ["server", "macros"], optional = true } log = { version = "0.4.0", default-features = false } scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } +serde = { version = "1.0.136", default-features = false, optional = true } # Substrate dependencies frame-support = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } frame-system = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "polkadot-v0.9.37", default-features = false } sp-api = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } sp-arithmetic = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } sp-blockchain = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37", optional = true } @@ -28,6 +30,7 @@ sp-std = { git = 'https://github.com/paritytech/substrate.git', default-features # Self dependencies manta-primitives = { path = "../../primitives/manta", default-features = false } +pallet-farming = { path = '../farming', default-features = false } pallet-parachain-staking = { path = '../parachain-staking', default-features = false } runtime-common = { path = "../../runtime/common", default-features = false } session-key-primitives = { path = '../../primitives/session-keys', default-features = false } @@ -38,8 +41,9 @@ rand = { version = "0.8.5", default-features = false, optional = true } [dev-dependencies] calamari-runtime = { path = "../../runtime/calamari", default-features = false } -lazy_static = "1.4.0" manta-collator-selection = { path = "../collator-selection", default-features = false } +pallet-asset-manager = { path = "../asset-manager", default-features = false } +pallet-assets = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } pallet-preimage = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } pallet-randomness = { path = '../randomness', default-features = false } @@ -48,6 +52,7 @@ pallet-transaction-payment = { git = "https://github.com/paritytech/substrate.gi rand = "0.8" similar-asserts = "1.1.0" sp-staking = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } +xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "release-v0.9.37", default-features = false } [features] default = ["std"] @@ -75,6 +80,8 @@ std = [ 'frame-benchmarking/std', "frame-support/std", "frame-system/std", + "serde/std", + "pallet-farming/std", ] try-runtime = [ "frame-support/try-runtime", diff --git a/pallets/pallet-lottery/src/lib.rs b/pallets/pallet-lottery/src/lib.rs index cd4cec5d1..23f1dcc06 100644 --- a/pallets/pallet-lottery/src/lib.rs +++ b/pallets/pallet-lottery/src/lib.rs @@ -104,7 +104,11 @@ pub mod pallet { PalletId, }; use frame_system::{pallet_prelude::*, RawOrigin}; + use manta_primitives::types::PoolId; + use orml_traits::MultiCurrency; use pallet_parachain_staking::BalanceOf; + #[cfg(feature = "std")] + use serde::{Deserialize, Serialize}; use sp_arithmetic::traits::SaturatedConversion; use sp_core::U256; use sp_runtime::{ @@ -118,7 +122,9 @@ pub mod pallet { pub type CallOf = ::RuntimeCall; #[pallet::config] - pub trait Config: frame_system::Config + pallet_parachain_staking::Config { + pub trait Config: + frame_system::Config + pallet_parachain_staking::Config + pallet_farming::Config + { /// The aggregated `RuntimeCall` type. type RuntimeCall: Parameter + Dispatchable @@ -131,7 +137,14 @@ pub mod pallet { Self::PalletsOrigin, Hash = Self::Hash, >; - // Randomness source to use for determining lottery winner + /// Helper to convert between Balance types of `MultiCurrency` and `Currency` (most likely equivalent types in runtime) + type BalanceConversion: Copy + + Into< + <::MultiCurrency as MultiCurrency< + ::AccountId, + >>::Balance, + > + From>; + /// Randomness source to use for determining lottery winner type RandomnessSource: Randomness; /// Something that can estimate the cost of sending an extrinsic type EstimateCallFee: frame_support::traits::EstimateCallFee< @@ -265,12 +278,28 @@ pub mod pallet { pub(super) type StakedCollators = StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf, ValueQuery>; + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[derive(Clone, Copy, Encode, Decode, TypeInfo, Default)] + pub struct FarmingParams { + pub mint_farming_token: bool, + pub destroy_farming_token: bool, + pub pool_id: PoolId, + pub currency_id: T, + } + + pub type FarmingParamsOf = FarmingParams<::CurrencyId>; + + /// Boolean for the minting of a farming token on `deposit` call + #[pallet::storage] + pub(super) type FarmingParameters = StorageValue<_, FarmingParamsOf, ValueQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { /// amount of token to keep in the pot for paying gas fees pub gas_reserve: BalanceOf, pub min_deposit: BalanceOf, pub min_withdraw: BalanceOf, + pub farming_pool_params: FarmingParamsOf, } #[cfg(feature = "std")] @@ -280,6 +309,12 @@ pub mod pallet { min_deposit: 1u32.into(), min_withdraw: 1u32.into(), gas_reserve: 10_000u32.into(), + farming_pool_params: FarmingParamsOf:: { + mint_farming_token: false, + destroy_farming_token: false, + pool_id: 0, + currency_id: ::CurrencyId::default(), + }, } } } @@ -291,6 +326,7 @@ pub mod pallet { GasReserve::::set(self.gas_reserve); MinDeposit::::set(self.min_deposit); MinWithdraw::::set(self.min_withdraw); + FarmingParameters::::set(self.farming_pool_params); } } @@ -394,6 +430,23 @@ pub mod pallet { Error::::PalletMisconfigured }; + let farming_params = FarmingParameters::::get(); + if farming_params.mint_farming_token { + // mint JUMBO token and put it in farming pool + let convert_amount: T::BalanceConversion = amount.into(); + ::MultiCurrency::deposit( + farming_params.currency_id, + &caller_account, + convert_amount.into(), + )?; + pallet_farming::Pallet::::deposit_farming( + caller_account.clone(), + farming_params.pool_id, + convert_amount.into(), + None, + )?; + } + // Transfer funds to pot ::Currency::transfer( &caller_account, @@ -461,6 +514,22 @@ pub mod pallet { Self::not_in_drawing_freezeout(), Error::::TooCloseToDrawing ); + + let farming_params = FarmingParameters::::get(); + if farming_params.destroy_farming_token { + let convert_amount: T::BalanceConversion = amount.into(); + pallet_farming::Pallet::::withdraw_and_unstake( + caller.clone(), + farming_params.pool_id, + Some(convert_amount.into()), + )?; + ::MultiCurrency::withdraw( + farming_params.currency_id, + &caller, + convert_amount.into(), + )?; + } + let now = >::block_number(); log::debug!("Requesting withdraw of {:?} tokens", amount); // Ensure user has enough funds active and mark them as offboarding (remove from `ActiveBalancePerUser`) @@ -849,6 +918,27 @@ pub mod pallet { GasReserve::::set(gas_reserve); Ok(()) } + #[pallet::call_index(12)] + #[pallet::weight(::WeightInfo::set_gas_reserve())] + pub fn set_farming_params( + origin: OriginFor, + mint_farming_token: bool, + burn_farming_token: bool, + pool_id: PoolId, + currency_id: ::CurrencyId, + ) -> DispatchResult { + T::ManageOrigin::ensure_origin(origin)?; + + let farming_params = FarmingParamsOf:: { + mint_farming_token, + destroy_farming_token: burn_farming_token, + pool_id, + currency_id, + }; + FarmingParameters::::set(farming_params); + + Ok(()) + } } impl Pallet { diff --git a/pallets/pallet-lottery/src/mock.rs b/pallets/pallet-lottery/src/mock.rs index 279d8a07c..d5b975233 100644 --- a/pallets/pallet-lottery/src/mock.rs +++ b/pallets/pallet-lottery/src/mock.rs @@ -18,15 +18,27 @@ use core::marker::PhantomData; use crate as pallet_lottery; -use crate::{pallet, Config}; +use crate::{pallet, Config, FarmingParamsOf}; use calamari_runtime::currency::KMA; use frame_support::{ - construct_runtime, parameter_types, - traits::{ConstU128, ConstU32, Everything, GenesisBuild, OnFinalize, OnInitialize}, + assert_ok, construct_runtime, ord_parameter_types, + pallet_prelude::*, + parameter_types, + traits::{ + AsEnsureOriginWithArg, ConstU128, ConstU32, EitherOfDiverse, Everything, GenesisBuild, + }, weights::Weight, }; -use frame_system::pallet_prelude::*; -use manta_primitives::types::{BlockNumber, Header}; +use frame_system::{pallet_prelude::*, EnsureNever, EnsureSignedBy}; +use manta_primitives::{ + assets::{ + AssetConfig, AssetIdType, AssetLocation, AssetRegistry, AssetRegistryMetadata, + AssetStorageMetadata, BalanceType, FungibleLedger, LocationType, NativeAndNonNative, + }, + constants::ASSET_MANAGER_PALLET_ID, + currencies::Currencies, + types::{BlockNumber, CalamariAssetId, Header, PoolId}, +}; use pallet_parachain_staking::{InflationInfo, Range}; use sp_core::H256; @@ -34,10 +46,30 @@ use sp_runtime::{ traits::{BlakeTwo256, Hash, IdentityLookup}, Perbill, Percent, }; +use xcm::{ + prelude::{Junctions, Parachain, X1}, + v2::MultiLocation, + VersionedMultiLocation, +}; pub type AccountId = u64; pub type Balance = u128; +pub const ALICE: AccountId = 1; +pub const BOB: AccountId = 2; +pub const CHARLIE: AccountId = 3; +pub const DAVE: AccountId = 4; +pub const EVE: AccountId = 5; +pub const TREASURY_ACCOUNT: AccountId = 10; + +pub const JUMBO: Balance = 1_000_000_000_000; +pub const INIT_JUMBO_AMOUNT: Balance = 100 * JUMBO; +pub const INIT_V_MANTA_AMOUNT: Balance = JUMBO; +pub const V_MANTA_ID: CalamariAssetId = 8; +pub const JUMBO_ID: CalamariAssetId = 9; + +pub const POOL_ID: PoolId = 0; + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -56,6 +88,9 @@ construct_runtime!( CollatorSelection: manta_collator_selection::{Pallet, Call, Storage, Config, Event}, Lottery: pallet_lottery::{Pallet, Call, Storage, Event, Config}, Preimage: pallet_preimage::{Pallet, Call, Storage, Event}, + Assets: pallet_assets::{Pallet, Storage, Config, Event}, + AssetManager: pallet_asset_manager::{Pallet, Call, Storage, Event}, + Farming: pallet_farming::{Pallet, Call, Storage, Event} } ); @@ -272,6 +307,179 @@ impl pallet_parachain_staking::Config for Test { type WeightInfo = calamari_runtime::weights::pallet_parachain_staking::SubstrateWeight; // XXX: Maybe use the actual calamari weights? } +parameter_types! { + // Does not really matter as this will be only called by root + pub const AssetDeposit: Balance = 0; + pub const AssetAccountDeposit: Balance = 0; + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + pub const MetadataDepositBase: Balance = 0; + pub const MetadataDepositPerByte: Balance = 0; +} + +impl pallet_assets::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = CalamariAssetId; + type Currency = Balances; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type AssetAccountDeposit = AssetAccountDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = pallet_assets::weights::SubstrateWeight; + type RemoveItemsLimit = ConstU32<1000>; + type AssetIdParameter = CalamariAssetId; + type CreateOrigin = AsEnsureOriginWithArg>; + type CallbackHandle = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +pub struct MantaAssetRegistry; +impl BalanceType for MantaAssetRegistry { + type Balance = Balance; +} +impl AssetIdType for MantaAssetRegistry { + type AssetId = CalamariAssetId; +} +impl AssetRegistry for MantaAssetRegistry { + type Metadata = AssetStorageMetadata; + type Error = sp_runtime::DispatchError; + + fn create_asset( + asset_id: CalamariAssetId, + metadata: AssetStorageMetadata, + min_balance: Balance, + is_sufficient: bool, + ) -> DispatchResult { + Assets::force_create( + RuntimeOrigin::root(), + asset_id, + AssetManager::account_id(), + is_sufficient, + min_balance, + )?; + + Assets::force_set_metadata( + RuntimeOrigin::root(), + asset_id, + metadata.name, + metadata.symbol, + metadata.decimals, + metadata.is_frozen, + )?; + + Assets::force_asset_status( + RuntimeOrigin::root(), + asset_id, + AssetManager::account_id(), + AssetManager::account_id(), + AssetManager::account_id(), + AssetManager::account_id(), + min_balance, + is_sufficient, + metadata.is_frozen, + ) + } + + fn update_asset_metadata( + asset_id: &CalamariAssetId, + metadata: AssetStorageMetadata, + ) -> DispatchResult { + Assets::force_set_metadata( + RuntimeOrigin::root(), + *asset_id, + metadata.name, + metadata.symbol, + metadata.decimals, + metadata.is_frozen, + ) + } +} + +parameter_types! { + pub const DummyAssetId: CalamariAssetId = 0; + pub const NativeAssetId: CalamariAssetId = 1; + pub const StartNonNativeAssetId: CalamariAssetId = 8; + pub NativeAssetLocation: AssetLocation = AssetLocation( + VersionedMultiLocation::V1(MultiLocation::new(1, X1(Parachain(1024))))); + pub NativeAssetMetadata: AssetRegistryMetadata = AssetRegistryMetadata { + metadata: AssetStorageMetadata { + name: b"Calamari".to_vec(), + symbol: b"KMA".to_vec(), + decimals: 12, + is_frozen: false, + }, + min_balance: 1u128, + is_sufficient: true, + }; + pub const AssetManagerPalletId: PalletId = ASSET_MANAGER_PALLET_ID; +} + +/// AssetConfig implementations for this runtime +#[derive(Clone, Eq, PartialEq)] +pub struct MantaAssetConfig; +impl LocationType for MantaAssetConfig { + type Location = AssetLocation; +} +impl AssetIdType for MantaAssetConfig { + type AssetId = CalamariAssetId; +} +impl BalanceType for MantaAssetConfig { + type Balance = Balance; +} +impl AssetConfig for MantaAssetConfig { + type NativeAssetId = NativeAssetId; + type StartNonNativeAssetId = StartNonNativeAssetId; + type NativeAssetLocation = NativeAssetLocation; + type NativeAssetMetadata = NativeAssetMetadata; + type AssetRegistry = MantaAssetRegistry; + type FungibleLedger = NativeAndNonNative; +} + +impl pallet_asset_manager::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AssetId = CalamariAssetId; + type Location = AssetLocation; + type AssetConfig = MantaAssetConfig; + type ModifierOrigin = EnsureRoot; + type SuspenderOrigin = EnsureRoot; + type PalletId = AssetManagerPalletId; + type WeightInfo = (); + type PermissionlessStartId = ConstU128<100>; + type TokenNameMaxLen = ConstU32<100>; + type TokenSymbolMaxLen = ConstU32<100>; + type PermissionlessAssetRegistryCost = ConstU128<1000>; +} + +parameter_types! { + pub const FarmingKeeperPalletId: PalletId = PalletId(*b"bf/fmkpr"); + pub const FarmingRewardIssuerPalletId: PalletId = PalletId(*b"bf/fmrir"); + pub const TreasuryAccount: AccountId = TREASURY_ACCOUNT; +} + +ord_parameter_types! { + pub const Alice: AccountId = ALICE; +} + +type MantaCurrencies = Currencies; + +impl pallet_farming::Config for Test { + type RuntimeEvent = RuntimeEvent; + type CurrencyId = CalamariAssetId; + type MultiCurrency = MantaCurrencies; + type ControlOrigin = EitherOfDiverse, EnsureSignedBy>; + type TreasuryAccount = TreasuryAccount; + type Keeper = FarmingKeeperPalletId; + type RewardIssuer = FarmingRewardIssuerPalletId; + type WeightInfo = (); +} + impl block_author::Config for Test {} use frame_support::PalletId; @@ -324,6 +532,7 @@ impl Config for Test { type DrawingInterval = DrawingInterval; type DrawingFreezeout = DrawingFreezeout; type UnstakeLockTime = UnstakeLockTime; + type BalanceConversion = Balance; type WeightInfo = (); } @@ -338,6 +547,8 @@ pub(crate) struct ExtBuilder { delegations: Vec<(AccountId, AccountId, Balance)>, // inflation config inflation: InflationInfo, + // enable farming + with_farming: bool, } impl Default for ExtBuilder { @@ -365,6 +576,7 @@ impl Default for ExtBuilder { max: Perbill::from_percent(5), }, }, + with_farming: false, } } } @@ -381,6 +593,11 @@ impl ExtBuilder { self } + pub(crate) fn with_farming(mut self) -> Self { + self.with_farming = true; + self + } + pub(crate) fn with_candidates(mut self, collators: Vec<(AccountId, Balance)>) -> Self { self.collators = collators; self @@ -409,20 +626,141 @@ impl ExtBuilder { } .assimilate_storage(&mut t) .expect("Parachain Staking's storage can be assimilated"); + + let farming_params = if self.with_farming { + FarmingParamsOf:: { + mint_farming_token: true, + destroy_farming_token: true, + pool_id: 0, + currency_id: 8, + } + } else { + FarmingParamsOf::::default() + }; + pallet_lottery::GenesisConfig:: { min_deposit: 5_000 * KMA, min_withdraw: 5_000 * KMA, gas_reserve: 10_000 * KMA, + farming_pool_params: farming_params, } .assimilate_storage(&mut t) .expect("pallet_lottery's storage can be assimilated"); + pallet_asset_manager::GenesisConfig:: { + start_id: >::StartNonNativeAssetId::get(), + } + .assimilate_storage(&mut t) + .expect("pallet_asset_manager storage fails"); let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| System::set_block_number(1)); + + ext.execute_with(|| { + let v_manta_asset_metadata = + create_asset_metadata("vManta", "vMANTA", 12, 1u128, false, true); + let jumbo_asset_metadata = + create_asset_metadata("Jumbo", "JUMBO", 12, 1u128, false, true); + let v_manta_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new( + 1, + Junctions::Here, + ))); + let native_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new( + 0, + Junctions::Here, + ))); + // register vMANTA and JUMBO asset should work. + AssetManager::register_asset( + RuntimeOrigin::root(), + v_manta_location, + v_manta_asset_metadata, + ) + .unwrap(); + AssetManager::register_asset( + RuntimeOrigin::root(), + native_location, + jumbo_asset_metadata, + ) + .unwrap(); + + assert_ok!( + >::FungibleLedger::deposit_minting( + JUMBO_ID, + &ALICE, + INIT_JUMBO_AMOUNT, + ) + ); + + assert_ok!( + >::FungibleLedger::deposit_minting( + V_MANTA_ID, + &ALICE, + INIT_V_MANTA_AMOUNT + ) + ); + + init_jumbo_farming(); + }); + ext } } +fn init_jumbo_farming() { + let tokens_proportion = vec![(V_MANTA_ID, Perbill::from_percent(100))]; + let tokens = JUMBO; + let basic_rewards = vec![(JUMBO_ID, JUMBO)]; + + assert_ok!(Farming::create_farming_pool( + RuntimeOrigin::signed(ALICE), + tokens_proportion, + basic_rewards, + None, + 0, // min_deposit_to_start + 0, // after_block_to_start + 0, // withdraw_limit_time + 0, // claim_limit_time + 5 // withdraw_limit_count + )); + + let pool_id = 0; + let charge_rewards = vec![(JUMBO_ID, 100 * JUMBO)]; + + assert_ok!(Farming::charge( + RuntimeOrigin::signed(ALICE), + pool_id, + charge_rewards + )); + assert_ok!(Farming::deposit( + RuntimeOrigin::signed(ALICE), + pool_id, + tokens, + None + )); + + let share_info = Farming::shares_and_withdrawn_rewards(pool_id, ALICE).unwrap(); + assert_eq!(share_info.share, tokens); +} + +pub(crate) fn create_asset_metadata( + name: &str, + symbol: &str, + decimals: u8, + min_balance: u128, + is_frozen: bool, + is_sufficient: bool, +) -> AssetRegistryMetadata { + AssetRegistryMetadata { + metadata: AssetStorageMetadata { + name: name.as_bytes().to_vec(), + symbol: symbol.as_bytes().to_vec(), + decimals, + is_frozen, + }, + min_balance, + is_sufficient, + } +} + pub mod from_bench { /// copied from frame benchmarking use super::*; @@ -506,7 +844,6 @@ macro_rules! assert_last_event { #[frame_support::pallet] pub mod block_author { use super::*; - use frame_support::{pallet_prelude::*, traits::Get}; #[pallet::config] pub trait Config: frame_system::Config {} diff --git a/pallets/pallet-lottery/src/tests.rs b/pallets/pallet-lottery/src/tests.rs index 2edbdd809..80ec05164 100644 --- a/pallets/pallet-lottery/src/tests.rs +++ b/pallets/pallet-lottery/src/tests.rs @@ -17,23 +17,17 @@ use crate::{ assert_last_event, mock::{ - roll_one_block, roll_to, roll_to_round_begin, roll_to_round_end, AccountId, Balance, - Balances, ExtBuilder, Lottery, ParachainStaking, RuntimeOrigin as Origin, System, Test, + roll_one_block, roll_to, roll_to_round_begin, roll_to_round_end, AccountId, Assets, + Balance, Balances, ExtBuilder, Farming, Lottery, ParachainStaking, RuntimeOrigin as Origin, + System, Test, ALICE, BOB, CHARLIE, DAVE, EVE, INIT_JUMBO_AMOUNT, INIT_V_MANTA_AMOUNT, + JUMBO_ID, POOL_ID, V_MANTA_ID, }, - Config, Error, + Config, Error, FarmingParameters, }; use frame_support::{assert_noop, assert_ok, traits::Currency}; use frame_system::RawOrigin; -lazy_static::lazy_static! { - pub(crate) static ref ALICE: AccountId = 1; - pub(crate) static ref BOB: AccountId = 2; - pub(crate) static ref CHARLIE: AccountId =3; - pub(crate) static ref DAVE: AccountId =4; - pub(crate) static ref EVE: AccountId =5; -} - const UNIT: Balance = 1_000_000_000_000; const HIGH_BALANCE: Balance = 1_000_000_000 * UNIT; @@ -120,13 +114,13 @@ fn restarting_funded_lottery_should_work() { fn depositing_and_withdrawing_in_freezeout_should_not_work() { let balance = 300_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); assert_eq!(Lottery::sum_of_deposits(), balance); assert_eq!(Lottery::total_pot(), balance); assert_ok!(Lottery::start_lottery(RawOrigin::Root.into(),)); @@ -136,11 +130,11 @@ fn depositing_and_withdrawing_in_freezeout_should_not_work() { ); assert!(!Lottery::not_in_drawing_freezeout()); assert_noop!( - Lottery::deposit(Origin::signed(*ALICE), balance), + Lottery::deposit(Origin::signed(ALICE), balance), Error::::TooCloseToDrawing ); assert_noop!( - Lottery::request_withdraw(Origin::signed(*ALICE), balance), + Lottery::request_withdraw(Origin::signed(ALICE), balance), Error::::TooCloseToDrawing ); assert_eq!(Lottery::sum_of_deposits(), balance); @@ -152,32 +146,32 @@ fn depositing_and_withdrawing_in_freezeout_should_not_work() { fn depositing_and_withdrawing_should_work() { let balance = 500_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); assert_last_event!(crate::mock::RuntimeEvent::Lottery( crate::Event::Deposited { - account: *ALICE, + account: ALICE, amount: balance } )); - assert_eq!(Lottery::active_balance_per_user(*ALICE), balance); + assert_eq!(Lottery::active_balance_per_user(ALICE), balance); assert_eq!(Lottery::sum_of_deposits(), balance); assert_eq!(Lottery::total_pot(), balance); - assert_ok!(Lottery::request_withdraw(Origin::signed(*ALICE), balance)); + assert_ok!(Lottery::request_withdraw(Origin::signed(ALICE), balance)); assert_last_event!(crate::mock::RuntimeEvent::Lottery( crate::Event::ScheduledWithdraw { - account: *ALICE, + account: ALICE, amount: balance } )); assert_eq!(Lottery::sum_of_deposits(), balance); - assert_eq!(Lottery::active_balance_per_user(*ALICE), 0); + assert_eq!(Lottery::active_balance_per_user(ALICE), 0); assert_eq!(Lottery::total_pot(), 0); assert_eq!(Lottery::withdrawal_request_queue().len(), 1); assert_eq!(Lottery::surplus_unstaking_balance(), 0); @@ -189,54 +183,54 @@ fn depositing_and_withdrawing_partial_in_one_block_should_work() { let balance = 500_000_000 * UNIT; let half_balance = 250_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); - assert_eq!(Lottery::active_balance_per_user(*ALICE), balance); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + assert_eq!(Lottery::active_balance_per_user(ALICE), balance); assert_eq!(Lottery::sum_of_deposits(), balance); assert_eq!(Lottery::total_pot(), balance); - assert_eq!(Lottery::staked_collators(*BOB), balance); + assert_eq!(Lottery::staked_collators(BOB), balance); assert_eq!(crate::UnstakingCollators::::get().len(), 0); assert_ok!(Lottery::request_withdraw( - Origin::signed(*ALICE), + Origin::signed(ALICE), half_balance )); assert_eq!(Lottery::sum_of_deposits(), balance); - assert_eq!(Lottery::active_balance_per_user(*ALICE), half_balance); + assert_eq!(Lottery::active_balance_per_user(ALICE), half_balance); assert_eq!(Lottery::total_pot(), half_balance); assert_eq!(Lottery::withdrawal_request_queue().len(), 1); assert_eq!(Lottery::surplus_unstaking_balance(), half_balance); assert_eq!(crate::UnstakingCollators::::get().len(), 1); - assert_eq!(Lottery::staked_collators(*BOB), balance); + assert_eq!(Lottery::staked_collators(BOB), balance); assert_ok!(Lottery::request_withdraw( - Origin::signed(*ALICE), + Origin::signed(ALICE), half_balance )); assert_eq!(Lottery::sum_of_deposits(), balance); - assert_eq!(Lottery::active_balance_per_user(*ALICE), 0); + assert_eq!(Lottery::active_balance_per_user(ALICE), 0); assert_eq!(Lottery::total_pot(), 0); assert_eq!(Lottery::withdrawal_request_queue().len(), 2); assert_eq!(Lottery::surplus_unstaking_balance(), 0); - assert_eq!(Lottery::staked_collators(*BOB), balance); + assert_eq!(Lottery::staked_collators(BOB), balance); assert_eq!(crate::UnstakingCollators::::get().len(), 1); assert_noop!( - Lottery::request_withdraw(Origin::signed(*ALICE), half_balance), + Lottery::request_withdraw(Origin::signed(ALICE), half_balance), Error::::NoDepositForAccount ); assert_noop!( - Lottery::deposit(Origin::signed(*ALICE), half_balance), + Lottery::deposit(Origin::signed(ALICE), half_balance), Error::::NoCollatorForDeposit ); assert_noop!( - Lottery::deposit(Origin::signed(*BOB), half_balance), + Lottery::deposit(Origin::signed(BOB), half_balance), Error::::NoCollatorForDeposit ); }); @@ -246,25 +240,25 @@ fn depositing_and_withdrawing_partial_in_one_block_should_work() { fn processing_withdrawing_leaves_correct_balance_with_user() { let balance = 500_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - let alice_starting_balance = Balances::free_balance(*ALICE); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); + let alice_starting_balance = Balances::free_balance(ALICE); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); assert_eq!( - Balances::free_balance(*ALICE), + Balances::free_balance(ALICE), alice_starting_balance - balance ); assert_eq!(Lottery::sum_of_deposits(), balance); assert_eq!(Lottery::total_pot(), balance); - assert_ok!(Lottery::request_withdraw(Origin::signed(*ALICE), balance)); + assert_ok!(Lottery::request_withdraw(Origin::signed(ALICE), balance)); assert_eq!(Lottery::sum_of_deposits(), balance); assert_eq!(Lottery::total_pot(), 0); - let alice_balance_after_request = Balances::free_balance(*ALICE); + let alice_balance_after_request = Balances::free_balance(ALICE); assert_eq!( alice_balance_after_request, alice_starting_balance - balance @@ -275,10 +269,10 @@ fn processing_withdrawing_leaves_correct_balance_with_user() { assert_ok!(Lottery::process_matured_withdrawals(RawOrigin::Root.into())); assert_eq!(Lottery::sum_of_deposits(), 0); assert_eq!( - Balances::free_balance(*ALICE), + Balances::free_balance(ALICE), alice_balance_after_request + balance ); - assert_eq!(Balances::free_balance(*ALICE), alice_starting_balance); + assert_eq!(Balances::free_balance(ALICE), alice_starting_balance); assert_eq!(Lottery::withdrawal_request_queue().len(), 0); assert_eq!(Lottery::unlocked_unstaking_funds(), 0); }); @@ -289,16 +283,16 @@ fn multiple_request_withdraw_processing_withdrawing_leaves_correct_balance_with_ let balance = 500_000_000 * UNIT; let one = 100_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - let alice_starting_balance = Balances::free_balance(*ALICE); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); + let alice_starting_balance = Balances::free_balance(ALICE); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); assert_eq!( - Balances::free_balance(*ALICE), + Balances::free_balance(ALICE), alice_starting_balance - balance ); assert_eq!(Lottery::sum_of_deposits(), balance); @@ -306,13 +300,13 @@ fn multiple_request_withdraw_processing_withdrawing_leaves_correct_balance_with_ // request withdraw 5 times for i in 1..6 { - assert_ok!(Lottery::request_withdraw(Origin::signed(*ALICE), one)); + assert_ok!(Lottery::request_withdraw(Origin::signed(ALICE), one)); assert_eq!(Lottery::total_pot(), balance - i * one); } assert_eq!(Lottery::sum_of_deposits(), balance); assert_eq!(Lottery::total_pot(), 0); - let alice_balance_after_request = Balances::free_balance(*ALICE); + let alice_balance_after_request = Balances::free_balance(ALICE); assert_eq!( alice_balance_after_request, alice_starting_balance - balance @@ -323,10 +317,10 @@ fn multiple_request_withdraw_processing_withdrawing_leaves_correct_balance_with_ assert_ok!(Lottery::process_matured_withdrawals(RawOrigin::Root.into())); assert_eq!(Lottery::sum_of_deposits(), 0); assert_eq!( - Balances::free_balance(*ALICE), + Balances::free_balance(ALICE), alice_balance_after_request + balance ); - assert_eq!(Balances::free_balance(*ALICE), alice_starting_balance); + assert_eq!(Balances::free_balance(ALICE), alice_starting_balance); assert_eq!(Lottery::withdrawal_request_queue().len(), 0); assert_eq!(Lottery::unlocked_unstaking_funds(), 0); }); @@ -336,24 +330,24 @@ fn multiple_request_withdraw_processing_withdrawing_leaves_correct_balance_with_ fn double_processing_withdrawals_does_not_double_pay() { let balance = 500_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); - assert_ok!(Lottery::request_withdraw(Origin::signed(*ALICE), balance)); - let alice_balance_after_request = Balances::free_balance(*ALICE); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + assert_ok!(Lottery::request_withdraw(Origin::signed(ALICE), balance)); + let alice_balance_after_request = Balances::free_balance(ALICE); roll_to_round_begin(3); assert_ok!(Lottery::process_matured_withdrawals(RawOrigin::Root.into())); assert_eq!( - Balances::free_balance(*ALICE), + Balances::free_balance(ALICE), alice_balance_after_request + balance ); assert_ok!(Lottery::process_matured_withdrawals(RawOrigin::Root.into())); assert_eq!( - Balances::free_balance(*ALICE), + Balances::free_balance(ALICE), alice_balance_after_request + balance ); }); @@ -366,63 +360,59 @@ fn deposit_staking_to_one_underallocated_collator_works() { let balance6 = 60_000_000 * UNIT; ExtBuilder::default() .with_balances(vec![ - (*ALICE, HIGH_BALANCE), - (*BOB, HIGH_BALANCE), - (*CHARLIE, HIGH_BALANCE), + (ALICE, HIGH_BALANCE), + (BOB, HIGH_BALANCE), + (CHARLIE, HIGH_BALANCE), ]) .with_candidates(vec![ - (*ALICE, balance4), - (*BOB, balance5), - (*CHARLIE, balance6), + (ALICE, balance4), + (BOB, balance5), + (CHARLIE, balance6), ]) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance6 + balance5 + balance4); assert_eq!( - ParachainStaking::candidate_info(*ALICE) + ParachainStaking::candidate_info(ALICE) .unwrap() .total_counted, balance4 ); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance6)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance6)); // Median = 50k, ALICE is the only underallocated collator, gets all token assert_eq!( - ParachainStaking::candidate_info(*ALICE) + ParachainStaking::candidate_info(ALICE) .unwrap() .total_counted, balance4 + balance6 ); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance5)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance5)); // Median = 60k, BOB is the only underallocated, gets all token assert_eq!( - ParachainStaking::candidate_info(*BOB) - .unwrap() - .total_counted, + ParachainStaking::candidate_info(BOB).unwrap().total_counted, balance5 + balance5 ); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance4)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance4)); // Median = 100k CHARLIE is the only underallocated, gets all token assert_eq!( - ParachainStaking::candidate_info(*CHARLIE) + ParachainStaking::candidate_info(CHARLIE) .unwrap() .total_counted, balance6 + balance4 ); // Now all 3 tie at 100k, there is no underallocation, deposit is given randomly - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance6)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance6)); assert!( - (ParachainStaking::candidate_info(*ALICE) + (ParachainStaking::candidate_info(ALICE) .unwrap() .total_counted == balance4 + balance6 + balance6) - || (ParachainStaking::candidate_info(*BOB) - .unwrap() - .total_counted + || (ParachainStaking::candidate_info(BOB).unwrap().total_counted == balance5 + balance5 + balance6) - || (ParachainStaking::candidate_info(*CHARLIE) + || (ParachainStaking::candidate_info(CHARLIE) .unwrap() .total_counted == balance6 + balance4 + balance6), @@ -434,42 +424,40 @@ fn deposit_staking_to_one_underallocated_collator_works() { fn unstaking_works_with_zero_collators_left() { let balance = 50_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*ALICE, balance), (*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(ALICE, balance), (BOB, balance)]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); assert_eq!( - ParachainStaking::candidate_info(*ALICE) + ParachainStaking::candidate_info(ALICE) .unwrap() .total_counted, balance ); assert_eq!(crate::StakedCollators::::iter().count(), 0); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); assert_eq!(crate::StakedCollators::::iter().count(), 2); - assert_eq!(Balances::free_balance(*ALICE), HIGH_BALANCE - 2 * balance); + assert_eq!(Balances::free_balance(ALICE), HIGH_BALANCE - 2 * balance); assert_eq!( Balances::free_balance(crate::Pallet::::account_id()), HIGH_BALANCE + 2 * balance ); assert_eq!( - ParachainStaking::candidate_info(*ALICE) + ParachainStaking::candidate_info(ALICE) .unwrap() .total_counted, balance * 2 ); assert_eq!( - ParachainStaking::candidate_info(*BOB) - .unwrap() - .total_counted, + ParachainStaking::candidate_info(BOB).unwrap().total_counted, balance * 2 ); assert_ok!(Lottery::request_withdraw( - Origin::signed(*ALICE), + Origin::signed(ALICE), balance * 2 )); assert_eq!(crate::StakedCollators::::iter().count(), 2); @@ -484,7 +472,7 @@ fn unstaking_works_with_zero_collators_left() { assert_eq!(Lottery::withdrawal_request_queue().len(), 0); assert_eq!(Lottery::surplus_unstaking_balance(), 0); assert_eq!(Lottery::unlocked_unstaking_funds(), 0); - assert_eq!(Balances::free_balance(*ALICE), HIGH_BALANCE); + assert_eq!(Balances::free_balance(ALICE), HIGH_BALANCE); assert_eq!( Balances::free_balance(crate::Pallet::::account_id()), HIGH_BALANCE @@ -497,10 +485,10 @@ fn winner_distribution_should_be_equality_with_equal_deposits() { let balance = 500_000_000 * UNIT; ExtBuilder::default() .with_balances(vec![ - (*ALICE, HIGH_BALANCE), - (*BOB, HIGH_BALANCE), + (ALICE, HIGH_BALANCE), + (BOB, HIGH_BALANCE), ]) - .with_candidates(vec![(*BOB, balance)]) + .with_candidates(vec![(BOB, balance)]) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); @@ -559,34 +547,34 @@ fn winner_distribution_should_be_equality_with_equal_deposits() { fn depsiting_to_one_collator_multiple_times_in_one_block_should_work() { let balance = 50_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - assert_eq!(Lottery::staked_collators(*BOB), 0); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); - assert_eq!(Lottery::staked_collators(*BOB), balance); + assert_eq!(Lottery::staked_collators(BOB), 0); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + assert_eq!(Lottery::staked_collators(BOB), balance); assert_last_event!(crate::mock::RuntimeEvent::Lottery( crate::Event::Deposited { - account: *ALICE, + account: ALICE, amount: balance } )); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); - assert_eq!(Lottery::staked_collators(*BOB), 2 * balance); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + assert_eq!(Lottery::staked_collators(BOB), 2 * balance); assert_last_event!(crate::mock::RuntimeEvent::Lottery( crate::Event::Deposited { - account: *ALICE, + account: ALICE, amount: balance } )); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); - assert_eq!(Lottery::staked_collators(*BOB), 3 * balance); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + assert_eq!(Lottery::staked_collators(BOB), 3 * balance); assert_last_event!(crate::mock::RuntimeEvent::Lottery( crate::Event::Deposited { - account: *ALICE, + account: ALICE, amount: balance } )); @@ -599,34 +587,34 @@ fn depsiting_to_two_collator_multiple_times_in_one_block_should_work() { let balance = 50_000_000 * UNIT; let balance1 = 20_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*ALICE, balance1), (*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(ALICE, balance1), (BOB, balance)]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - assert_eq!(Lottery::staked_collators(*BOB), 0); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance1)); - assert_eq!(Lottery::staked_collators(*ALICE), balance1); + assert_eq!(Lottery::staked_collators(BOB), 0); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance1)); + assert_eq!(Lottery::staked_collators(ALICE), balance1); assert_last_event!(crate::mock::RuntimeEvent::Lottery( crate::Event::Deposited { - account: *ALICE, + account: ALICE, amount: balance1 } )); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance1)); - assert_eq!(Lottery::staked_collators(*ALICE), 2 * balance1); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance1)); + assert_eq!(Lottery::staked_collators(ALICE), 2 * balance1); assert_last_event!(crate::mock::RuntimeEvent::Lottery( crate::Event::Deposited { - account: *ALICE, + account: ALICE, amount: balance1 } )); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance1)); - assert_eq!(Lottery::staked_collators(*BOB), balance1); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance1)); + assert_eq!(Lottery::staked_collators(BOB), balance1); assert_last_event!(crate::mock::RuntimeEvent::Lottery( crate::Event::Deposited { - account: *ALICE, + account: ALICE, amount: balance1 } )); @@ -638,24 +626,24 @@ fn depsiting_to_two_collator_multiple_times_in_one_block_should_work() { fn deposit_withdraw_deposit_to_new_joined_collator_works() { let balance = 50_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - assert_eq!(Lottery::staked_collators(*BOB), 0); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); - assert_eq!(Lottery::staked_collators(*BOB), balance); + assert_eq!(Lottery::staked_collators(BOB), 0); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + assert_eq!(Lottery::staked_collators(BOB), balance); - assert_ok!(Lottery::request_withdraw(Origin::signed(*ALICE), balance)); - assert_eq!(Lottery::staked_collators(*BOB), balance); + assert_ok!(Lottery::request_withdraw(Origin::signed(ALICE), balance)); + assert_eq!(Lottery::staked_collators(BOB), balance); assert_eq!(Lottery::withdrawal_request_queue().len(), 1); assert_eq!(ParachainStaking::selected_candidates().len(), 1); // join a new collator because BOB is now ineligible to receive deposits assert_noop!( - Lottery::deposit(Origin::signed(*ALICE), balance), + Lottery::deposit(Origin::signed(ALICE), balance), Error::::NoCollatorForDeposit ); let (new_collator, _) = crate::mock::from_bench::create_funded_user::( @@ -675,7 +663,7 @@ fn deposit_withdraw_deposit_to_new_joined_collator_works() { assert_eq!(ParachainStaking::selected_candidates()[1], new_collator); // pretend the collator got some rewards pallet_parachain_staking::AwardedPts::::insert(1, new_collator, 20); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); assert_eq!(Lottery::staked_collators(new_collator), balance); }); } @@ -686,21 +674,21 @@ fn deposit_withdraw_partial_draw_lottery_works() { let half_balance = 250_000_000 * UNIT; let quarter_balance = 125_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) .with_funded_lottery_account(balance) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - assert_eq!(0, Lottery::staked_collators(*BOB)); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); - let alice_post_deposit_balance = Balances::free_balance(*ALICE); - assert_eq!(balance, Lottery::staked_collators(*BOB)); + assert_eq!(0, Lottery::staked_collators(BOB)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + let alice_post_deposit_balance = Balances::free_balance(ALICE); + assert_eq!(balance, Lottery::staked_collators(BOB)); assert_eq!(balance, Lottery::total_pot()); assert_eq!(balance, Lottery::sum_of_deposits()); assert_ok!(Lottery::request_withdraw( - Origin::signed(*ALICE), + Origin::signed(ALICE), half_balance )); // surplus = balance - half_balance = half_balance @@ -708,20 +696,20 @@ fn deposit_withdraw_partial_draw_lottery_works() { roll_one_block(); assert_ok!(Lottery::request_withdraw( - Origin::signed(*ALICE), + Origin::signed(ALICE), quarter_balance )); - assert_eq!(balance, Lottery::staked_collators(*BOB)); + assert_eq!(balance, Lottery::staked_collators(BOB)); // surplus = half_balance - quarter_balance = quarter_balance assert_eq!(quarter_balance, Lottery::surplus_unstaking_balance()); - pallet_parachain_staking::AwardedPts::::insert(2, *BOB, 20); + pallet_parachain_staking::AwardedPts::::insert(2, BOB, 20); roll_to_round_begin(3); // funds should be unlocked now and BOB is finished unstaking, so it's eligible for redepositing assert_ok!(Lottery::draw_lottery(RawOrigin::Root.into())); assert_eq!( alice_post_deposit_balance + half_balance + quarter_balance, - Balances::free_balance(*ALICE) + Balances::free_balance(ALICE) ); assert_eq!(0, Lottery::surplus_unstaking_balance()); assert_eq!(0, Lottery::unlocked_unstaking_funds()); @@ -729,7 +717,7 @@ fn deposit_withdraw_partial_draw_lottery_works() { assert!(crate::UnstakingCollators::::get().is_empty()); // draw lottery rebalance will restake surplus funds to collators. assert_eq!(crate::StakedCollators::::iter().count(), 1); - assert_eq!(quarter_balance, Lottery::staked_collators(*BOB)); + assert_eq!(quarter_balance, Lottery::staked_collators(BOB)); assert_eq!(quarter_balance, Lottery::total_pot()); assert_eq!(quarter_balance, Lottery::sum_of_deposits()); }); @@ -741,8 +729,8 @@ fn multiround_withdraw_partial_deposit_works() { let half_balance = 250_000_000 * UNIT; let quarter_balance = 125_000_000 * UNIT; ExtBuilder::default() - .with_balances(vec![(*ALICE, HIGH_BALANCE), (*BOB, HIGH_BALANCE)]) - .with_candidates(vec![(*BOB, balance)]) + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) .with_funded_lottery_account(balance) .build() .execute_with(|| { @@ -751,38 +739,38 @@ fn multiround_withdraw_partial_deposit_works() { assert!( ::LeaveCandidatesDelay::get() == 1u32 ); - assert_eq!(0, Lottery::staked_collators(*BOB)); - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); - let alice_post_deposit_balance = Balances::free_balance(*ALICE); - assert_eq!(balance, Lottery::staked_collators(*BOB)); + assert_eq!(0, Lottery::staked_collators(BOB)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + let alice_post_deposit_balance = Balances::free_balance(ALICE); + assert_eq!(balance, Lottery::staked_collators(BOB)); assert_eq!(balance, Lottery::total_pot()); assert_eq!(balance, Lottery::sum_of_deposits()); roll_one_block(); // ensure this unlocks *after* round 3 start assert_ok!(Lottery::request_withdraw( - Origin::signed(*ALICE), + Origin::signed(ALICE), half_balance )); - assert_eq!(balance, Lottery::staked_collators(*BOB)); + assert_eq!(balance, Lottery::staked_collators(BOB)); assert_eq!(1, Lottery::withdrawal_request_queue().len()); assert_eq!(half_balance, Lottery::surplus_unstaking_balance()); // withdrawing funds are still locked roll_to_round_begin(2); roll_one_block(); // ensure this unlocks *after* round 3 start - pallet_parachain_staking::AwardedPts::::insert(1, *BOB, 20); + pallet_parachain_staking::AwardedPts::::insert(1, BOB, 20); assert_ok!(Lottery::request_withdraw( - Origin::signed(*ALICE), + Origin::signed(ALICE), quarter_balance )); - assert_eq!(balance, Lottery::staked_collators(*BOB)); + assert_eq!(balance, Lottery::staked_collators(BOB)); assert_eq!(2, Lottery::withdrawal_request_queue().len()); assert_eq!( half_balance - quarter_balance, Lottery::surplus_unstaking_balance() ); assert_ok!(Lottery::draw_lottery(RawOrigin::Root.into())); - assert_eq!(balance, Lottery::staked_collators(*BOB)); + assert_eq!(balance, Lottery::staked_collators(BOB)); assert_eq!( half_balance - quarter_balance, Lottery::surplus_unstaking_balance() @@ -792,28 +780,28 @@ fn multiround_withdraw_partial_deposit_works() { // collator becomes unstaked on draw_lottery, must keep quarter for withdrawal, can restake other quarter roll_to_round_begin(3); - pallet_parachain_staking::AwardedPts::::insert(2, *BOB, 20); + pallet_parachain_staking::AwardedPts::::insert(2, BOB, 20); assert_ok!(Lottery::draw_lottery(RawOrigin::Root.into())); // balance - half - quarter - assert_eq!(quarter_balance, Lottery::staked_collators(*BOB)); + assert_eq!(quarter_balance, Lottery::staked_collators(BOB)); assert_eq!(quarter_balance, Lottery::unlocked_unstaking_funds()); assert_eq!(0, Lottery::surplus_unstaking_balance()); assert_eq!( alice_post_deposit_balance + half_balance, - Balances::free_balance(*ALICE) + Balances::free_balance(ALICE) ); assert_eq!(1, Lottery::withdrawal_request_queue().len()); assert!(crate::UnstakingCollators::::get().is_empty()); roll_to_round_begin(4); - pallet_parachain_staking::AwardedPts::::insert(3, *BOB, 20); + pallet_parachain_staking::AwardedPts::::insert(3, BOB, 20); // second withdrawal can be paid out at new round assert_ok!(Lottery::draw_lottery(RawOrigin::Root.into())); assert_eq!( alice_post_deposit_balance + half_balance + quarter_balance, - Balances::free_balance(*ALICE) + Balances::free_balance(ALICE) ); - assert_eq!(quarter_balance, Lottery::staked_collators(*BOB)); + assert_eq!(quarter_balance, Lottery::staked_collators(BOB)); assert_eq!(0, Lottery::surplus_unstaking_balance()); assert_eq!(0, Lottery::unlocked_unstaking_funds()); assert!(Lottery::withdrawal_request_queue().is_empty()); @@ -827,19 +815,19 @@ fn multiround_withdraw_partial_with_two_collators_works() { let quarter_balance = 125_000_000 * UNIT; ExtBuilder::default() .with_balances(vec![ - (*ALICE, HIGH_BALANCE), - (*BOB, HIGH_BALANCE), - (*CHARLIE, HIGH_BALANCE), + (ALICE, HIGH_BALANCE), + (BOB, HIGH_BALANCE), + (CHARLIE, HIGH_BALANCE), ]) - .with_candidates(vec![(*BOB, balance), (*CHARLIE, balance)]) + .with_candidates(vec![(BOB, balance), (CHARLIE, balance)]) .with_funded_lottery_account(reserve) // minimally fund lottery .build() .execute_with(|| { assert_eq!(reserve, Lottery::gas_reserve()); // XXX: Cant use getter in the ExtBuilder - assert_ok!(Lottery::deposit(Origin::signed(*ALICE), balance)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); assert_eq!(crate::StakedCollators::::iter().count(), 1); assert_ok!(Lottery::request_withdraw( - Origin::signed(*ALICE), + Origin::signed(ALICE), quarter_balance )); @@ -847,11 +835,11 @@ fn multiround_withdraw_partial_with_two_collators_works() { assert_ok!(Lottery::draw_lottery(RawOrigin::Root.into())); roll_one_block(); assert_ok!(Lottery::request_withdraw( - Origin::signed(*ALICE), + Origin::signed(ALICE), quarter_balance )); assert_ok!(Lottery::request_withdraw( - Origin::signed(*ALICE), + Origin::signed(ALICE), quarter_balance )); assert_eq!(3, Lottery::withdrawal_request_queue().len()); @@ -859,16 +847,16 @@ fn multiround_withdraw_partial_with_two_collators_works() { assert_eq!(crate::UnstakingCollators::::get().len(), 1); roll_to_round_begin(3); - pallet_parachain_staking::AwardedPts::::insert(3, *BOB, 20); - pallet_parachain_staking::AwardedPts::::insert(3, *CHARLIE, 20); + pallet_parachain_staking::AwardedPts::::insert(3, BOB, 20); + pallet_parachain_staking::AwardedPts::::insert(3, CHARLIE, 20); assert_ok!(Lottery::draw_lottery(RawOrigin::Root.into())); assert_eq!(2, Lottery::withdrawal_request_queue().len()); assert_eq!(crate::StakedCollators::::iter().count(), 0); assert_eq!(crate::UnstakingCollators::::get().len(), 0); roll_to_round_begin(4); - pallet_parachain_staking::AwardedPts::::insert(4, *BOB, 20); - pallet_parachain_staking::AwardedPts::::insert(4, *CHARLIE, 20); + pallet_parachain_staking::AwardedPts::::insert(4, BOB, 20); + pallet_parachain_staking::AwardedPts::::insert(4, CHARLIE, 20); assert_ok!(Lottery::draw_lottery(RawOrigin::Root.into())); assert_eq!(0, Lottery::unlocked_unstaking_funds()); assert_eq!(0, Lottery::surplus_unstaking_balance()); @@ -884,24 +872,24 @@ fn many_deposit_withdrawals_work() { let mut round_count = 2; ExtBuilder::default() .with_balances(vec![ - (*ALICE, HIGH_BALANCE), - (*BOB, HIGH_BALANCE), - (*CHARLIE, HIGH_BALANCE), - (*DAVE, HIGH_BALANCE), - (*EVE, HIGH_BALANCE), + (ALICE, HIGH_BALANCE), + (BOB, HIGH_BALANCE), + (CHARLIE, HIGH_BALANCE), + (DAVE, HIGH_BALANCE), + (EVE, HIGH_BALANCE), ]) .with_candidates(vec![ - (*ALICE, HIGH_BALANCE), - (*BOB, HIGH_BALANCE), - (*CHARLIE, HIGH_BALANCE), - (*DAVE, HIGH_BALANCE), - (*EVE, HIGH_BALANCE), + (ALICE, HIGH_BALANCE), + (BOB, HIGH_BALANCE), + (CHARLIE, HIGH_BALANCE), + (DAVE, HIGH_BALANCE), + (EVE, HIGH_BALANCE), ]) .with_funded_lottery_account(HIGH_BALANCE) .build() .execute_with(|| { assert!(HIGH_BALANCE > balance); - let all_collators = &[*ALICE, *BOB, *CHARLIE, *DAVE, *EVE]; + let all_collators = &[ALICE, BOB, CHARLIE, DAVE, EVE]; reward_collators_for_round(round_count - 1, all_collators); roll_to_round_end(1); assert_ok!(Lottery::start_lottery(RawOrigin::Root.into())); @@ -966,3 +954,190 @@ fn reward_collators_for_round(round: u32, collators: &[AccountId]) { pallet_parachain_staking::AwardedPts::::insert(round, c, 20); } } + +#[test] +fn enable_farming_works() { + ExtBuilder::default().build().execute_with(|| { + let farming_params = FarmingParameters::::get(); + assert!(!farming_params.mint_farming_token); + assert!(!farming_params.destroy_farming_token); + assert_eq!(farming_params.currency_id, 0); + assert_eq!(farming_params.pool_id, 0); + + assert_noop!( + Lottery::set_farming_params(Origin::signed(BOB), true, true, 1, 1), + sp_runtime::DispatchError::BadOrigin + ); + + let farming_params = FarmingParameters::::get(); + assert!(!farming_params.mint_farming_token); + assert!(!farming_params.destroy_farming_token); + assert_eq!(farming_params.currency_id, 0); + assert_eq!(farming_params.pool_id, 0); + + assert_ok!(Lottery::set_farming_params( + Origin::root(), + true, + true, + 1, + 1 + )); + + let farming_params = FarmingParameters::::get(); + assert!(farming_params.mint_farming_token); + assert!(farming_params.destroy_farming_token); + assert_eq!(farming_params.currency_id, 1); + assert_eq!(farming_params.pool_id, 1); + }); +} + +#[test] +fn farming_deposit_withdraw() { + let balance = 500_000_000 * UNIT; + let half_balance = 250_000_000 * UNIT; + let quarter_balance = 125_000_000 * UNIT; + ExtBuilder::default() + .with_balances(vec![(ALICE, HIGH_BALANCE), (BOB, HIGH_BALANCE)]) + .with_candidates(vec![(BOB, balance)]) + .with_funded_lottery_account(balance) + .with_farming() + .build() + .execute_with(|| { + assert_eq!(INIT_JUMBO_AMOUNT, Assets::total_supply(JUMBO_ID)); + assert_eq!(INIT_V_MANTA_AMOUNT, Assets::total_supply(V_MANTA_ID)); + + assert!(HIGH_BALANCE > balance); + assert_eq!(0, Lottery::staked_collators(BOB)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + let alice_post_deposit_balance = Balances::free_balance(ALICE); + assert_eq!(balance, Lottery::staked_collators(BOB)); + assert_eq!(balance, Lottery::total_pot()); + assert_eq!(balance, Lottery::sum_of_deposits()); + + // asset accounting is correct + assert_eq!(INIT_JUMBO_AMOUNT, Assets::total_supply(JUMBO_ID)); + assert_eq!( + balance + INIT_V_MANTA_AMOUNT, + Assets::total_supply(V_MANTA_ID) + ); + assert_eq!(0, Assets::balance(V_MANTA_ID, ALICE)); + + assert_ok!(Lottery::request_withdraw( + Origin::signed(ALICE), + half_balance + )); + // surplus = balance - half_balance = half_balance + assert_eq!(half_balance, Lottery::surplus_unstaking_balance()); + + assert_eq!( + half_balance + INIT_V_MANTA_AMOUNT, + Assets::total_supply(V_MANTA_ID) + ); + // no rewards when no time has past + assert_eq!(0, Assets::balance(JUMBO_ID, ALICE)); + assert_eq!(0, Assets::balance(JUMBO_ID, ALICE)); + + roll_one_block(); + assert_ok!(Lottery::request_withdraw( + Origin::signed(ALICE), + quarter_balance + )); + assert_eq!(balance, Lottery::staked_collators(BOB)); + // surplus = half_balance - quarter_balance = quarter_balance + assert_eq!(quarter_balance, Lottery::surplus_unstaking_balance()); + + assert_eq!( + quarter_balance + INIT_V_MANTA_AMOUNT, + Assets::total_supply(V_MANTA_ID) + ); + // no rewards when no time has past + assert_eq!(0, Assets::balance(V_MANTA_ID, ALICE)); + assert_eq!(0, Assets::balance(JUMBO_ID, ALICE)); + + pallet_parachain_staking::AwardedPts::::insert(2, BOB, 20); + roll_to_round_begin(3); + // funds should be unlocked now and BOB is finished unstaking, so it's eligible for redepositing + assert_ok!(Lottery::draw_lottery(RawOrigin::Root.into())); + assert_eq!( + alice_post_deposit_balance + half_balance + quarter_balance, + Balances::free_balance(ALICE) + ); + assert_eq!(0, Lottery::surplus_unstaking_balance()); + assert_eq!(0, Lottery::unlocked_unstaking_funds()); + assert!(Lottery::withdrawal_request_queue().is_empty()); + assert!(crate::UnstakingCollators::::get().is_empty()); + // draw lottery rebalance will restake surplus funds to collators. + assert_eq!(crate::StakedCollators::::iter().count(), 1); + assert_eq!(quarter_balance, Lottery::staked_collators(BOB)); + assert_eq!(quarter_balance, Lottery::total_pot()); + assert_eq!(quarter_balance, Lottery::sum_of_deposits()); + + assert_eq!( + quarter_balance + INIT_V_MANTA_AMOUNT, + Assets::total_supply(V_MANTA_ID) + ); + // no rewards when no time has past + assert_eq!(0, Assets::balance(V_MANTA_ID, ALICE)); + assert_eq!(0, Assets::balance(JUMBO_ID, ALICE)); + }); +} + +#[test] +fn fails_withdrawing_more_than_vmanta() { + let balance = 500_000_000 * UNIT; + let half_balance = 250_000_000 * UNIT; + ExtBuilder::default() + .with_balances(vec![ + (ALICE, HIGH_BALANCE), + (BOB, HIGH_BALANCE), + (CHARLIE, HIGH_BALANCE), + ]) + .with_candidates(vec![(BOB, balance)]) + .with_funded_lottery_account(balance) + .with_farming() + .build() + .execute_with(|| { + assert_ok!(Lottery::deposit(Origin::signed(CHARLIE), balance)); + assert_ok!(Lottery::deposit(Origin::signed(ALICE), balance)); + + assert_eq!( + (balance * 2) + INIT_V_MANTA_AMOUNT, + Assets::total_supply(V_MANTA_ID) + ); + assert_eq!(0, Assets::balance(V_MANTA_ID, CHARLIE)); + assert_ok!(Farming::withdraw( + Origin::signed(CHARLIE), + POOL_ID, + Some(half_balance) + )); + assert_ok!(Farming::withdraw_claim(Origin::signed(CHARLIE), POOL_ID)); + assert_eq!(half_balance, Assets::balance(V_MANTA_ID, CHARLIE)); + assert_eq!( + (balance * 2) + INIT_V_MANTA_AMOUNT, + Assets::total_supply(V_MANTA_ID) + ); + + assert_ok!(Assets::transfer( + Origin::signed(CHARLIE), + V_MANTA_ID, + ALICE, + half_balance + )); + assert_noop!( + Lottery::request_withdraw(Origin::signed(CHARLIE), balance), + pallet_assets::Error::::BalanceLow + ); + assert_eq!(0, Assets::balance(V_MANTA_ID, CHARLIE)); + + assert_ok!(Assets::transfer( + Origin::signed(ALICE), + V_MANTA_ID, + CHARLIE, + half_balance + )); + // will work if V_MANTA is not in farming pool but is in user account + assert_ok!(Lottery::request_withdraw(Origin::signed(CHARLIE), balance)); + // no leftover V_MANTA + assert_eq!(0, Assets::balance(V_MANTA_ID, CHARLIE)); + }); +} diff --git a/runtime/calamari/src/diff_tx_fees.rs b/runtime/calamari/src/diff_tx_fees.rs index ecfd8794d..f3ea710c7 100644 --- a/runtime/calamari/src/diff_tx_fees.rs +++ b/runtime/calamari/src/diff_tx_fees.rs @@ -2791,7 +2791,7 @@ fn calculate_all_current_extrinsic_tx_fee() -> ( { assert_eq!( crate::RuntimeCall::get_call_names("Lottery").len(), - 12, + 13, "Please update new extrinsic here." ); t.execute_with(|| { @@ -2915,6 +2915,20 @@ fn calculate_all_current_extrinsic_tx_fee() -> ( dispatch_info, call_len, )); + + let call = crate::RuntimeCall::Lottery(pallet_lottery::Call::set_farming_params { + mint_farming_token: true, + burn_farming_token: true, + pool_id: 0, + currency_id: 1, + }); + let (dispatch_info, call_len) = get_call_details(&call); + calamari_runtime_calls.push(( + "pallet_lottery", + "set_farming_params", + dispatch_info, + call_len, + )); }); } diff --git a/runtime/calamari/src/lib.rs b/runtime/calamari/src/lib.rs index e42236e68..e71cefd03 100644 --- a/runtime/calamari/src/lib.rs +++ b/runtime/calamari/src/lib.rs @@ -460,6 +460,7 @@ impl pallet_lottery::Config for Runtime { type DrawingInterval = DrawingInterval; type DrawingFreezeout = DrawingFreezeout; type UnstakeLockTime = UnstakeLockTime; + type BalanceConversion = Balance; type WeightInfo = weights::pallet_lottery::SubstrateWeight; } impl pallet_authorship::Config for Runtime { diff --git a/runtime/manta/src/diff_tx_fees.rs b/runtime/manta/src/diff_tx_fees.rs index dcf596c0a..b81926263 100644 --- a/runtime/manta/src/diff_tx_fees.rs +++ b/runtime/manta/src/diff_tx_fees.rs @@ -2778,7 +2778,7 @@ fn calculate_all_current_extrinsic_tx_fee() -> ( { assert_eq!( crate::RuntimeCall::get_call_names("Lottery").len(), - 12, + 13, "Please update new extrinsic here." ); t.execute_with(|| { @@ -2902,6 +2902,20 @@ fn calculate_all_current_extrinsic_tx_fee() -> ( dispatch_info, call_len, )); + + let call = crate::RuntimeCall::Lottery(pallet_lottery::Call::set_farming_params { + mint_farming_token: true, + burn_farming_token: true, + pool_id: 0, + currency_id: 1, + }); + let (dispatch_info, call_len) = get_call_details(&call); + calamari_runtime_calls.push(( + "pallet_lottery", + "set_farming_params", + dispatch_info, + call_len, + )); }); } diff --git a/runtime/manta/src/lib.rs b/runtime/manta/src/lib.rs index b4410d02b..3ce56d419 100644 --- a/runtime/manta/src/lib.rs +++ b/runtime/manta/src/lib.rs @@ -423,6 +423,7 @@ impl pallet_lottery::Config for Runtime { type DrawingInterval = DrawingInterval; type DrawingFreezeout = DrawingFreezeout; type UnstakeLockTime = UnstakeLockTime; + type BalanceConversion = Balance; type WeightInfo = weights::pallet_lottery::SubstrateWeight; }