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 Identity,
24 dispatch::DispatchResult,
25 pallet_prelude::{ValueQuery, *},
26 traits::Currency,
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(6);
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::{NamedReservableCurrency, 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 + NamedReservableCurrency<Self::AccountId, ReserveIdentifier = [u8; 8]>
261 + Send
262 + Sync;
263 type ExistentialDeposit: Get<BalanceOf<Self>>;
264
265 type Governance: GovernanceApi<Self::AccountId>;
266
267 type Emission: Emission0Api<Self::AccountId>;
268 type Permission0: Permission0NamespacesApi<Self::AccountId, NamespacePath>;
269
270 type WeightInfo: WeightInfo;
271 }
272
273 #[pallet::pallet]
274 #[pallet::storage_version(STORAGE_VERSION)]
275 pub struct Pallet<T>(_);
276
277 #[pallet::call]
278 impl<T: Config> Pallet<T> {
279 #[pallet::call_index(0)]
281 #[pallet::weight((T::WeightInfo::add_stake(), DispatchClass::Normal, Pays::Yes))]
282 pub fn add_stake(
283 origin: OriginFor<T>,
284 agent_key: AccountIdOf<T>,
285 amount: BalanceOf<T>,
286 ) -> DispatchResult {
287 let key = ensure_signed(origin)?;
288 ensure!(
289 amount >= crate::MinAllowedStake::<T>::get(),
290 crate::Error::<T>::StakeTooSmall
291 );
292 stake::add_stake::<T>(key, agent_key, amount)
293 }
294
295 #[pallet::call_index(1)]
297 #[pallet::weight((T::WeightInfo::remove_stake(), DispatchClass::Normal, Pays::Yes))]
298 pub fn remove_stake(
299 origin: OriginFor<T>,
300 agent_key: AccountIdOf<T>,
301 amount: BalanceOf<T>,
302 ) -> DispatchResult {
303 let key = ensure_signed(origin)?;
304 stake::remove_stake::<T>(key, agent_key, amount)
305 }
306
307 #[pallet::call_index(2)]
309 #[pallet::weight((T::WeightInfo::transfer_stake(), DispatchClass::Normal, Pays::Yes))]
310 pub fn transfer_stake(
311 origin: OriginFor<T>,
312 agent_key: AccountIdOf<T>,
313 new_agent_key: AccountIdOf<T>,
314 amount: BalanceOf<T>,
315 ) -> DispatchResult {
316 let key = ensure_signed(origin)?;
317 stake::transfer_stake::<T>(key, agent_key, new_agent_key, amount)
318 }
319
320 #[pallet::call_index(3)]
322 #[pallet::weight((T::WeightInfo::register_agent(), DispatchClass::Normal, Pays::Yes))]
323 pub fn register_agent(
324 origin: OriginFor<T>,
325 name: Vec<u8>,
326 url: Vec<u8>,
327 metadata: Vec<u8>,
328 ) -> DispatchResult {
329 let agent_key = ensure_signed(origin)?;
330 agent::register::<T>(agent_key, name, url, metadata)
331 }
332
333 #[pallet::call_index(4)]
335 #[pallet::weight((T::WeightInfo::deregister_agent(), DispatchClass::Normal, Pays::Yes))]
336 pub fn deregister_agent(origin: OriginFor<T>) -> DispatchResult {
337 let agent_key = ensure_signed(origin)?;
338 agent::deregister::<T>(agent_key)
339 }
340
341 #[pallet::call_index(5)]
343 #[pallet::weight((T::WeightInfo::update_agent(), DispatchClass::Normal, Pays::Yes))]
344 pub fn update_agent(
345 origin: OriginFor<T>,
346 url: Vec<u8>,
347 metadata: Option<Vec<u8>>,
348 staking_fee: Option<Percent>,
349 weight_control_fee: Option<Percent>,
350 ) -> DispatchResult {
351 let agent_key = ensure_signed(origin)?;
352 agent::update::<T>(agent_key, url, metadata, staking_fee, weight_control_fee)
353 }
354
355 #[pallet::call_index(6)]
357 #[pallet::weight((T::WeightInfo::set_agent_update_cooldown(), DispatchClass::Normal, Pays::Yes))]
358 pub fn set_agent_update_cooldown(
359 origin: OriginFor<T>,
360 new_cooldown: BlockNumberFor<T>,
361 ) -> DispatchResult {
362 ensure_root(origin)?;
363 AgentUpdateCooldown::<T>::set(new_cooldown);
364 Ok(())
365 }
366
367 #[pallet::call_index(7)]
369 #[pallet::weight(Weight::default())]
370 pub fn create_namespace(origin: OriginFor<T>, path: NamespacePathInner) -> DispatchResult {
371 let owner = ensure_signed(origin)?;
372
373 ensure!(
374 <T as pallet::Config>::Governance::can_create_namespace(&owner),
375 Error::<T>::NamespacesFrozen
376 );
377
378 let namespace_path =
379 NamespacePath::new_agent(&path).map_err(|_| Error::<T>::InvalidNamespacePath)?;
380
381 namespace::create_namespace::<T>(NamespaceOwnership::Account(owner), namespace_path)
382 }
383
384 #[pallet::call_index(8)]
386 #[pallet::weight(Weight::default())]
387 pub fn delete_namespace(origin: OriginFor<T>, path: NamespacePathInner) -> DispatchResult {
388 let owner = ensure_signed(origin)?;
389
390 let namespace_path =
391 NamespacePath::new_agent(&path).map_err(|_| Error::<T>::InvalidNamespacePath)?;
392
393 ensure!(
394 !namespace_path.is_agent_root(),
395 Error::<T>::InvalidNamespacePath
396 );
397
398 namespace::delete_namespace::<T>(NamespaceOwnership::Account(owner), namespace_path)
399 }
400 }
401
402 #[pallet::event]
403 #[pallet::generate_deposit(pub fn deposit_event)]
404 pub enum Event<T: Config> {
405 StakeAdded(AccountIdOf<T>, AccountIdOf<T>, BalanceOf<T>),
408 StakeRemoved(AccountIdOf<T>, AccountIdOf<T>, BalanceOf<T>),
411 AgentRegistered(AccountIdOf<T>),
414 AgentUnregistered(AccountIdOf<T>),
417 AgentUpdated(AccountIdOf<T>),
420 NamespaceCreated {
422 owner: NamespaceOwnership<T>,
423 path: NamespacePath,
424 },
425 NamespaceDeleted {
427 owner: NamespaceOwnership<T>,
428 path: NamespacePath,
429 },
430 }
431
432 #[pallet::error]
433 pub enum Error<T> {
434 AgentDoesNotExist,
436 NotEnoughStakeToWithdraw,
438 NotEnoughBalanceToStake,
441 TooManyAgentRegistrationsThisBlock,
444 TooManyAgentRegistrationsThisInterval,
447 AgentAlreadyRegistered,
449 CouldNotConvertToBalance,
451 BalanceNotAdded,
453 StakeNotRemoved,
455 InvalidShares,
457 NotEnoughBalanceToRegisterAgent,
459 StakeNotAdded,
461 BalanceNotRemoved,
463 BalanceCouldNotBeRemoved,
465 NotEnoughStakeToRegister,
467 StillRegistered,
469 NotEnoughBalanceToTransfer,
471 InvalidAgentMetadata,
473 AgentMetadataTooLong,
475 AgentMetadataTooShort,
477 InvalidMinBurn,
479 InvalidMaxBurn,
481 AgentNameTooLong,
483 AgentNameTooShort,
485 InvalidAgentName,
487 AgentUrlTooLong,
489 AgentUrlTooShort,
491 InvalidAgentUrl,
493 AgentNameAlreadyExists,
495 StakeTooSmall,
497 AgentKeyNotWhitelisted,
500 InvalidAmount,
502 InvalidStakingFee,
504 InvalidWeightControlFee,
506 AgentUpdateOnCooldown,
508 InvalidNamespacePath,
510 NamespaceAlreadyExists,
512 NamespaceNotFound,
514 ParentNamespaceNotFound,
516 NotNamespaceOwner,
518 NamespaceHasChildren,
520 NamespaceDepthExceeded,
522 NamespaceBeingDelegated,
524 AgentsFrozen,
526 NamespacesFrozen,
528 }
529}
530
531impl<T: Config>
532 pallet_torus0_api::Torus0Api<T::AccountId, <T::Currency as Currency<T::AccountId>>::Balance>
533 for Pallet<T>
534{
535 fn reward_interval() -> u16 {
536 RewardInterval::<T>::get()
537 }
538
539 fn min_validator_stake() -> u128 {
540 MinValidatorStake::<T>::get()
541 }
542
543 fn max_validators() -> u16 {
544 MaxAllowedValidators::<T>::get()
545 }
546
547 fn weight_control_fee(who: &T::AccountId) -> Percent {
548 Agents::<T>::get(who)
549 .map(|agent| agent.fees.weight_control_fee)
550 .unwrap_or_else(|| FeeConstraints::<T>::get().min_weight_control_fee)
551 }
552
553 fn weight_penalty_factor(who: &T::AccountId) -> Percent {
554 Agents::<T>::get(who)
555 .map(|agent| agent.weight_penalty_factor)
556 .unwrap_or_default()
557 }
558
559 fn staking_fee(who: &T::AccountId) -> Percent {
560 Agents::<T>::get(who)
561 .map(|agent| agent.fees.staking_fee)
562 .unwrap_or_else(|| FeeConstraints::<T>::get().min_staking_fee)
563 }
564
565 fn sum_staking_to(staker: &T::AccountId) -> BalanceOf<T> {
566 stake::sum_staking_to::<T>(staker)
567 }
568
569 fn staked_by(
570 staked: &T::AccountId,
571 ) -> sp_std::vec::Vec<(
572 T::AccountId,
573 <T::Currency as Currency<T::AccountId>>::Balance,
574 )> {
575 stake::get_staked_by_vector::<T>(staked)
576 }
577
578 fn stake_to(
579 staker: &T::AccountId,
580 staked: &T::AccountId,
581 amount: <T::Currency as Currency<T::AccountId>>::Balance,
582 ) -> DispatchResult {
583 stake::add_stake::<T>(staker.clone(), staked.clone(), amount)
584 }
585
586 fn agent_ids() -> impl Iterator<Item = T::AccountId> {
587 Agents::<T>::iter_keys()
588 }
589
590 fn find_agent_by_name(name: &[u8]) -> Option<T::AccountId> {
591 Agents::<T>::iter()
592 .find(|(_, agent)| *agent.name == name)
593 .map(|(id, _)| id)
594 }
595
596 fn is_agent_registered(agent: &T::AccountId) -> bool {
597 Agents::<T>::contains_key(agent)
598 }
599
600 fn namespace_exists(agent: &T::AccountId, path: &NamespacePath) -> bool {
601 Namespaces::<T>::contains_key(NamespaceOwnership::Account(agent.clone()), path)
602 }
603
604 #[cfg(feature = "runtime-benchmarks")]
605 fn force_register_agent(
606 id: &T::AccountId,
607 name: Vec<u8>,
608 url: Vec<u8>,
609 metadata: Vec<u8>,
610 ) -> DispatchResult {
611 crate::Agents::<T>::set(
612 id,
613 Some(Agent {
614 key: id.clone(),
615 name: name
616 .try_into()
617 .map_err(|_| DispatchError::Other("failed to trim fields"))?,
618 url: url
619 .try_into()
620 .map_err(|_| DispatchError::Other("failed to trim fields"))?,
621 metadata: metadata
622 .try_into()
623 .map_err(|_| DispatchError::Other("failed to trim fields"))?,
624 weight_penalty_factor: Default::default(),
625 registration_block: Default::default(),
626 fees: Default::default(),
627 last_update_block: Default::default(),
628 }),
629 );
630
631 Ok(())
632 }
633
634 #[cfg(feature = "runtime-benchmarks")]
635 fn force_set_stake(
636 staker: &T::AccountId,
637 staked: &T::AccountId,
638 amount: BalanceOf<T>,
639 ) -> DispatchResult {
640 stake::add_stake::<T>(staker.clone(), staked.clone(), amount)
641 }
642}