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        CloneNoBound, DebugNoBound, EqNoBound, PartialEqNoBound,
7        traits::{ExistenceRequirement, ReservableCurrency},
8    },
9    frame_system::{self, pallet_prelude::BlockNumberFor},
10    sp_runtime::{
11        DispatchResult, FixedPointNumber, FixedU128,
12        traits::{One, Saturating, Zero},
13    },
14};
15use scale_info::TypeInfo;
16
17use crate::*;
18
19pub use pallet_torus0_api::{
20    MAX_NAMESPACE_PATH_LENGTH, MAX_NAMESPACE_SEGMENTS, MAX_SEGMENT_LENGTH, NAMESPACE_SEPARATOR,
21    NamespacePath,
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        if account_namespace_count == 0 {
64            return Ok(Zero::zero());
65        }
66
67        let Self {
68            base_fee,
69            count_midpoint,
70            fee_steepness,
71            max_fee_multiplier,
72            ..
73        } = self;
74
75        let multiplier = {
76            let fee_steepness = fee_steepness.deconstruct() as f64 / 100.;
77            let position = (account_namespace_count as i64).saturating_sub(*count_midpoint as i64);
78            let adjusted = -fee_steepness * position as f64;
79            let exp = (libm::exp(adjusted) * FixedU128::DIV as f64) as u128;
80
81            let max_fee_multiplier = FixedU128::from_u32(*max_fee_multiplier);
82
83            FixedU128::one().saturating_add(
84                max_fee_multiplier.saturating_mul(
85                    FixedU128::one()
86                        .const_checked_div(
87                            FixedU128::one().saturating_add(FixedU128::from_inner(exp)),
88                        )
89                        .unwrap_or_default(),
90                ),
91            )
92        };
93
94        let base_fee = FixedU128::from_inner(*base_fee);
95        Ok(base_fee.saturating_mul(multiplier).into_inner())
96    }
97
98    /// Calculates the deposit needed to register a namespace. If a path with the agent prefix
99    /// is provided and contains more segments, the prefix (agent literal and agent name)
100    /// will be dropped.
101    pub fn namespace_deposit(&self, path: &NamespacePath) -> BalanceOf<T> {
102        if path.is_agent_root() {
103            return Zero::zero();
104        }
105
106        self.deposit_per_byte
107            .saturating_mul((path.as_bytes().len() as u32).into())
108    }
109
110    /// The fee midpoint.
111    pub fn fee_midpoint(&self) -> BalanceOf<T> {
112        self.base_fee.saturating_add(
113            FixedU128::from_u32(self.max_fee_multiplier)
114                .const_checked_div(FixedU128::from_u32(2))
115                .unwrap_or_default()
116                .saturating_mul(FixedU128::from_inner(self.base_fee))
117                .into_inner(),
118        )
119    }
120
121    /// The fee theoretical ceiling.
122    pub fn fee_ceiling(&self) -> BalanceOf<T> {
123        self.base_fee.saturating_add(
124            FixedU128::from_u32(self.max_fee_multiplier)
125                .saturating_mul(FixedU128::from_inner(self.base_fee))
126                .into_inner(),
127        )
128    }
129}
130
131/// Metadata stored for each namespace
132#[derive(
133    Encode, Decode, CloneNoBound, PartialEqNoBound, EqNoBound, TypeInfo, MaxEncodedLen, DebugNoBound,
134)]
135#[scale_info(skip_type_params(T))]
136pub struct NamespaceMetadata<T: Config> {
137    /// Block number when the namespace was created
138    pub created_at: BlockNumberFor<T>,
139    /// Storage deposit paid for this namespace
140    pub deposit: BalanceOf<T>,
141}
142
143pub fn find_missing_paths<T: Config>(
144    owner: &NamespaceOwnership<T>,
145    path: &NamespacePath,
146) -> Vec<NamespacePath> {
147    let mut paths_to_create = path.parents();
148    paths_to_create.insert(0, path.clone());
149
150    let mut iter = paths_to_create.iter().enumerate().rev();
151    if matches!(owner, NamespaceOwnership::Account(_))
152        && path.root().as_ref().map(NamespacePath::as_bytes) == Some(&b"agent"[..])
153    {
154        // We drop the first segment, agent
155        let _ = iter.by_ref().next();
156    }
157
158    for (i, segment) in iter {
159        if !Namespaces::<T>::contains_key(owner, segment) {
160            return paths_to_create.get(..=i).unwrap_or_default().to_vec();
161        }
162    }
163
164    Default::default()
165}
166
167/// Calculates the total cost for registering, (Fee, Deposit)
168pub fn calculate_cost<T: Config>(
169    owner: &NamespaceOwnership<T>,
170    missing_paths: &[NamespacePath],
171) -> Result<(BalanceOf<T>, BalanceOf<T>), DispatchError> {
172    let current_count = NamespaceCount::<T>::get(owner);
173
174    let pricing_config = crate::NamespacePricingConfig::<T>::get();
175    let mut total_fee = BalanceOf::<T>::zero();
176    let mut total_deposit = BalanceOf::<T>::zero();
177
178    for (index, path) in missing_paths.iter().enumerate() {
179        let count = current_count.saturating_add(index as u32);
180        let fee = pricing_config.namespace_fee(count)?;
181        let deposit = pricing_config.namespace_deposit(path);
182
183        total_fee = total_fee.saturating_add(fee);
184        total_deposit = total_deposit.saturating_add(deposit);
185    }
186
187    Ok((total_fee, total_deposit))
188}
189
190pub fn create_namespace<T: Config>(
191    owner: NamespaceOwnership<T>,
192    path: NamespacePath,
193) -> DispatchResult {
194    #[allow(deprecated)]
195    create_namespace0(owner, path, true)
196}
197
198#[doc(hidden)]
199#[deprecated = "use crate_namespace instead"]
200pub(crate) fn create_namespace0<T: Config>(
201    owner: NamespaceOwnership<T>,
202    path: NamespacePath,
203    charge: bool,
204) -> DispatchResult {
205    ensure!(
206        !Namespaces::<T>::contains_key(&owner, &path),
207        Error::<T>::NamespaceAlreadyExists
208    );
209
210    if let NamespaceOwnership::Account(owner) = &owner {
211        let path_agent = path
212            .segments()
213            .nth(1)
214            .ok_or(Error::<T>::InvalidNamespacePath)?;
215        let agent = Agents::<T>::get(owner).ok_or(Error::<T>::AgentDoesNotExist)?;
216
217        ensure!(path_agent == *agent.name, Error::<T>::InvalidNamespacePath);
218    }
219
220    let missing_paths = find_missing_paths::<T>(&owner, &path);
221
222    if charge {
223        let (total_fee, total_deposit) = calculate_cost::<T>(&owner, &missing_paths)?;
224
225        if let NamespaceOwnership::Account(owner) = &owner {
226            T::Currency::reserve(owner, total_deposit)?;
227
228            <T as crate::Config>::Currency::transfer(
229                owner,
230                &<T as crate::Config>::Governance::dao_treasury_address(),
231                total_fee,
232                ExistenceRequirement::AllowDeath,
233            )
234            .map_err(|_| crate::Error::<T>::NotEnoughBalanceToRegisterAgent)?;
235        }
236    }
237
238    let current_block = <frame_system::Pallet<T>>::block_number();
239    let pricing_config = crate::NamespacePricingConfig::<T>::get();
240
241    for path in missing_paths.iter() {
242        let deposit = if charge {
243            pricing_config.namespace_deposit(path)
244        } else {
245            Zero::zero()
246        };
247
248        let metadata = NamespaceMetadata {
249            created_at: current_block,
250            deposit,
251        };
252
253        Namespaces::<T>::insert(&owner, path, metadata);
254
255        Pallet::<T>::deposit_event(Event::NamespaceCreated {
256            owner: owner.clone(),
257            path: path.clone(),
258        });
259    }
260
261    NamespaceCount::<T>::mutate(&owner, |count| {
262        *count = count.saturating_add(missing_paths.len() as u32)
263    });
264
265    Ok(())
266}
267
268pub fn delete_namespace<T: Config>(
269    owner: NamespaceOwnership<T>,
270    path: NamespacePath,
271) -> DispatchResult {
272    ensure!(
273        Namespaces::<T>::contains_key(&owner, &path),
274        Error::<T>::NamespaceNotFound
275    );
276
277    if let NamespaceOwnership::Account(owner) = &owner {
278        ensure!(
279            !T::Permission0::is_delegating_namespace(owner, &path),
280            Error::<T>::NamespaceBeingDelegated
281        );
282    }
283
284    let mut total_deposit = BalanceOf::<T>::zero();
285    let namespaces_to_delete: Vec<_> = Namespaces::<T>::iter_prefix(&owner)
286        .filter_map(|(other, metadata)| {
287            if other == path || path.is_parent_of(&other) {
288                Some((other, metadata.deposit))
289            } else {
290                None
291            }
292        })
293        .collect();
294
295    let deleted_count = namespaces_to_delete.len() as u32;
296
297    for (path_to_delete, deposit) in namespaces_to_delete {
298        total_deposit = total_deposit.saturating_add(deposit);
299        Namespaces::<T>::remove(&owner, &path_to_delete);
300        Pallet::<T>::deposit_event(Event::NamespaceDeleted {
301            owner: owner.clone(),
302            path: path_to_delete.clone(),
303        });
304    }
305
306    NamespaceCount::<T>::mutate(&owner, |count| *count = count.saturating_sub(deleted_count));
307
308    if let NamespaceOwnership::Account(owner) = &owner {
309        T::Currency::unreserve(owner, total_deposit);
310    }
311
312    Ok(())
313}