diff --git a/Cargo.lock b/Cargo.lock index 9b7f09c8a0..9ac49b027b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7105,6 +7105,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-rmrk-market" +version = "0.0.1" +source = "git+https://github.com/Phala-Network/rmrk-substrate?branch=polkadot-v0.9.42#73b59c92b81bb0b495c29d30b73700fe278fc982" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-rmrk-core", + "pallet-uniques", + "parity-scale-codec", + "rmrk-traits", + "scale-info", + "serde", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" @@ -7957,6 +7976,7 @@ dependencies = [ "pallet-proxy", "pallet-recovery", "pallet-rmrk-core", + "pallet-rmrk-market", "pallet-scheduler", "pallet-session", "pallet-session-benchmarking", @@ -8022,6 +8042,7 @@ dependencies = [ "pallet-insecure-randomness-collective-flip", "pallet-preimage", "pallet-rmrk-core", + "pallet-rmrk-market", "pallet-scheduler", "pallet-timestamp", "pallet-uniques", diff --git a/pallets/phala/Cargo.toml b/pallets/phala/Cargo.toml index 0dba9e4e64..7dd974b13d 100644 --- a/pallets/phala/Cargo.toml +++ b/pallets/phala/Cargo.toml @@ -23,6 +23,7 @@ pallet-preimage = { git = "https://github.com/paritytech/substrate", branch = "p # RMRK dependencies pallet-rmrk-core = { git = "https://github.com/Phala-Network/rmrk-substrate", branch = "polkadot-v0.9.42", default-features = false } rmrk-traits = { git = "https://github.com/Phala-Network/rmrk-substrate", branch = "polkadot-v0.9.42", default-features = false } +pallet-rmrk-market = { git = "https://github.com/Phala-Network/rmrk-substrate", branch = "polkadot-v0.9.42", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } @@ -86,6 +87,7 @@ std = [ "phala-types/enable_serde", "pallet-rmrk-core/std", "rmrk-traits/std", + "pallet-rmrk-market/std", "pallet-collective/std", "pallet-insecure-randomness-collective-flip/std", "pallet-preimage/std", diff --git a/pallets/phala/src/compute/stake_pool_v2.rs b/pallets/phala/src/compute/stake_pool_v2.rs index 45150410c2..46ab24dc4b 100644 --- a/pallets/phala/src/compute/stake_pool_v2.rs +++ b/pallets/phala/src/compute/stake_pool_v2.rs @@ -14,7 +14,6 @@ pub mod pallet { use crate::computation; use crate::pool_proxy::{ensure_stake_pool, ensure_vault, PoolProxy, StakePool}; use crate::registry; - use crate::stake_pool; use crate::vault; use crate::wrapped_balances; @@ -25,10 +24,7 @@ pub mod pallet { dispatch::DispatchResult, pallet_prelude::*, traits::{ - tokens::{ - fungibles::{Inspect, Mutate}, - Preservation, - }, + tokens::{fungibles::Mutate, Preservation}, StorageVersion, UnixTime, }, }; @@ -51,11 +47,11 @@ pub mod pallet { + registry::Config + computation::Config + pallet_rmrk_core::Config + + pallet_rmrk_market::Config + base_pool::Config + pallet_assets::Config + pallet_democracy::Config + wrapped_balances::Config - + stake_pool::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -97,14 +93,6 @@ pub mod pallet { pub type SubAccountPreimages = StorageMap<_, Twox64Concat, T::AccountId, (u64, WorkerPublicKey)>; - #[pallet::type_value] - pub fn StakepoolIterateStartPosByDefault() -> Option { - None - } - #[pallet::storage] - pub type StakepoolIterateStartPos = - StorageValue<_, Option, ValueQuery, StakepoolIterateStartPosByDefault>; - #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -301,6 +289,8 @@ pub mod pallet { LockAccountStakeError, NoLegacyRewardToClaim, + /// The pool's delegation nft is on sell. + UserNftListed, } #[pallet::call] @@ -316,7 +306,7 @@ pub mod pallet { #[pallet::weight({0})] #[frame_support::transactional] pub fn create(origin: OriginFor) -> DispatchResult { - let owner = ensure_signed(origin)?; + let owner = ensure_signed(origin.clone())?; let pid = base_pool::Pallet::::consume_new_pid(); let collection_id: CollectionId = base_pool::Pallet::::consume_new_cid(); // Create a NFT collection related to the new stake pool @@ -333,6 +323,10 @@ pub mod pallet { None, symbol, )?; + pallet_uniques::Pallet::::thaw_collection( + Origin::::Signed(base_pool::pallet_id::()).into(), + collection_id, + )?; let account_id = base_pool::pallet::generate_staker_account::(pid, owner.clone()); let (owner_reward_account, lock_account) = @@ -556,28 +550,13 @@ pub mod pallet { Ok(()) } - #[pallet::call_index(6)] - #[pallet::weight({0})] - pub fn backfill_add_missing_reward( - origin: OriginFor, - input: Vec<(T::AccountId, u64, BalanceOf)>, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - base_pool::Pallet::::ensure_migration_root(who)?; - - for (account_id, pid, balance) in input.iter() { - LegacyRewards::::insert((account_id.clone(), *pid), *balance); - } - Ok(()) - } - /// Claims pool-owner's pending rewards of the sender and send to the `target` /// /// The rewards associate to sender's "staker role" will not be claimed /// /// Requires: /// 1. The sender is a pool owner - #[pallet::call_index(7)] + #[pallet::call_index(6)] #[pallet::weight({0})] pub fn claim_owner_rewards( origin: OriginFor, @@ -614,7 +593,7 @@ pub mod pallet { /// If the shutdown condition is met, all workers in the pool will be forced shutdown. /// Note: This function doesn't guarantee no-op when there's error. /// TODO(mingxuan): add more detail comment later. - #[pallet::call_index(8)] + #[pallet::call_index(7)] #[pallet::weight({0})] #[frame_support::transactional] pub fn check_and_maybe_force_withdraw(origin: OriginFor, pid: u64) -> DispatchResult { @@ -664,7 +643,7 @@ pub mod pallet { /// Requires: /// 1. The pool exists /// 2. After the deposit, the pool doesn't reach the cap - #[pallet::call_index(9)] + #[pallet::call_index(8)] #[pallet::weight({0})] #[frame_support::transactional] pub fn contribute( @@ -689,6 +668,13 @@ pub mod pallet { maybe_vault = Some((vault_pid, vault_info)); } let mut pool_info = ensure_stake_pool::(pid)?; + ensure!( + !wrapped_balances::pallet::Pallet::::have_nft_on_list( + &who, + &pool_info.basepool.cid + ), + Error::::UserNftListed + ); let a = amount; // Alias to reduce confusion in the code below // If the pool has a contribution whitelist in storages, check if the origin is authorized to contribute ensure!( @@ -761,7 +747,7 @@ pub mod pallet { /// Once a withdraw request is proceeded successfully, The withdrawal would be queued and waiting to be dealed. /// Afer the withdrawal is queued, The withdraw queue will be automaticly consumed util there are not enough free stakes to fullfill withdrawals. /// Everytime the free stakes in the pools increases (except for rewards distributing), the withdraw queue will be consumed as it describes above. - #[pallet::call_index(10)] + #[pallet::call_index(9)] #[pallet::weight({0})] #[frame_support::transactional] pub fn withdraw( @@ -784,6 +770,13 @@ pub mod pallet { who = vault_info.basepool.pool_account_id; } let mut pool_info = ensure_stake_pool::(pid)?; + ensure!( + !wrapped_balances::pallet::Pallet::::have_nft_on_list( + &who, + &pool_info.basepool.cid + ), + Error::::UserNftListed + ); let maybe_nft_id = base_pool::Pallet::::merge_nft_for_staker( pool_info.basepool.cid, who.clone(), @@ -835,81 +828,12 @@ pub mod pallet { Ok(()) } - #[pallet::call_index(11)] - #[pallet::weight({0})] - #[frame_support::transactional] - pub fn reset_iter_pos(origin: OriginFor) -> DispatchResult { - let who = ensure_signed(origin)?; - base_pool::Pallet::::ensure_migration_root(who)?; - StakepoolIterateStartPos::::put(None::); - Ok(()) - } - - #[pallet::call_index(12)] - #[pallet::weight({0})] - #[frame_support::transactional] - pub fn fix_missing_worker_lock( - origin: OriginFor, - max_iterations: u32, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - base_pool::Pallet::::ensure_migration_root(who)?; - let mut last_pid = StakepoolIterateStartPos::::get(); - let mut iter = match last_pid { - Some(pid) => { - let key: Vec = base_pool::pallet::Pools::::hashed_key_for(pid); - base_pool::pallet::Pools::::iter_from(key) - } - None => base_pool::pallet::Pools::::iter(), - }; - let asset_id = ::WPhaAssetId::get(); - let mut i = 0; - for (pid, pool_proxy) in iter.by_ref() { - match pool_proxy { - PoolProxy::StakePool(pool_info) => { - let mut total_lock = Zero::zero(); - pool_info.workers.into_iter().for_each(|pubkey| { - let session: T::AccountId = pool_sub_account(pid, &pubkey); - total_lock += - computation::Stakes::::get(&session).unwrap_or_default(); - }); - pool_info.cd_workers.into_iter().for_each(|pubkey| { - let session: T::AccountId = pool_sub_account(pid, &pubkey); - total_lock += - computation::Stakes::::get(&session).unwrap_or_default(); - }); - let curr_lock: BalanceOf = - as Inspect>::balance( - asset_id, - &pool_info.lock_account, - ); - ensure!(curr_lock <= total_lock, Error::::LockAccountStakeError); - if curr_lock < total_lock { - wrapped_balances::Pallet::::mint_into( - &pool_info.lock_account, - total_lock - curr_lock, - )?; - } - } - PoolProxy::Vault(_) => (), - } - i += 1; - last_pid = Some(pid); - if i >= max_iterations { - break; - } - } - StakepoolIterateStartPos::::put(last_pid); - - Ok(()) - } - /// Starts a worker on behalf of the stake pool /// /// Requires: /// 1. The worker is bound to the pool and is in Ready state /// 2. The remaining stake in the pool can cover the minimal stake required - #[pallet::call_index(13)] + #[pallet::call_index(10)] #[pallet::weight({0})] pub fn start_computing( origin: OriginFor, @@ -926,7 +850,7 @@ pub mod pallet { /// /// Requires: /// 1. There worker is bound to the pool and is in a stoppable state - #[pallet::call_index(14)] + #[pallet::call_index(11)] #[pallet::weight({0})] pub fn stop_computing( origin: OriginFor, @@ -938,7 +862,7 @@ pub mod pallet { } /// Reclaims the releasing stake of a worker in a pool. - #[pallet::call_index(15)] + #[pallet::call_index(12)] #[pallet::weight({0})] pub fn reclaim_pool_worker( origin: OriginFor, @@ -952,7 +876,7 @@ pub mod pallet { } /// Restarts the worker with a higher stake - #[pallet::call_index(17)] + #[pallet::call_index(13)] #[pallet::weight(Weight::from_parts(195_000_000, 0))] #[frame_support::transactional] pub fn restart_computing( @@ -1195,7 +1119,7 @@ pub mod pallet { worker: info.pubkey, amount: reward, }); - return; + continue; } }; let mut pool_info = diff --git a/pallets/phala/src/compute/vault.rs b/pallets/phala/src/compute/vault.rs index 9d5db6753d..52bd4ed42f 100644 --- a/pallets/phala/src/compute/vault.rs +++ b/pallets/phala/src/compute/vault.rs @@ -35,6 +35,7 @@ pub mod pallet { + registry::Config + computation::Config + pallet_rmrk_core::Config + + pallet_rmrk_market::Config + base_pool::Config + pallet_assets::Config + pallet_democracy::Config @@ -139,6 +140,8 @@ pub mod pallet { VaultBankrupt, /// The caller has no nft to withdraw NoNftToWithdraw, + /// The pool's delegation nft is on sell. + UserNftListed, } #[pallet::call] @@ -247,6 +250,13 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin.clone())?; let mut pool_info = ensure_vault::(vault_pid)?; + ensure!( + !wrapped_balances::pallet::Pallet::::have_nft_on_list( + &who, + &pool_info.basepool.cid + ), + Error::::UserNftListed + ); ensure!( who == pool_info.basepool.owner, Error::::UnauthorizedPoolOwner @@ -447,6 +457,13 @@ pub mod pallet { pub fn contribute(origin: OriginFor, pid: u64, amount: BalanceOf) -> DispatchResult { let who = ensure_signed(origin)?; let mut pool_info = ensure_vault::(pid)?; + ensure!( + !wrapped_balances::pallet::Pallet::::have_nft_on_list( + &who, + &pool_info.basepool.cid + ), + Error::::UserNftListed + ); let a = amount; // Alias to reduce confusion in the code below ensure!( @@ -501,6 +518,13 @@ pub mod pallet { pub fn withdraw(origin: OriginFor, pid: u64, shares: BalanceOf) -> DispatchResult { let who = ensure_signed(origin)?; let mut pool_info = ensure_vault::(pid)?; + ensure!( + !wrapped_balances::pallet::Pallet::::have_nft_on_list( + &who, + &pool_info.basepool.cid + ), + Error::::UserNftListed + ); let maybe_nft_id = base_pool::Pallet::::merge_nft_for_staker( pool_info.basepool.cid, who.clone(), diff --git a/pallets/phala/src/compute/wrapped_balances.rs b/pallets/phala/src/compute/wrapped_balances.rs index 838fc57a94..301ab7ec2c 100644 --- a/pallets/phala/src/compute/wrapped_balances.rs +++ b/pallets/phala/src/compute/wrapped_balances.rs @@ -9,6 +9,7 @@ pub mod pallet { use crate::registry; use crate::vault; use crate::{BalanceOf, NegativeImbalanceOf, PhalaConfig}; + use frame_support::traits::tokens::{Fortitude, Precision}; use frame_support::{ pallet_prelude::*, traits::{ @@ -19,7 +20,6 @@ pub mod pallet { OnUnbalanced, StorageVersion, }, }; - use frame_support::traits::tokens::{Fortitude, Precision}; use frame_system::{pallet_prelude::*, RawOrigin}; use pallet_democracy::{AccountVote, ReferendumIndex, ReferendumInfo}; pub use rmrk_traits::primitives::{CollectionId, NftId}; @@ -32,6 +32,7 @@ pub mod pallet { + crate::PhalaConfig + registry::Config + pallet_rmrk_core::Config + + pallet_rmrk_market::Config + computation::Config + pallet_assets::Config + pallet_democracy::Config @@ -144,17 +145,42 @@ pub mod pallet { T: Config + vault::Config, { fn pre_check( - _sender: &T::AccountId, - _recipient: &T::AccountId, + sender: &T::AccountId, + recipient: &T::AccountId, collection_id: &CollectionId, - _nft_id: &NftId, + nft_id: &NftId, ) -> bool { - if base_pool::pallet::PoolCollections::::get(collection_id).is_some() { - // Forbid any delegation transfer before delegation nft transfer and sell is fully prepared. - // TODO(mingxuan): reopen pre_check function. - return false; - } - + if let Some(pid) = base_pool::pallet::PoolCollections::::get(collection_id) { + if pallet_rmrk_market::ListedNfts::::contains_key(collection_id, nft_id) { + return false; + } + if Self::have_nft_on_list(recipient, collection_id) { + return false; + } + if let Ok(net_value) = Pallet::::get_net_value((*sender).clone()) { + let property_guard = + base_pool::Pallet::::get_nft_attr_guard(*collection_id, *nft_id) + .expect("get nft should not fail: qed."); + let property = &property_guard.attr; + let account_status = match StakerAccounts::::get(sender) { + Some(account_status) => account_status, + None => unreachable!(), + }; + let pool_proxy = base_pool::Pallet::::pool_collection(pid) + .expect("get pool should not fail: qed."); + let basepool = &match pool_proxy { + PoolProxy::Vault(p) => p.basepool, + PoolProxy::StakePool(p) => p.basepool, + }; + if let Some(price) = basepool.share_price() { + let nft_value = bmul(property.shares, &price); + if account_status.locked + nft_value > net_value { + return false; + } + } + } + }; + pallet_rmrk_core::pallet::Lock::::remove((collection_id, nft_id)); true } fn post_transfer( @@ -170,6 +196,7 @@ pub mod pallet { pid, ) .expect("mrege or init should not fail"); + let _ = Self::maybe_subscribe_to_pool(recipient, pid, *collection_id); } true } @@ -379,9 +406,14 @@ pub mod pallet { pub fn remove_dust(who: &T::AccountId, dust: BalanceOf) { debug_assert!(dust != Zero::zero()); if dust != Zero::zero() { - let actual_removed = - pallet_assets::Pallet::::burn_from(T::WPhaAssetId::get(), who, dust, Precision::BestEffort, Fortitude::Force) - .expect("slash should success with correct amount: qed."); + let actual_removed = pallet_assets::Pallet::::burn_from( + T::WPhaAssetId::get(), + who, + dust, + Precision::BestEffort, + Fortitude::Force, + ) + .expect("slash should success with correct amount: qed."); let (imbalance, _remaining) = ::Currency::slash( &>::account_id(), dust, @@ -407,7 +439,7 @@ pub mod pallet { target, amount, Precision::BestEffort, - Fortitude::Force + Fortitude::Force, )?; Ok(()) } @@ -478,6 +510,18 @@ pub mod pallet { } } + /// Check if recipient has Nft listing in a specific collection. + pub fn have_nft_on_list(recipient: &T::AccountId, collection_id: &CollectionId) -> bool { + let iter = pallet_uniques::Pallet::::owned_in_collection(collection_id, recipient) + .take_while(|nftid| { + pallet_rmrk_market::ListedNfts::::contains_key(collection_id, nftid) + }); + if iter.count() > 0 { + return true; + } + false + } + /// Tries to update locked W-PHA amount of the user fn update_user_locked(user: T::AccountId) -> DispatchResult { let mut max_lock: BalanceOf = Zero::zero(); diff --git a/pallets/phala/src/lib.rs b/pallets/phala/src/lib.rs index cff335a980..455fb117b7 100644 --- a/pallets/phala/src/lib.rs +++ b/pallets/phala/src/lib.rs @@ -21,7 +21,6 @@ pub mod mq; pub mod phat; pub mod puppets; pub mod registry; -pub mod stake_pool; use compute::{base_pool, computation, pool_proxy, stake_pool_v2, vault, wrapped_balances}; @@ -49,7 +48,6 @@ pub use mq as pallet_mq; pub use phat as pallet_phat; pub use phat_tokenomic as pallet_phat_tokenomic; pub use registry as pallet_registry; -pub use stake_pool as pallet_stake_pool; pub mod phat_tokenomic; #[cfg(feature = "native")] diff --git a/pallets/phala/src/mock.rs b/pallets/phala/src/mock.rs index 44eb45293d..1f45354237 100644 --- a/pallets/phala/src/mock.rs +++ b/pallets/phala/src/mock.rs @@ -1,5 +1,5 @@ use crate::{ - base_pool, computation, mq, registry, stake_pool, stake_pool_v2, + base_pool, computation, mq, registry, stake_pool_v2, utils::attestation_legacy::{ Attestation, AttestationValidator, Error as AttestationError, IasFields, }, @@ -21,6 +21,7 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, + Permill, }; pub(crate) type Balance = u128; @@ -41,6 +42,7 @@ frame_support::construct_runtime!( Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, Uniques: pallet_uniques::{Pallet, Storage, Event}, RmrkCore: pallet_rmrk_core::{Pallet, Call, Event}, + RmrkMarket: pallet_rmrk_market::{Pallet, Call, Event}, Democracy: pallet_democracy::{Pallet, Call, Storage, Config, Event}, Assets: pallet_assets::{Pallet, Event}, Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, @@ -52,7 +54,6 @@ frame_support::construct_runtime!( PhalaVault: vault::{Pallet, Event}, PhalaWrappedBalances: wrapped_balances::{Pallet, Event}, PhalaBasePool: base_pool::{Pallet, Event}, - PhalaStakePool: stake_pool::{Event}, Preimage: pallet_preimage::{Event}, } ); @@ -236,6 +237,21 @@ impl pallet_rmrk_core::Config for Test { type Helper = pallet_rmrk_core::RmrkBenchmark; } +parameter_types! { + pub const MinimumOfferAmount: Balance = DOLLARS / 10_000; + pub const MarketFee: Permill = Permill::from_parts(5_000); +} + +impl pallet_rmrk_market::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ProtocolOrigin = EnsureRoot; + type Currency = Balances; + type MinimumOfferAmount = MinimumOfferAmount; + type WeightInfo = pallet_rmrk_market::weights::SubstrateWeight; + type MarketplaceHooks = (); + type MarketFee = MarketFee; +} + pub struct SetBudgetMembers; impl SortedMembers for SetBudgetMembers { @@ -407,11 +423,6 @@ impl base_pool::Config for Test { type WPhaMinBalance = WPhaMinBalance; } -impl stake_pool::Config for Test { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; -} - pub struct MockValidator; impl AttestationValidator for MockValidator { fn validate( diff --git a/pallets/phala/src/stake_pool.rs b/pallets/phala/src/stake_pool.rs deleted file mode 100644 index 6d1a130b1d..0000000000 --- a/pallets/phala/src/stake_pool.rs +++ /dev/null @@ -1,210 +0,0 @@ -//! Pool for collaboratively mining staking - -pub use self::pallet::*; - -use crate::BalanceOf; - -#[frame_support::pallet] -pub mod pallet { - use crate::compute::{base_pool, computation}; - use crate::registry; - use crate::utils::fixed_point::CodecFixedPoint; - - use super::BalanceOf; - use frame_support::{ - pallet_prelude::*, - traits::{LockableCurrency, StorageVersion}, - }; - use scale_info::TypeInfo; - use sp_runtime::Permill; - use sp_std::{collections::vec_deque::VecDeque, prelude::*}; - - use phala_types::WorkerPublicKey; - - pub struct DescMaxLen; - - impl Get for DescMaxLen { - fn get() -> u32 { - 4400 - } - } - /// The functions to manage user's native currency lock in the Balances pallet - pub trait Ledger { - /// Increases the locked amount for a user - /// - /// Unsafe: it assumes there's enough free `amount` - fn ledger_accrue(who: &AccountId, amount: Balance); - /// Decreases the locked amount for a user - /// - /// Optionally remove some dust by `Currency::slash` and move it to the Treasury. - /// Unsafe: it assumes there's enough locked `amount` - fn ledger_reduce(who: &AccountId, amount: Balance, dust: Balance); - /// Gets the locked amount of `who` - fn ledger_query(who: &AccountId) -> Balance; - } - - #[pallet::config] - pub trait Config: frame_system::Config + registry::Config + computation::Config { - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - type Currency: LockableCurrency; - } - - const STORAGE_VERSION: StorageVersion = StorageVersion::new(7); - - #[pallet::pallet] - #[pallet::storage_version(STORAGE_VERSION)] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// Mapping from pool id to PoolInfo - #[pallet::storage] - #[pallet::getter(fn stake_pools)] - pub type StakePools = - StorageMap<_, Twox64Concat, u64, PoolInfo>>; - - /// Mapping from (pid, staker) to UserStakeInfo - #[pallet::storage] - #[pallet::getter(fn pool_stakers)] - pub type PoolStakers = - StorageMap<_, Twox64Concat, (u64, T::AccountId), UserStakeInfo>>; - - /// The number of total pools - #[pallet::storage] - #[pallet::getter(fn pool_count)] - pub type PoolCount = StorageValue<_, u64, ValueQuery>; - - /// Mapping from workers to the pool they belong to - /// - /// The map entry lasts from `add_worker()` to `remove_worker()` or force unbinding. - #[pallet::storage] - pub type WorkerAssignments = StorageMap<_, Twox64Concat, WorkerPublicKey, u64>; - - /// (Deprecated) - // TODO: remove it - #[pallet::storage] - pub type SubAccountAssignments = StorageMap<_, Twox64Concat, T::AccountId, u64>; - - /// Mapping staker to it's the balance locked in all pools - #[pallet::storage] - #[pallet::getter(fn stake_ledger)] - pub type StakeLedger = StorageMap<_, Twox64Concat, T::AccountId, BalanceOf>; - - /// Mapping from the block timestamp to pools that has withdrawal requests queued in that block - #[pallet::storage] - #[pallet::getter(fn withdrawal_queued_pools)] - pub type WithdrawalQueuedPools = StorageMap<_, Twox64Concat, u64, Vec>; - - /// Queue that contains all block's timestamp, in that block contains the waiting withdraw reqeust. - /// This queue has a max size of (T::GracePeriod * 8) bytes - #[pallet::storage] - #[pallet::getter(fn withdrawal_timestamps)] - pub type WithdrawalTimestamps = StorageValue<_, VecDeque, ValueQuery>; - - /// Helper storage to track the preimage of the mining sub-accounts. Not used in consensus. - #[pallet::storage] - pub type SubAccountPreimages = - StorageMap<_, Twox64Concat, T::AccountId, (u64, WorkerPublicKey)>; - - /// Mapping for pools that specify certain stakers to contribute stakes - #[pallet::storage] - #[pallet::getter(fn pool_whitelist)] - pub type PoolContributionWhitelists = - StorageMap<_, Twox64Concat, u64, Vec>; - - /// Mapping for pools that store their descriptions set by owner - #[pallet::storage] - #[pallet::getter(fn pool_descriptions)] - pub type PoolDescriptions = - StorageMap<_, Twox64Concat, u64, base_pool::pallet::DescStr>; - - #[pallet::event] - pub enum Event {} - - #[pallet::error] - pub enum Error {} - - /// The state of a pool - #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, Default, RuntimeDebug)] - pub struct PoolInfo { - /// Pool ID - pub pid: u64, - /// The owner of the pool - pub owner: AccountId, - /// The commission the pool owner takes - /// - /// For example, 10% commission means 10% of the miner reward goes to the pool owner, and - /// the remaining 90% is distributed to the contributors. Setting to `None` means a - /// commission of 0%. - pub payout_commission: Option, - /// Claimable owner reward - /// - /// Whenver a miner gets some reward, the commission the pool taken goes to here. The owner - /// can claim their reward at any time. - pub owner_reward: Balance, - /// The hard capacity of the pool - /// - /// When it's set, the totals stake a pool can receive will not exceed this capacity. - pub cap: Option, - /// The reward [accumulator](crate::utils::accumulator) - /// - /// An individual user's reward is tracked by [`reward_acc`](PoolInfo::reward_acc), their - /// [`shares`](UserStakeInfo::shares) and the [`reward_debt`](UserStakeInfo::reward_debt). - pub reward_acc: CodecFixedPoint, - /// Total shares - /// - /// It tracks the total number of shared of all the contributors. Guaranteed to be - /// non-dust. - pub total_shares: Balance, - /// Total stake - /// - /// It tracks the total number of the stake the pool received. Guaranteed to be non-dust. - pub total_stake: Balance, - /// Total free stake - /// - /// It tracks the total free stake (not used by any miner) in the pool. Can be dust. - pub free_stake: Balance, - /// Releasing stake - /// - /// It tracks the stake that will be unlocked in the future. It's the sum of all the - /// cooling down miners' remaining stake. - pub releasing_stake: Balance, - /// Bound workers - pub workers: Vec, - /// The queue of withdraw requests - pub withdraw_queue: VecDeque>, - } - - /// A user's staking info - #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, RuntimeDebug)] - pub struct UserStakeInfo { - /// User's address - pub user: AccountId, - /// The actual locked stake in the pool - pub locked: Balance, - /// The share in the pool - /// - /// Guaranteed to be non-dust. Invariant must hold: - /// - `StakePools[pid].total_stake == sum(PoolStakers[(pid, user)].shares)` - pub shares: Balance, - /// Resolved claimable rewards - /// - /// It's accumulated by resolving "pending stake" from the reward - /// [accumulator](crate::utils::accumulator). - pub available_rewards: Balance, - /// The debt of a user's stake - /// - /// It's subject to the pool reward [accumulator](crate::utils::accumulator). - pub reward_debt: Balance, - } - - /// A withdraw request, usually stored in the withdrawal queue - #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, RuntimeDebug)] - pub struct WithdrawInfo { - /// The withdrawal requester - pub user: AccountId, - /// The shares to withdraw. Cannot be dust. - pub shares: Balance, - /// The start time of the request - pub start_time: u64, - } -} diff --git a/standalone/runtime/Cargo.toml b/standalone/runtime/Cargo.toml index f9b9117736..2bdb32b509 100644 --- a/standalone/runtime/Cargo.toml +++ b/standalone/runtime/Cargo.toml @@ -99,6 +99,7 @@ phat-offchain-rollup = { path = "../../pallets/offchain-rollup", default-feature # RMRK dependencies pallet-rmrk-core = { git = "https://github.com/Phala-Network/rmrk-substrate", branch = "polkadot-v0.9.42", default-features = false } rmrk-traits = { git = "https://github.com/Phala-Network/rmrk-substrate", branch = "polkadot-v0.9.42", default-features = false } +pallet-rmrk-market = { git = "https://github.com/Phala-Network/rmrk-substrate", branch = "polkadot-v0.9.42", default-features = false } [build-dependencies] substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", optional = true } @@ -182,6 +183,7 @@ std = [ "pallet-uniques/std", "pallet-rmrk-core/std", "rmrk-traits/std", + "pallet-rmrk-market/std", "phat-offchain-rollup/std", ] runtime-benchmarks = [ diff --git a/standalone/runtime/src/lib.rs b/standalone/runtime/src/lib.rs index efee5563de..41ccdc8adc 100644 --- a/standalone/runtime/src/lib.rs +++ b/standalone/runtime/src/lib.rs @@ -107,7 +107,7 @@ mod voter_bags; pub use phala_pallets::{ pallet_base_pool, pallet_computation, pallet_phat, pallet_phat_tokenomic, pallet_mq, - pallet_registry, pallet_stake_pool, pallet_stake_pool_v2, pallet_vault, pallet_wrapped_balances, + pallet_registry, pallet_stake_pool_v2, pallet_vault, pallet_wrapped_balances, puppets, }; use phat_offchain_rollup::{anchor as pallet_anchor, oracle as pallet_oracle}; @@ -1364,10 +1364,6 @@ impl pallet_stake_pool_v2::Config for Runtime { type GracePeriod = WorkingGracePeriod; type MaxPoolWorkers = MaxPoolWorkers; } -impl pallet_stake_pool::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; -} parameter_types! { pub const InitialPriceCheckPoint: Balance = 1 * DOLLARS; @@ -1432,6 +1428,23 @@ impl pallet_rmrk_core::Config for Runtime { #[cfg(feature = "runtime-benchmarks")] type Helper = pallet_rmrk_core::RmrkBenchmark; } + +parameter_types! { + pub const MinimumOfferAmount: Balance = DOLLARS / 10_000; + pub const MarketFee: Permill = Permill::from_parts(5_000); +} + + +impl pallet_rmrk_market::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ProtocolOrigin = EnsureRoot; + type Currency = Balances; + type MinimumOfferAmount = MinimumOfferAmount; + type WeightInfo = pallet_rmrk_market::weights::SubstrateWeight; + type MarketplaceHooks = (); + type MarketFee = MarketFee; +} + impl pallet_phat::Config for Runtime { type RuntimeEvent = RuntimeEvent; type InkCodeSizeLimit = ConstU32<{ 1024 * 1024 * 2 }>; @@ -1580,7 +1593,6 @@ construct_runtime!( PhalaRegistry: pallet_registry, PhalaComputation: pallet_computation, PhalaStakePoolv2: pallet_stake_pool_v2, - PhalaStakePool: pallet_stake_pool, PhalaVault: pallet_vault, PhalaWrappedBalances: pallet_wrapped_balances, PhalaBasePool: pallet_base_pool, @@ -1598,6 +1610,7 @@ construct_runtime!( // NFT Uniques: pallet_uniques::{Pallet, Storage, Event}, RmrkCore: pallet_rmrk_core::{Pallet, Call, Event}, + RmrkMarket: pallet_rmrk_market::{Pallet, Call, Event}, } );