#![allow(unused)] fn main() { //! # Fundamentals lesson 3: Assets Holding //! //! Create and managing an in-memory asset holding for the XCM Executor. use sp_runtime::Saturating; use sp_std::{ collections::{btree_map::BTreeMap, btree_set::BTreeSet}, mem, prelude::*, }; use xcm::latest::prelude::*; /// Map of non-wildcard fungible and non-fungible assets held in the holding register. #[derive(Default, Clone, Debug, Eq, PartialEq)] pub struct AssetsInHolding { /// The fungible assets. pub fungible: BTreeMap<AssetId, u128>, /// The non-fungible assets. pub non_fungible: BTreeSet<(AssetId, AssetInstance)>, } impl From<Vec<Asset>> for AssetsInHolding { fn from(assets: Vec<Asset>) -> AssetsInHolding { let mut result = Self::default(); for asset in assets.into_iter() { result.subsume(asset) } result } } impl From<Assets> for AssetsInHolding { fn from(assets: Assets) -> AssetsInHolding { assets.into_inner().into() } } impl AssetsInHolding { /// New value, containing no assets. pub fn new() -> Self { Self::default() } /// Mutate `self` to contain the given `asset`, saturating if necessary. pub fn subsume(&mut self, asset: Asset) { /* TODO: - For fungible assets: - Check if we already have some existing assets in the holding. - If we do, `saturating_add` to the existing amount. - If we don't, `insert` the asset into the `BTreeMap`. - For non-fungible assets: - Simply insert the `asset.id` and `instance` into the `BTreeSet`. */ todo!("{:?}", asset) } /// Mutate `self` to contain all given `assets`, saturating if necessary. /// NOTE: This function can be written more optimally given the fact that assets in the holding /// are always in order. pub fn subsume_assets(&mut self, mut assets: AssetsInHolding) { /* TODO: - For each `(key, value)` in `assets.fungible`. - If the asset already exists `self.fungible`, `saturating_accrue` the new `value`. - If it is new, `insert` the new asset. - For non-fungible assets, take `self.non_fungible` and `append` the `assets.non_fungible`. - Then clear `assets.fungible` and `assets.non_fungible`. */ todo!("{:?}", assets) } /// Mutates `self` to its original value less `mask` and returns `true` iff it contains at least /// `mask`. /// /// Returns `Ok` with the non-wildcard equivalence of `mask` taken and mutates `self` to its /// value minus `mask` if `self` contains `asset`, and return `Err` otherwise. pub fn saturating_take(&mut self, asset: AssetFilter) -> AssetsInHolding { let mut taken = AssetsInHolding::new(); match asset { AssetFilter::Wild(All) => { /* TODO: Match on and return all assets. - Create a new `AssetsInHolding` called `new_holding` - use `mem::swap` to swap this with `self` - return the `new_holding` */ todo!() }, AssetFilter::Definite(assets) => for asset in assets.into_inner() { match asset { Asset { fun: Fungible(amount), id } => { /* TODO: - Check if there is any of this asset in the holding. - Get the `min` amount between the filter and what is in holding. - Reduce the holding by that amount. - Check if we should `remove` the asset from the holding when it's value goes to zero. - Finally, `subsume` these assets into `taken`. */ todo!("{:?} {:?}", amount, id) }, Asset { fun: NonFungible(instance), id } => { /* TODO: - Try to `remove` the asset from `self.non_fungible` - If the asset was there, move it to `taken` */ todo!("{:?} {:?}", instance, id) }, } }, _ => unimplemented!("Handling other asset filters is not included to simplify the scope of the project.") } taken } /// A consuming iterator over all assets. pub fn into_assets_iter(self) -> impl Iterator<Item = Asset> { self.fungible .into_iter() .map(|(id, amount)| Asset { fun: Fungible(amount), id }) .chain( self.non_fungible .into_iter() .map(|(id, instance)| Asset { fun: NonFungible(instance), id }), ) } } }
#![allow(unused)] fn main() { //! # Fundamentals lesson 3: Assets Holding //! //! Create and managing an in-memory asset holding for the XCM Executor. use sp_runtime::Saturating; use sp_std::{ collections::{btree_map::BTreeMap, btree_set::BTreeSet}, mem, prelude::*, }; use xcm::latest::prelude::*; /// Map of non-wildcard fungible and non-fungible assets held in the holding register. #[derive(Default, Clone, Debug, Eq, PartialEq)] pub struct AssetsInHolding { /// The fungible assets. pub fungible: BTreeMap<AssetId, u128>, /// The non-fungible assets. pub non_fungible: BTreeSet<(AssetId, AssetInstance)>, } impl From<Vec<Asset>> for AssetsInHolding { fn from(assets: Vec<Asset>) -> AssetsInHolding { let mut result = Self::default(); for asset in assets.into_iter() { result.subsume(asset) } result } } impl From<Assets> for AssetsInHolding { fn from(assets: Assets) -> AssetsInHolding { assets.into_inner().into() } } impl AssetsInHolding { /// New value, containing no assets. pub fn new() -> Self { Self::default() } /// Mutate `self` to contain the given `asset`, saturating if necessary. pub fn subsume(&mut self, asset: Asset) { match asset.fun { Fungible(amount) => if let Some(existing_amount) = self.fungible.get_mut(&asset.id) { *existing_amount = existing_amount.saturating_add(amount); } else { self.fungible.insert(asset.id, amount); }, NonFungible(instance) => { self.non_fungible.insert((asset.id, instance)); }, } } /// Mutate `self` to contain all given `assets`, saturating if necessary. /// NOTE: This function can be written more optimally given the fact that assets in the holding /// are always in order. pub fn subsume_assets(&mut self, mut assets: AssetsInHolding) { // Loop through all fungible assets in `assets` and add them to `self` for (key, value) in assets.fungible.iter_mut() { if let Some(existing_value) = self.fungible.get_mut(key) { // If the asset already exists in `self`, add the values, saturating if necessary existing_value.saturating_accrue(*value); } else { // Otherwise, insert the new asset into `self` self.fungible.insert(key.clone(), *value); } } // Append all non-fungible assets from `assets` to `self` self.non_fungible.append(&mut assets.non_fungible); // Clear the original `assets` collections as they are now part of `self` assets.fungible.clear(); assets.non_fungible.clear(); } /// Mutates `self` to its original value less `mask` and returns `true` iff it contains at least /// `mask`. /// /// Returns `Ok` with the non-wildcard equivalence of `mask` taken and mutates `self` to its /// value minus `mask` if `self` contains `asset`, and return `Err` otherwise. pub fn saturating_take(&mut self, asset: AssetFilter) -> AssetsInHolding { let mut taken = AssetsInHolding::new(); match asset { AssetFilter::Wild(All) => { let mut new_holding = AssetsInHolding::new(); mem::swap(&mut *self, &mut new_holding); return new_holding }, AssetFilter::Definite(assets) => for asset in assets.into_inner() { match asset { Asset { fun: Fungible(amount), id } => { let amount = if let Some(self_amount) = self.fungible.get_mut(&id) { let amount = amount.min(*self_amount); *self_amount -= amount; if *self_amount == 0 { self.fungible.remove(&id); } amount } else { 0 }; if amount > 0 { taken.subsume(Asset::from((id, amount))); } }, Asset { fun: NonFungible(instance), id } => { let id_instance = (id, instance); if self.non_fungible.remove(&id_instance) { taken.subsume(id_instance.into()); } }, } }, _ => unimplemented!("Handling other asset filters is not included to simplify the scope of the project.") } taken } /// A consuming iterator over all assets. pub fn into_assets_iter(self) -> impl Iterator<Item = Asset> { self.fungible .into_iter() .map(|(id, amount)| Asset { fun: Fungible(amount), id }) .chain( self.non_fungible .into_iter() .map(|(id, instance)| Asset { fun: NonFungible(instance), id }), ) } } }
diff --git a/fundamentals/src/holding.rs b/fundamentals/src/holding.rs index 8b13789..8b9c09b 100644 --- a/fundamentals/src/holding.rs +++ b/fundamentals/src/holding.rs @@ -1 +1,129 @@ +//! # Fundamentals lesson 3: Assets Holding +//! +//! Create and managing an in-memory asset holding for the XCM Executor. +use sp_runtime::Saturating; +use sp_std::{ + collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + mem, + prelude::*, +}; +use xcm::latest::prelude::*; + +/// Map of non-wildcard fungible and non-fungible assets held in the holding register. +#[derive(Default, Clone, Debug, Eq, PartialEq)] +pub struct AssetsInHolding { + /// The fungible assets. + pub fungible: BTreeMap<AssetId, u128>, + + /// The non-fungible assets. + pub non_fungible: BTreeSet<(AssetId, AssetInstance)>, +} + +impl From<Vec<Asset>> for AssetsInHolding { + fn from(assets: Vec<Asset>) -> AssetsInHolding { + let mut result = Self::default(); + for asset in assets.into_iter() { + result.subsume(asset) + } + result + } +} + +impl From<Assets> for AssetsInHolding { + fn from(assets: Assets) -> AssetsInHolding { + assets.into_inner().into() + } +} + +impl AssetsInHolding { + /// New value, containing no assets. + pub fn new() -> Self { + Self::default() + } + + /// Mutate `self` to contain the given `asset`, saturating if necessary. + pub fn subsume(&mut self, asset: Asset) { + /* TODO: + - For fungible assets: + - Check if we already have some existing assets in the holding. + - If we do, `saturating_add` to the existing amount. + - If we don't, `insert` the asset into the `BTreeMap`. + - For non-fungible assets: + - Simply insert the `asset.id` and `instance` into the `BTreeSet`. + + */ + todo!("{:?}", asset) + } + + /// Mutate `self` to contain all given `assets`, saturating if necessary. + /// NOTE: This function can be written more optimally given the fact that assets in the holding + /// are always in order. + pub fn subsume_assets(&mut self, mut assets: AssetsInHolding) { + /* TODO: + - For each `(key, value)` in `assets.fungible`. + - If the asset already exists `self.fungible`, `saturating_accrue` the new `value`. + - If it is new, `insert` the new asset. + - For non-fungible assets, take `self.non_fungible` and `append` the `assets.non_fungible`. + - Then clear `assets.fungible` and `assets.non_fungible`. + */ + todo!("{:?}", assets) + } + + /// Mutates `self` to its original value less `mask` and returns `true` iff it contains at least + /// `mask`. + /// + /// Returns `Ok` with the non-wildcard equivalence of `mask` taken and mutates `self` to its + /// value minus `mask` if `self` contains `asset`, and return `Err` otherwise. + pub fn saturating_take(&mut self, asset: AssetFilter) -> AssetsInHolding { + let mut taken = AssetsInHolding::new(); + + match asset { + AssetFilter::Wild(All) => { + /* TODO: Match on and return all assets. + - Create a new `AssetsInHolding` called `new_holding` + - use `mem::swap` to swap this with `self` + - return the `new_holding` + */ + todo!() + }, + AssetFilter::Definite(assets) => + for asset in assets.into_inner() { + match asset { + Asset { fun: Fungible(amount), id } => { + /* TODO: + - Check if there is any of this asset in the holding. + - Get the `min` amount between the filter and what is in holding. + - Reduce the holding by that amount. + - Check if we should `remove` the asset from the holding when it's value goes to zero. + - Finally, `subsume` these assets into `taken`. + */ + todo!("{:?} {:?}", amount, id) + }, + Asset { fun: NonFungible(instance), id } => { + /* TODO: + - Try to `remove` the asset from `self.non_fungible` + - If the asset was there, move it to `taken` + */ + todo!("{:?} {:?}", instance, id) + }, + } + }, + _ => unimplemented!("Handling other asset filters is not included to simplify the scope of the project.") + } + + taken + } + + /// A consuming iterator over all assets. + pub fn into_assets_iter(self) -> impl Iterator<Item = Asset> { + self.fungible + .into_iter() + .map(|(id, amount)| Asset { fun: Fungible(amount), id }) + .chain( + self.non_fungible + .into_iter() + .map(|(id, instance)| Asset { fun: NonFungible(instance), id }), + ) + } +}
diff --git a/fundamentals/src/holding.rs b/fundamentals/src/holding.rs index 8b9c09b..657ad56 100644 --- a/fundamentals/src/holding.rs +++ b/fundamentals/src/holding.rs @@ -44,30 +44,40 @@ impl AssetsInHolding { /// Mutate `self` to contain the given `asset`, saturating if necessary. pub fn subsume(&mut self, asset: Asset) { - /* TODO: - - For fungible assets: - - Check if we already have some existing assets in the holding. - - If we do, `saturating_add` to the existing amount. - - If we don't, `insert` the asset into the `BTreeMap`. - - For non-fungible assets: - - Simply insert the `asset.id` and `instance` into the `BTreeSet`. - - */ - todo!("{:?}", asset) + match asset.fun { + Fungible(amount) => + if let Some(existing_amount) = self.fungible.get_mut(&asset.id) { + *existing_amount = existing_amount.saturating_add(amount); + } else { + self.fungible.insert(asset.id, amount); + }, + NonFungible(instance) => { + self.non_fungible.insert((asset.id, instance)); + }, + } } /// Mutate `self` to contain all given `assets`, saturating if necessary. /// NOTE: This function can be written more optimally given the fact that assets in the holding /// are always in order. pub fn subsume_assets(&mut self, mut assets: AssetsInHolding) { - /* TODO: - - For each `(key, value)` in `assets.fungible`. - - If the asset already exists `self.fungible`, `saturating_accrue` the new `value`. - - If it is new, `insert` the new asset. - - For non-fungible assets, take `self.non_fungible` and `append` the `assets.non_fungible`. - - Then clear `assets.fungible` and `assets.non_fungible`. - */ - todo!("{:?}", assets) + // Loop through all fungible assets in `assets` and add them to `self` + for (key, value) in assets.fungible.iter_mut() { + if let Some(existing_value) = self.fungible.get_mut(key) { + // If the asset already exists in `self`, add the values, saturating if necessary + existing_value.saturating_accrue(*value); + } else { + // Otherwise, insert the new asset into `self` + self.fungible.insert(key.clone(), *value); + } + } + + // Append all non-fungible assets from `assets` to `self` + self.non_fungible.append(&mut assets.non_fungible); + + // Clear the original `assets` collections as they are now part of `self` + assets.fungible.clear(); + assets.non_fungible.clear(); } /// Mutates `self` to its original value less `mask` and returns `true` iff it contains at least @@ -80,32 +90,33 @@ impl AssetsInHolding { match asset { AssetFilter::Wild(All) => { - /* TODO: Match on and return all assets. - - Create a new `AssetsInHolding` called `new_holding` - - use `mem::swap` to swap this with `self` - - return the `new_holding` - */ - todo!() + let mut new_holding = AssetsInHolding::new(); + mem::swap(&mut *self, &mut new_holding); + return new_holding }, AssetFilter::Definite(assets) => for asset in assets.into_inner() { match asset { Asset { fun: Fungible(amount), id } => { - /* TODO: - - Check if there is any of this asset in the holding. - - Get the `min` amount between the filter and what is in holding. - - Reduce the holding by that amount. - - Check if we should `remove` the asset from the holding when it's value goes to zero. - - Finally, `subsume` these assets into `taken`. - */ - todo!("{:?} {:?}", amount, id) + let amount = if let Some(self_amount) = self.fungible.get_mut(&id) { + let amount = amount.min(*self_amount); + *self_amount -= amount; + if *self_amount == 0 { + self.fungible.remove(&id); + } + amount + } else { + 0 + }; + if amount > 0 { + taken.subsume(Asset::from((id, amount))); + } }, Asset { fun: NonFungible(instance), id } => { - /* TODO: - - Try to `remove` the asset from `self.non_fungible` - - If the asset was there, move it to `taken` - */ - todo!("{:?} {:?}", instance, id) + let id_instance = (id, instance); + if self.non_fungible.remove(&id_instance) { + taken.subsume(id_instance.into()); + } }, } },