#![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());
+ }
},
}
},