diff --git a/pallets/asset-index/src/lib.rs b/pallets/asset-index/src/lib.rs index be09d7f63b..ba4db79334 100644 --- a/pallets/asset-index/src/lib.rs +++ b/pallets/asset-index/src/lib.rs @@ -355,32 +355,21 @@ pub mod pallet { location: MultiLocation, amount: T::Balance, ) -> DispatchResultWithPostInfo { - let caller = T::AdminOrigin::ensure_origin(origin)?; - if units.is_zero() { - return Ok(().into()); - } - - let availability = AssetAvailability::Liquid(location); - - // check whether this is a new asset and make sure locations match otherwise - let is_new_asset = if let Some(asset) = Assets::::get(&asset_id) { - ensure!(asset == availability, Error::::AssetAlreadyExists); - false - } else { - true - }; - - // transfer the caller's fund into the treasury account - Self::add_liquid(&caller, asset_id, units, amount)?; - - // register asset if not yet known - if is_new_asset { - Assets::::insert(asset_id, availability.clone()); - Self::deposit_event(Event::AssetRegistered(asset_id, availability)); - } + Self::do_add_asset(T::AdminOrigin::ensure_origin(origin)?, asset_id, units, location, amount) + } - Self::deposit_event(Event::AssetAdded(asset_id, units, caller, amount)); - Ok(().into()) + /// Add liquid asset with root origin, see `add_asset` + #[pallet::weight(T::WeightInfo::add_asset())] + pub fn force_add_asset( + origin: OriginFor, + asset_id: T::AssetId, + units: T::Balance, + location: MultiLocation, + amount: T::Balance, + recipient: T::AccountId, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + Self::do_add_asset(recipient, asset_id, units, location, amount) } /// Dispatches transfer to move assets out of the index’s account, @@ -400,20 +389,20 @@ pub mod pallet { units: T::Balance, recipient: Option, ) -> DispatchResultWithPostInfo { - let caller = T::AdminOrigin::ensure_origin(origin)?; - if units.is_zero() { - return Ok(().into()); - } - Self::ensure_not_native_asset(&asset_id)?; - - // the amount of index token the given units of the liquid assets are worth - let index_tokens = Self::index_token_equivalent(asset_id, units)?; - - // transfer the caller's fund into the treasury account - Self::remove_liquid(caller.clone(), asset_id, units, index_tokens, recipient.clone())?; + Self::do_remove_asset(T::AdminOrigin::ensure_origin(origin)?, asset_id, units, recipient) + } - Self::deposit_event(Event::AssetRemoved(asset_id, units, caller, recipient, index_tokens)); - Ok(().into()) + /// Remove liquid asset with root origin + #[pallet::weight(T::WeightInfo::remove_asset())] + pub fn force_remove_asset( + origin: OriginFor, + who: T::AccountId, + asset_id: T::AssetId, + units: T::Balance, + recipient: Option, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + Self::do_remove_asset(who, asset_id, units, recipient) } /// Registers a new asset in the index together with its availability @@ -755,6 +744,66 @@ pub mod pallet { .ok_or_else(|| ArithmeticError::Overflow.into()) } + /// Adds liquid asset + /// + /// - If asset not exists, create asset + /// - If asset exists, deposit more units into matched asset + fn do_add_asset( + recipient: T::AccountId, + asset_id: T::AssetId, + units: T::Balance, + location: MultiLocation, + amount: T::Balance, + ) -> DispatchResultWithPostInfo { + if units.is_zero() { + return Ok(().into()); + } + + let availability = AssetAvailability::Liquid(location); + + // check whether this is a new asset and make sure locations match otherwise + let is_new_asset = if let Some(asset) = Assets::::get(&asset_id) { + ensure!(asset == availability, Error::::AssetAlreadyExists); + false + } else { + true + }; + + // transfer the caller's fund into the treasury account + Self::add_liquid(&recipient, asset_id, units, amount)?; + + // register asset if not yet known + if is_new_asset { + Assets::::insert(asset_id, availability.clone()); + Self::deposit_event(Event::AssetRegistered(asset_id, availability)); + } + + Self::deposit_event(Event::AssetAdded(asset_id, units, recipient, amount)); + Ok(().into()) + } + + /// Removes liquid assets + fn do_remove_asset( + who: T::AccountId, + asset_id: T::AssetId, + units: T::Balance, + recipient: Option, + ) -> DispatchResultWithPostInfo { + if units.is_zero() { + return Ok(().into()); + } + Self::ensure_not_native_asset(&asset_id)?; + + // the amount of index token the given units of the liquid assets are worth + let index_tokens = Self::index_token_equivalent(asset_id, units)?; + + // transfer the caller's fund into the treasury account + Self::remove_liquid(who.clone(), asset_id, units, index_tokens, recipient.clone())?; + + Self::deposit_event(Event::AssetRemoved(asset_id, units, who, recipient, index_tokens)); + Ok(().into()) + } + /// The fee model depends on how long LP contributions remained in the index. /// Therefore, LP deposits in `deposit` are time-stamped (block number) so that fees can be /// determined as a function of time spent in the index. diff --git a/pallets/saft-registry/src/lib.rs b/pallets/saft-registry/src/lib.rs index a3ff03ddd6..155808d149 100644 --- a/pallets/saft-registry/src/lib.rs +++ b/pallets/saft-registry/src/lib.rs @@ -171,37 +171,26 @@ pub mod pallet { /// /// Weight: `O(1)` #[pallet::weight(T::WeightInfo::add_saft())] - #[transactional] pub fn add_saft( origin: OriginFor, asset_id: T::AssetId, nav: T::Balance, units: T::Balance, ) -> DispatchResult { - let caller = T::AdminOrigin::ensure_origin(origin.clone())?; - if units.is_zero() { - return Ok(()); - } - // mint SAFT units into the index and credit the caller's account with PINT - T::AssetRecorder::add_saft(&caller, asset_id, units, nav)?; - - // keep track of total nav - SAFTNetAssetValue::::try_mutate(asset_id, |val| -> Result<_, DispatchError> { - *val = val.checked_add(&nav).ok_or(ArithmeticError::Overflow)?; - Ok(()) - })?; - - // Determine the next id for the SAFT - let saft_id = SAFTCounter::::try_mutate(asset_id, |counter| -> Result<_, DispatchError> { - let id = *counter; - *counter = counter.checked_add(SAFTId::one()).ok_or(ArithmeticError::Overflow)?; - Ok(id) - })?; + Self::do_add_saft(T::AdminOrigin::ensure_origin(origin.clone())?, asset_id, nav, units) + } - // insert the new record - ActiveSAFTs::::insert(asset_id, saft_id, SAFTRecord::new(nav, units)); - Self::deposit_event(Event::::SAFTAdded(asset_id, saft_id)); - Ok(()) + /// Adds saft with root origin + #[pallet::weight(T::WeightInfo::add_saft())] + pub fn force_add_saft( + origin: OriginFor, + recipient: T::AccountId, + asset_id: T::AssetId, + nav: T::Balance, + units: T::Balance, + ) -> DispatchResult { + ensure_root(origin)?; + Self::do_add_saft(recipient, asset_id, nav, units) } /// Removes the SAFT from the registry by purging it from the @@ -219,18 +208,19 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::remove_saft())] #[transactional] pub fn remove_saft(origin: OriginFor, asset_id: T::AssetId, saft_id: SAFTId) -> DispatchResult { - let who = T::AdminOrigin::ensure_origin(origin.clone())?; - - // remove the SAFT record - let saft = ActiveSAFTs::::take(asset_id, saft_id).ok_or(Error::::SAFTNotFound)?; - - // reflect the change in NAV - T::AssetRecorder::remove_saft(who, asset_id, saft.units, saft.nav)?; - SAFTNetAssetValue::::mutate(asset_id, |nav| *nav = nav.saturating_sub(saft.nav)); - - Self::deposit_event(Event::::SAFTRemoved(asset_id, saft_id)); + Self::do_remove_saft(T::AdminOrigin::ensure_origin(origin.clone())?, asset_id, saft_id) + } - Ok(()) + /// Removes saft assets with root origin + #[pallet::weight(T::WeightInfo::remove_saft())] + pub fn force_remove_saft( + origin: OriginFor, + asset_id: T::AssetId, + saft_id: SAFTId, + who: T::AccountId, + ) -> DispatchResult { + ensure_root(origin)?; + Self::do_remove_saft(who, asset_id, saft_id) } /// Called to update the Net Asset Value (NAV) associated with @@ -317,6 +307,59 @@ pub mod pallet { } } + impl Pallet { + /// Adds saft asset + /// + /// - If asset not exists, create asset + /// - If asset exists, deposit more units into matched asset + #[transactional] + fn do_add_saft( + recipient: T::AccountId, + asset_id: T::AssetId, + nav: T::Balance, + units: T::Balance, + ) -> DispatchResult { + if units.is_zero() { + return Ok(()); + } + // mint SAFT units into the index and credit the recipient's account with PINT + T::AssetRecorder::add_saft(&recipient, asset_id, units, nav)?; + + // keep track of total nav + SAFTNetAssetValue::::try_mutate(asset_id, |val| -> Result<_, DispatchError> { + *val = val.checked_add(&nav).ok_or(ArithmeticError::Overflow)?; + Ok(()) + })?; + + // Determine the next id for the SAFT + let saft_id = SAFTCounter::::try_mutate(asset_id, |counter| -> Result<_, DispatchError> { + let id = *counter; + *counter = counter.checked_add(SAFTId::one()).ok_or(ArithmeticError::Overflow)?; + Ok(id) + })?; + + // insert the new record + ActiveSAFTs::::insert(asset_id, saft_id, SAFTRecord::new(nav, units)); + Self::deposit_event(Event::::SAFTAdded(asset_id, saft_id)); + Ok(()) + } + + /// Removes saft assets + #[transactional] + fn do_remove_saft(who: T::AccountId, asset_id: T::AssetId, saft_id: SAFTId) -> DispatchResult { + // remove the SAFT record + let saft = ActiveSAFTs::::take(asset_id, saft_id).ok_or(Error::::SAFTNotFound)?; + + // reflect the change in NAV + T::AssetRecorder::remove_saft(who, asset_id, saft.units, saft.nav)?; + SAFTNetAssetValue::::mutate(asset_id, |nav| *nav = nav.saturating_sub(saft.nav)); + + Self::deposit_event(Event::::SAFTRemoved(asset_id, saft_id)); + + Ok(()) + } + } + // implementation of NAV reporting for SAFT records impl SaftRegistry for Pallet { fn net_saft_value(asset: T::AssetId) -> T::Balance {