1#![cfg_attr(not(feature = "std"), no_std)]
2
3pub mod agent;
4pub mod burn;
5mod ext;
6pub mod fee;
7pub mod migrations;
8pub mod namespace;
9pub mod stake;
10
11pub mod benchmarking;
12pub mod weights;
13
14pub(crate) use ext::*;
15use frame::{
16 arithmetic::Percent,
17 prelude::{ensure_root, ensure_signed},
18};
19use namespace::{NamespaceMetadata, NamespaceOwnership, NamespacePath};
20pub use pallet::*;
21use polkadot_sdk::{
22 frame_support::{
23 dispatch::DispatchResult,
24 pallet_prelude::{ValueQuery, *},
25 traits::Currency,
26 Identity,
27 },
28 frame_system::pallet_prelude::OriginFor,
29 polkadot_sdk_frame as frame, sp_std,
30};
31use scale_info::prelude::vec::Vec;
32
33use crate::{agent::Agent, burn::BurnConfiguration, fee::ValidatorFeeConstraints};
34
35#[frame::pallet]
36pub mod pallet {
37 const STORAGE_VERSION: StorageVersion = StorageVersion::new(5);
38
39 use frame::prelude::BlockNumberFor;
40 use pallet_emission0_api::Emission0Api;
41 use pallet_governance_api::GovernanceApi;
42 use pallet_permission0_api::Permission0NamespacesApi;
43 use pallet_torus0_api::NamespacePathInner;
44 use polkadot_sdk::frame_support::traits::ReservableCurrency;
45 use weights::WeightInfo;
46
47 use super::*;
48
49 #[pallet::storage]
53 pub type MaxAllowedValidators<T: Config> =
54 StorageValue<_, u16, ValueQuery, T::DefaultMaxAllowedValidators>;
55
56 #[pallet::storage]
58 pub type Burn<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
59
60 #[pallet::storage]
63 pub type RegistrationsThisInterval<T: Config> = StorageValue<_, u16, ValueQuery>;
64
65 #[pallet::storage]
67 pub type MinValidatorStake<T: Config> =
68 StorageValue<_, BalanceOf<T>, ValueQuery, T::DefaultMinValidatorStake>;
69
70 #[pallet::storage]
72 pub type RewardInterval<T: Config> = StorageValue<_, u16, ValueQuery, T::DefaultRewardInterval>;
73
74 #[pallet::storage]
76 pub type Agents<T: Config> = StorageMap<_, Identity, AccountIdOf<T>, Agent<T>>;
77
78 #[pallet::storage]
80 pub type MaxNameLength<T: Config> = StorageValue<_, u16, ValueQuery, T::DefaultMaxNameLength>;
81
82 #[pallet::storage]
84 pub type MinNameLength<T: Config> = StorageValue<_, u16, ValueQuery, T::DefaultMinNameLength>;
85
86 #[pallet::storage]
88 pub type MaxAgentUrlLength<T: Config> =
89 StorageValue<_, u16, ValueQuery, T::DefaultMaxAgentUrlLength>;
90
91 #[pallet::storage]
93 pub type RegistrationsThisBlock<T> = StorageValue<_, u16, ValueQuery>;
94
95 #[pallet::storage]
98 pub type MaxRegistrationsPerBlock<T: Config> =
99 StorageValue<_, u16, ValueQuery, T::DefaultMaxRegistrationsPerBlock>;
100
101 #[pallet::storage]
104 pub type StakingTo<T: Config> =
105 StorageDoubleMap<_, Identity, T::AccountId, Identity, T::AccountId, BalanceOf<T>>;
106
107 #[pallet::storage]
110 pub type StakedBy<T: Config> =
111 StorageDoubleMap<_, Identity, T::AccountId, Identity, T::AccountId, BalanceOf<T>>;
112
113 #[pallet::storage]
115 pub type TotalStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
116
117 #[pallet::storage]
119 pub type MinAllowedStake<T: Config> =
120 StorageValue<_, BalanceOf<T>, ValueQuery, T::DefaultMinAllowedStake>;
121
122 #[pallet::storage]
125 pub type DividendsParticipationWeight<T: Config> =
126 StorageValue<_, Percent, ValueQuery, T::DefaultDividendsParticipationWeight>;
127
128 #[pallet::storage]
130 pub type FeeConstraints<T: Config> = StorageValue<_, ValidatorFeeConstraints<T>, ValueQuery>;
131
132 #[pallet::storage]
134 pub type BurnConfig<T: Config> = StorageValue<_, BurnConfiguration<T>, ValueQuery>;
135
136 #[pallet::storage]
138 pub type AgentUpdateCooldown<T: Config> =
139 StorageValue<_, BlockNumberFor<T>, ValueQuery, T::DefaultAgentUpdateCooldown>;
140
141 #[pallet::storage]
143 pub type Namespaces<T: Config> = StorageDoubleMap<
144 _,
145 Blake2_128Concat,
146 NamespaceOwnership<T>,
147 Blake2_128Concat,
148 NamespacePath,
149 NamespaceMetadata<T>,
150 >;
151
152 #[pallet::storage]
154 pub type NamespaceCount<T: Config> =
155 StorageMap<_, Blake2_128Concat, NamespaceOwnership<T>, u32, ValueQuery>;
156
157 #[pallet::storage]
158 pub type NamespacePricingConfig<T: Config> = StorageValue<
159 _,
160 namespace::NamespacePricingConfig<T>,
161 ValueQuery,
162 T::DefaultNamespacePricingConfig,
163 >;
164
165 #[pallet::hooks]
166 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
167 fn on_initialize(block_number: BlockNumberFor<T>) -> Weight {
168 let current_block: u64 = block_number
169 .try_into()
170 .ok()
171 .expect("blockchain won't pass 2 ^ 64 blocks");
172
173 burn::adjust_burn::<T>(current_block);
174
175 RegistrationsThisBlock::<T>::set(0);
176
177 Weight::default()
178 }
179 }
180
181 #[pallet::config]
182 pub trait Config: polkadot_sdk::frame_system::Config {
183 #[pallet::constant]
184 type DefaultMaxAllowedValidators: Get<u16>;
185
186 #[pallet::constant]
187 type DefaultMinValidatorStake: Get<BalanceOf<Self>>;
188
189 #[pallet::constant]
190 type DefaultRewardInterval: Get<u16>;
191
192 #[pallet::constant]
193 type DefaultMinNameLength: Get<u16>;
194
195 #[pallet::constant]
196 type DefaultMaxNameLength: Get<u16>;
197
198 #[pallet::constant]
199 type DefaultMaxAgentUrlLength: Get<u16>;
200
201 #[pallet::constant]
202 type DefaultMaxRegistrationsPerBlock: Get<u16>;
203
204 #[pallet::constant]
205 type DefaultMinAllowedStake: Get<BalanceOf<Self>>;
206
207 #[pallet::constant]
208 type DefaultMinStakingFee: Get<u8>;
209
210 #[pallet::constant]
211 type DefaultMinWeightControlFee: Get<u8>;
212
213 #[pallet::constant]
214 type DefaultMinBurn: Get<BalanceOf<Self>>;
215
216 #[pallet::constant]
217 type DefaultMaxBurn: Get<BalanceOf<Self>>;
218
219 #[pallet::constant]
220 type DefaultAdjustmentAlpha: Get<u64>;
221
222 #[pallet::constant]
223 type DefaultTargetRegistrationsInterval: Get<BlockNumberFor<Self>>;
224
225 #[pallet::constant]
226 type DefaultTargetRegistrationsPerInterval: Get<u16>;
227
228 #[pallet::constant]
229 type DefaultMaxRegistrationsPerInterval: Get<u16>;
230
231 #[pallet::constant]
235 type MaxAgentNameLengthConstraint: Get<u32>;
236
237 #[pallet::constant]
240 type MaxAgentUrlLengthConstraint: Get<u32>;
241
242 #[pallet::constant]
243 type MaxAgentMetadataLengthConstraint: Get<u32>;
244
245 #[pallet::constant]
246 type DefaultDividendsParticipationWeight: Get<Percent>;
247
248 #[pallet::constant]
250 type DefaultAgentUpdateCooldown: Get<BlockNumberFor<Self>>;
251
252 #[pallet::constant]
253 type DefaultNamespacePricingConfig: Get<namespace::NamespacePricingConfig<Self>>;
254
255 type RuntimeEvent: From<Event<Self>>
256 + IsType<<Self as polkadot_sdk::frame_system::Config>::RuntimeEvent>;
257
258 type Currency: Currency<Self::AccountId, Balance = u128>
259 + ReservableCurrency<Self::AccountId>
260 + Send
261 + Sync;
262
263 type Governance: GovernanceApi<Self::AccountId>;
264
265 type Emission: Emission0Api<Self::AccountId>;
266 type Permission0: Permission0NamespacesApi<Self::AccountId, NamespacePath>;
267
268 type WeightInfo: WeightInfo;
269 }
270
271 #[pallet::pallet]
272 #[pallet::storage_version(STORAGE_VERSION)]
273 pub struct Pallet<T>(_);
274
275 #[pallet::call]
276 impl<T: Config> Pallet<T> {
277 #[pallet::call_index(0)]
279 #[pallet::weight((T::WeightInfo::add_stake(), DispatchClass::Normal, Pays::Yes))]
280 pub fn add_stake(
281 origin: OriginFor<T>,
282 agent_key: AccountIdOf<T>,
283 amount: BalanceOf<T>,
284 ) -> DispatchResult {
285 let key = ensure_signed(origin)?;
286 ensure!(
287 amount >= crate::MinAllowedStake::<T>::get(),
288 crate::Error::<T>::StakeTooSmall
289 );
290 stake::add_stake::<T>(key, agent_key, amount)
291 }
292
293 #[pallet::call_index(1)]
295 #[pallet::weight((T::WeightInfo::remove_stake(), DispatchClass::Normal, Pays::Yes))]
296 pub fn remove_stake(
297 origin: OriginFor<T>,
298 agent_key: AccountIdOf<T>,
299 amount: BalanceOf<T>,
300 ) -> DispatchResult {
301 let key = ensure_signed(origin)?;
302 stake::remove_stake::<T>(key, agent_key, amount)
303 }
304
305 #[pallet::call_index(2)]
307 #[pallet::weight((T::WeightInfo::transfer_stake(), DispatchClass::Normal, Pays::Yes))]
308 pub fn transfer_stake(
309 origin: OriginFor<T>,
310 agent_key: AccountIdOf<T>,
311 new_agent_key: AccountIdOf<T>,
312 amount: BalanceOf<T>,
313 ) -> DispatchResult {
314 let key = ensure_signed(origin)?;
315 stake::transfer_stake::<T>(key, agent_key, new_agent_key, amount)
316 }
317
318 #[pallet::call_index(3)]
320 #[pallet::weight((T::WeightInfo::register_agent(), DispatchClass::Normal, Pays::Yes))]
321 pub fn register_agent(
322 origin: OriginFor<T>,
323 agent_key: T::AccountId,
324 name: Vec<u8>,
325 url: Vec<u8>,
326 metadata: Vec<u8>,
327 ) -> DispatchResult {
328 let payer = ensure_signed(origin)?;
329 agent::register::<T>(payer, agent_key, name, url, metadata)
330 }
331
332 #[pallet::call_index(4)]
334 #[pallet::weight((T::WeightInfo::unregister_agent(), DispatchClass::Normal, Pays::Yes))]
335 pub fn unregister_agent(origin: OriginFor<T>) -> DispatchResult {
336 let agent_key = ensure_signed(origin)?;
337 agent::unregister::<T>(agent_key)
338 }
339
340 #[pallet::call_index(5)]
342 #[pallet::weight((T::WeightInfo::update_agent(), DispatchClass::Normal, Pays::Yes))]
343 pub fn update_agent(
344 origin: OriginFor<T>,
345 url: Vec<u8>,
346 metadata: Option<Vec<u8>>,
347 staking_fee: Option<Percent>,
348 weight_control_fee: Option<Percent>,
349 ) -> DispatchResult {
350 let agent_key = ensure_signed(origin)?;
351 agent::update::<T>(agent_key, url, metadata, staking_fee, weight_control_fee)
352 }
353
354 #[pallet::call_index(6)]
356 #[pallet::weight((T::WeightInfo::set_agent_update_cooldown(), DispatchClass::Normal, Pays::Yes))]
357 pub fn set_agent_update_cooldown(
358 origin: OriginFor<T>,
359 new_cooldown: BlockNumberFor<T>,
360 ) -> DispatchResult {
361 ensure_root(origin)?;
362 AgentUpdateCooldown::<T>::set(new_cooldown);
363 Ok(())
364 }
365
366 #[pallet::call_index(7)]
368 #[pallet::weight(Weight::default())]
369 pub fn create_namespace(origin: OriginFor<T>, path: NamespacePathInner) -> DispatchResult {
370 let owner = ensure_signed(origin)?;
371
372 ensure!(
373 <T as pallet::Config>::Governance::can_create_namespace(&owner),
374 Error::<T>::NamespacesFrozen
375 );
376
377 let namespace_path =
378 NamespacePath::new_agent(&path).map_err(|_| Error::<T>::InvalidNamespacePath)?;
379
380 namespace::create_namespace::<T>(NamespaceOwnership::Account(owner), namespace_path)
381 }
382
383 #[pallet::call_index(8)]
385 #[pallet::weight(Weight::default())]
386 pub fn delete_namespace(origin: OriginFor<T>, path: NamespacePathInner) -> DispatchResult {
387 let owner = ensure_signed(origin)?;
388
389 let namespace_path =
390 NamespacePath::new_agent(&path).map_err(|_| Error::<T>::InvalidNamespacePath)?;
391
392 ensure!(
393 !namespace_path.is_agent_root(),
394 Error::<T>::InvalidNamespacePath
395 );
396
397 namespace::delete_namespace::<T>(NamespaceOwnership::Account(owner), namespace_path)
398 }
399 }
400
401 #[pallet::event]
402 #[pallet::generate_deposit(pub fn deposit_event)]
403 pub enum Event<T: Config> {
404 StakeAdded(AccountIdOf<T>, AccountIdOf<T>, BalanceOf<T>),
407 StakeRemoved(AccountIdOf<T>, AccountIdOf<T>, BalanceOf<T>),
410 AgentRegistered(AccountIdOf<T>),
413 AgentUnregistered(AccountIdOf<T>),
416 AgentUpdated(AccountIdOf<T>),
419 NamespaceCreated {
421 owner: NamespaceOwnership<T>,
422 path: NamespacePath,
423 },
424 NamespaceDeleted {
426 owner: NamespaceOwnership<T>,
427 path: NamespacePath,
428 },
429 }
430
431 #[pallet::error]
432 pub enum Error<T> {
433 AgentDoesNotExist,
435 NotEnoughStakeToWithdraw,
437 NotEnoughBalanceToStake,
440 TooManyAgentRegistrationsThisBlock,
443 TooManyAgentRegistrationsThisInterval,
446 AgentAlreadyRegistered,
448 CouldNotConvertToBalance,
450 BalanceNotAdded,
452 StakeNotRemoved,
454 InvalidShares,
456 NotEnoughBalanceToRegisterAgent,
458 StakeNotAdded,
460 BalanceNotRemoved,
462 BalanceCouldNotBeRemoved,
464 NotEnoughStakeToRegister,
466 StillRegistered,
468 NotEnoughBalanceToTransfer,
470 InvalidAgentMetadata,
472 AgentMetadataTooLong,
474 AgentMetadataTooShort,
476 InvalidMinBurn,
478 InvalidMaxBurn,
480 AgentNameTooLong,
482 AgentNameTooShort,
484 InvalidAgentName,
486 AgentUrlTooLong,
488 AgentUrlTooShort,
490 InvalidAgentUrl,
492 AgentNameAlreadyExists,
494 StakeTooSmall,
496 AgentKeyNotWhitelisted,
499 InvalidAmount,
501 InvalidStakingFee,
503 InvalidWeightControlFee,
505 AgentUpdateOnCooldown,
507 InvalidNamespacePath,
509 NamespaceAlreadyExists,
511 NamespaceNotFound,
513 ParentNamespaceNotFound,
515 NotNamespaceOwner,
517 NamespaceHasChildren,
519 NamespaceDepthExceeded,
521 NamespaceBeingDelegated,
523 AgentsFrozen,
525 NamespacesFrozen,
527 }
528}
529
530impl<T: Config>
531 pallet_torus0_api::Torus0Api<T::AccountId, <T::Currency as Currency<T::AccountId>>::Balance>
532 for Pallet<T>
533{
534 fn reward_interval() -> u16 {
535 RewardInterval::<T>::get()
536 }
537
538 fn min_validator_stake() -> u128 {
539 MinValidatorStake::<T>::get()
540 }
541
542 fn max_validators() -> u16 {
543 MaxAllowedValidators::<T>::get()
544 }
545
546 fn weight_control_fee(who: &T::AccountId) -> Percent {
547 Agents::<T>::get(who)
548 .map(|agent| agent.fees.weight_control_fee)
549 .unwrap_or_else(|| FeeConstraints::<T>::get().min_weight_control_fee)
550 }
551
552 fn weight_penalty_factor(who: &T::AccountId) -> Percent {
553 Agents::<T>::get(who)
554 .map(|agent| agent.weight_penalty_factor)
555 .unwrap_or_default()
556 }
557
558 fn staking_fee(who: &T::AccountId) -> Percent {
559 Agents::<T>::get(who)
560 .map(|agent| agent.fees.staking_fee)
561 .unwrap_or_else(|| FeeConstraints::<T>::get().min_staking_fee)
562 }
563
564 fn sum_staking_to(staker: &T::AccountId) -> BalanceOf<T> {
565 stake::sum_staking_to::<T>(staker)
566 }
567
568 fn staked_by(
569 staked: &T::AccountId,
570 ) -> sp_std::vec::Vec<(
571 T::AccountId,
572 <T::Currency as Currency<T::AccountId>>::Balance,
573 )> {
574 stake::get_staked_by_vector::<T>(staked)
575 }
576
577 fn stake_to(
578 staker: &T::AccountId,
579 staked: &T::AccountId,
580 amount: <T::Currency as Currency<T::AccountId>>::Balance,
581 ) -> Result<(), <T::Currency as Currency<T::AccountId>>::Balance> {
582 stake::add_stake::<T>(staker.clone(), staked.clone(), amount).map_err(|_| amount)
583 }
584
585 fn agent_ids() -> impl Iterator<Item = T::AccountId> {
586 Agents::<T>::iter_keys()
587 }
588
589 fn is_agent_registered(agent: &T::AccountId) -> bool {
590 Agents::<T>::contains_key(agent)
591 }
592
593 fn namespace_exists(agent: &T::AccountId, path: &NamespacePath) -> bool {
594 Namespaces::<T>::contains_key(NamespaceOwnership::Account(agent.clone()), path)
595 }
596
597 #[cfg(feature = "runtime-benchmarks")]
598 fn force_register_agent(
599 id: &T::AccountId,
600 name: Vec<u8>,
601 url: Vec<u8>,
602 metadata: Vec<u8>,
603 ) -> DispatchResult {
604 crate::Agents::<T>::set(
605 id,
606 Some(Agent {
607 key: id.clone(),
608 name: name
609 .try_into()
610 .map_err(|_| DispatchError::Other("failed to trim fields"))?,
611 url: url
612 .try_into()
613 .map_err(|_| DispatchError::Other("failed to trim fields"))?,
614 metadata: metadata
615 .try_into()
616 .map_err(|_| DispatchError::Other("failed to trim fields"))?,
617 weight_penalty_factor: Default::default(),
618 registration_block: Default::default(),
619 fees: Default::default(),
620 last_update_block: Default::default(),
621 }),
622 );
623
624 Ok(())
625 }
626
627 #[cfg(feature = "runtime-benchmarks")]
628 fn force_set_stake(
629 staker: &T::AccountId,
630 staked: &T::AccountId,
631 amount: BalanceOf<T>,
632 ) -> DispatchResult {
633 stake::add_stake::<T>(staker.clone(), staked.clone(), amount)
634 }
635}