pallet_torus0/
namespace.rs

1use codec::{Decode, Encode, MaxEncodedLen};
2use pallet_governance_api::GovernanceApi;
3use pallet_permission0_api::Permission0NamespacesApi;
4use polkadot_sdk::{
5    frame_support::{
6        traits::{ExistenceRequirement, ReservableCurrency},
7        CloneNoBound, DebugNoBound, EqNoBound, PartialEqNoBound,
8    },
9    frame_system::{self, pallet_prelude::BlockNumberFor},
10    sp_runtime::{
11        traits::{One, Saturating, Zero},
12        DispatchResult, FixedPointNumber, FixedU128,
13    },
14};
15use scale_info::TypeInfo;
16
17use crate::*;
18
19pub use pallet_torus0_api::{
20    NamespacePath, MAX_NAMESPACE_PATH_LENGTH, MAX_NAMESPACE_SEGMENTS, MAX_SEGMENT_LENGTH,
21    NAMESPACE_SEPARATOR,
22};
23
24/// Describes the ownership of the namespace.
25#[derive(
26    Encode, Decode, CloneNoBound, DebugNoBound, PartialEqNoBound, EqNoBound, TypeInfo, MaxEncodedLen,
27)]
28#[scale_info(skip_type_params(T))]
29pub enum NamespaceOwnership<T: Config> {
30    /// Owned by the system. Used to register root-level namespaces, like agent.
31    System,
32    /// Owned by a SS58 account. Used to represent agent-owned namespaces.
33    Account(T::AccountId),
34}
35
36#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen, DebugNoBound)]
37#[scale_info(skip_type_params(T))]
38pub struct NamespacePricingConfig<T: Config> {
39    /// Tokens held per byte inserted.
40    pub deposit_per_byte: BalanceOf<T>,
41    /// Used as the base fee for the logistic function that
42    /// calculates the fees based on the amount of namespace entries
43    /// for a given agent.
44    pub base_fee: BalanceOf<T>,
45
46    /// The logistic function's midpoint.
47    /// The function is performed over the number of namespace entries
48    /// the agent has registered. The midpoint determines at how many
49    /// entries for that agent the price reaches it's mid-point.
50    pub count_midpoint: u32,
51    /// How steep the pricing increase is.
52    pub fee_steepness: Percent,
53    /// The maximum multiplier for the base fee.
54    pub max_fee_multiplier: u32,
55}
56
57impl<T: Config> NamespacePricingConfig<T> {
58    /// Calculate namespace cost using sigmoid pricing model.
59    pub fn namespace_fee(
60        &self,
61        account_namespace_count: u32,
62    ) -> Result<BalanceOf<T>, polkadot_sdk::sp_runtime::DispatchError> {
63        let Self {
64            base_fee,
65            count_midpoint,
66            fee_steepness,
67            max_fee_multiplier,
68            ..
69        } = self;
70
71        let multiplier = {
72            let fee_steepness = fee_steepness.deconstruct() as f64 / 100.;
73            let position = (account_namespace_count as i64).saturating_sub(*count_midpoint as i64);
74            let adjusted = -fee_steepness * position as f64;
75            let exp = (libm::exp(adjusted) * FixedU128::DIV as f64) as u128;
76
77            let max_fee_multiplier = FixedU128::from_u32(*max_fee_multiplier);
78
79            FixedU128::one().saturating_add(
80                max_fee_multiplier.saturating_mul(
81                    FixedU128::one()
82                        .const_checked_div(
83                            FixedU128::one().saturating_add(FixedU128::from_inner(exp)),
84                        )
85                        .unwrap_or_default(),
86                ),
87            )
88        };
89
90        let base_fee = FixedU128::from_inner(*base_fee);
91        Ok(base_fee.saturating_mul(multiplier).into_inner())
92    }
93
94    /// Calculates the deposit needed to register a namespace. If a path with the agent prefix
95    /// is provided and contains more segments, the prefix (agent literal and agent name)
96    /// will be dropped.
97    pub fn namespace_deposit(&self, path: &NamespacePath) -> BalanceOf<T> {
98        self.deposit_per_byte
99            .saturating_mul((path.as_bytes().len() as u32).into())
100    }
101
102    /// The fee midpoint.
103    pub fn fee_midpoint(&self) -> BalanceOf<T> {
104        self.base_fee.saturating_add(
105            FixedU128::from_u32(self.max_fee_multiplier)
106                .const_checked_div(FixedU128::from_u32(2))
107                .unwrap_or_default()
108                .saturating_mul(FixedU128::from_inner(self.base_fee))
109                .into_inner(),
110        )
111    }
112
113    /// The fee theoretical ceiling.
114    pub fn fee_ceiling(&self) -> BalanceOf<T> {
115        self.base_fee.saturating_add(
116            FixedU128::from_u32(self.max_fee_multiplier)
117                .saturating_mul(FixedU128::from_inner(self.base_fee))
118                .into_inner(),
119        )
120    }
121}
122
123/// Metadata stored for each namespace
124#[derive(
125    Encode, Decode, CloneNoBound, PartialEqNoBound, EqNoBound, TypeInfo, MaxEncodedLen, DebugNoBound,
126)]
127#[scale_info(skip_type_params(T))]
128pub struct NamespaceMetadata<T: Config> {
129    /// Block number when the namespace was created
130    pub created_at: BlockNumberFor<T>,
131    /// Storage deposit paid for this namespace
132    pub deposit: BalanceOf<T>,
133}
134
135pub fn find_missing_paths<T: Config>(
136    owner: &NamespaceOwnership<T>,
137    path: &NamespacePath,
138) -> Vec<NamespacePath> {
139    let mut paths_to_create = path.parents();
140    paths_to_create.insert(0, path.clone());
141
142    let mut iter = paths_to_create.iter().enumerate().rev();
143    if matches!(owner, NamespaceOwnership::Account(_))
144        && path.root().as_ref().map(NamespacePath::as_bytes) == Some(&b"agent"[..])
145    {
146        // We drop the first segment, agent
147        let _ = iter.by_ref().next();
148    }
149
150    for (i, segment) in iter {
151        if !Namespaces::<T>::contains_key(owner, segment) {
152            return paths_to_create.get(..=i).unwrap_or_default().to_vec();
153        }
154    }
155
156    Default::default()
157}
158
159/// Calculates the total cost for registering, (Fee, Deposit)
160pub fn calculate_cost<T: Config>(
161    owner: &NamespaceOwnership<T>,
162    missing_paths: &[NamespacePath],
163) -> Result<(BalanceOf<T>, BalanceOf<T>), DispatchError> {
164    let current_count = NamespaceCount::<T>::get(owner);
165
166    let pricing_config = crate::NamespacePricingConfig::<T>::get();
167    let mut total_fee = BalanceOf::<T>::zero();
168    let mut total_deposit = BalanceOf::<T>::zero();
169
170    for (index, path) in missing_paths.iter().enumerate() {
171        let count = current_count.saturating_add(index as u32);
172        let fee = pricing_config.namespace_fee(count)?;
173        let deposit = pricing_config.namespace_deposit(path);
174
175        total_fee = total_fee.saturating_add(fee);
176        total_deposit = total_deposit.saturating_add(deposit);
177    }
178
179    Ok((total_fee, total_deposit))
180}
181
182pub fn create_namespace<T: Config>(
183    owner: NamespaceOwnership<T>,
184    path: NamespacePath,
185) -> DispatchResult {
186    #[allow(deprecated)]
187    create_namespace0(owner, path, true)
188}
189
190#[doc(hidden)]
191#[deprecated = "use crate_namespace instead"]
192pub(crate) fn create_namespace0<T: Config>(
193    owner: NamespaceOwnership<T>,
194    path: NamespacePath,
195    charge: bool,
196) -> DispatchResult {
197    ensure!(
198        !Namespaces::<T>::contains_key(&owner, &path),
199        Error::<T>::NamespaceAlreadyExists
200    );
201
202    if let NamespaceOwnership::Account(owner) = &owner {
203        let path_agent = path
204            .segments()
205            .nth(1)
206            .ok_or(Error::<T>::InvalidNamespacePath)?;
207        let agent = Agents::<T>::get(owner).ok_or(Error::<T>::AgentDoesNotExist)?;
208
209        ensure!(path_agent == *agent.name, Error::<T>::InvalidNamespacePath);
210    }
211
212    let missing_paths = find_missing_paths::<T>(&owner, &path);
213
214    if charge {
215        let (total_fee, total_deposit) = calculate_cost::<T>(&owner, &missing_paths)?;
216
217        if let NamespaceOwnership::Account(owner) = &owner {
218            T::Currency::reserve(owner, total_deposit)?;
219
220            <T as crate::Config>::Currency::transfer(
221                owner,
222                &<T as crate::Config>::Governance::dao_treasury_address(),
223                total_fee,
224                ExistenceRequirement::AllowDeath,
225            )
226            .map_err(|_| crate::Error::<T>::NotEnoughBalanceToRegisterAgent)?;
227        }
228    }
229
230    let current_block = <frame_system::Pallet<T>>::block_number();
231    let pricing_config = crate::NamespacePricingConfig::<T>::get();
232
233    for path in missing_paths.iter() {
234        let deposit = if charge {
235            pricing_config.namespace_deposit(path)
236        } else {
237            Zero::zero()
238        };
239
240        let metadata = NamespaceMetadata {
241            created_at: current_block,
242            deposit,
243        };
244
245        Namespaces::<T>::insert(&owner, path, metadata);
246    }
247
248    NamespaceCount::<T>::mutate(&owner, |count| {
249        *count = count.saturating_add(missing_paths.len() as u32)
250    });
251
252    Pallet::<T>::deposit_event(Event::NamespaceCreated { owner, path });
253
254    Ok(())
255}
256
257pub fn delete_namespace<T: Config>(
258    owner: NamespaceOwnership<T>,
259    path: NamespacePath,
260) -> DispatchResult {
261    ensure!(
262        Namespaces::<T>::contains_key(&owner, &path),
263        Error::<T>::NamespaceNotFound
264    );
265
266    if let NamespaceOwnership::Account(owner) = &owner {
267        ensure!(
268            !T::Permission0::is_delegating_namespace(owner, &path),
269            Error::<T>::NamespaceBeingDelegated
270        );
271    }
272
273    let mut total_deposit = BalanceOf::<T>::zero();
274    let namespaces_to_delete: Vec<_> = Namespaces::<T>::iter_prefix(&owner)
275        .filter_map(|(other, metadata)| {
276            if other == path || path.is_parent_of(&other) {
277                Some((other, metadata.deposit))
278            } else {
279                None
280            }
281        })
282        .collect();
283
284    let deleted_count = namespaces_to_delete.len() as u32;
285
286    for (path_to_delete, deposit) in namespaces_to_delete {
287        total_deposit = total_deposit.saturating_add(deposit);
288        Namespaces::<T>::remove(&owner, &path_to_delete);
289    }
290
291    NamespaceCount::<T>::mutate(&owner, |count| *count = count.saturating_sub(deleted_count));
292
293    if let NamespaceOwnership::Account(owner) = &owner {
294        T::Currency::unreserve(owner, total_deposit);
295    }
296
297    Pallet::<T>::deposit_event(Event::NamespaceDeleted { owner, path });
298
299    Ok(())
300}