Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 87 additions & 38 deletions pallets/asset-index/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<T>::get(&asset_id) {
ensure!(asset == availability, Error::<T>::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::<T>::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<T>,
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,
Expand All @@ -400,20 +389,20 @@ pub mod pallet {
units: T::Balance,
recipient: Option<T::AccountId>,
) -> 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<T>,
who: T::AccountId,
asset_id: T::AssetId,
units: T::Balance,
recipient: Option<T::AccountId>,
) -> DispatchResultWithPostInfo {
ensure_root(origin)?;
Self::do_remove_asset(who, asset_id, units, recipient)
}

/// Registers a new asset in the index together with its availability
Expand Down Expand Up @@ -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::<T>::get(&asset_id) {
ensure!(asset == availability, Error::<T>::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::<T>::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<T::AccountId>,
) -> 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.
Expand Down
113 changes: 78 additions & 35 deletions pallets/saft-registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,37 +171,26 @@ pub mod pallet {
///
/// Weight: `O(1)`
#[pallet::weight(T::WeightInfo::add_saft())]
#[transactional]
pub fn add_saft(
origin: OriginFor<T>,
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::<T>::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::<T>::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::<T>::insert(asset_id, saft_id, SAFTRecord::new(nav, units));
Self::deposit_event(Event::<T>::SAFTAdded(asset_id, saft_id));
Ok(())
/// Adds saft with root origin
#[pallet::weight(T::WeightInfo::add_saft())]
pub fn force_add_saft(
origin: OriginFor<T>,
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
Expand All @@ -219,18 +208,19 @@ pub mod pallet {
#[pallet::weight(T::WeightInfo::remove_saft())]
#[transactional]
pub fn remove_saft(origin: OriginFor<T>, asset_id: T::AssetId, saft_id: SAFTId) -> DispatchResult {
let who = T::AdminOrigin::ensure_origin(origin.clone())?;

// remove the SAFT record
let saft = ActiveSAFTs::<T>::take(asset_id, saft_id).ok_or(Error::<T>::SAFTNotFound)?;

// reflect the change in NAV
T::AssetRecorder::remove_saft(who, asset_id, saft.units, saft.nav)?;
SAFTNetAssetValue::<T>::mutate(asset_id, |nav| *nav = nav.saturating_sub(saft.nav));

Self::deposit_event(Event::<T>::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<T>,
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
Expand Down Expand Up @@ -317,6 +307,59 @@ pub mod pallet {
}
}

impl<T: Config> Pallet<T> {
/// 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::<T>::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::<T>::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::<T>::insert(asset_id, saft_id, SAFTRecord::new(nav, units));
Self::deposit_event(Event::<T>::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::<T>::take(asset_id, saft_id).ok_or(Error::<T>::SAFTNotFound)?;

// reflect the change in NAV
T::AssetRecorder::remove_saft(who, asset_id, saft.units, saft.nav)?;
SAFTNetAssetValue::<T>::mutate(asset_id, |nav| *nav = nav.saturating_sub(saft.nav));

Self::deposit_event(Event::<T>::SAFTRemoved(asset_id, saft_id));

Ok(())
}
}

// implementation of NAV reporting for SAFT records
impl<T: Config> SaftRegistry<T::AssetId, T::Balance> for Pallet<T> {
fn net_saft_value(asset: T::AssetId) -> T::Balance {
Expand Down