use core::marker::PhantomData;
use frame_support::{
ensure, parameter_types,
traits::{
tokens::asset_ops::{
common_strategies::{
AutoId, CanCreate, ConfigValue, ConfigValueMarker, DeriveAndReportId, Owner,
WithConfig,
},
AssetDefinition, Create, Inspect,
},
Incrementable,
},
};
use sp_runtime::{
traits::{Convert, TypedGet},
DispatchError, DispatchResult,
};
use xcm::latest::prelude::*;
use xcm_builder::unique_instances::NonFungibleAsset;
use xcm_executor::traits::{ConvertLocation, Error, MatchesInstance};
pub trait DerivativesRegistry<Original, Derivative> {
fn try_register_derivative(original: &Original, derivative: &Derivative) -> DispatchResult;
fn try_deregister_derivative_of(original: &Original) -> DispatchResult;
fn get_derivative(original: &Original) -> Result<Derivative, DispatchError>;
fn get_original(derivative: &Derivative) -> Result<Original, DispatchError>;
}
pub struct OriginalToDerivativeConvert<R>(PhantomData<R>);
impl<Original, Derivative, R: DerivativesRegistry<Original, Derivative>>
Convert<Original, Result<Derivative, DispatchError>> for OriginalToDerivativeConvert<R>
{
fn convert(a: Original) -> Result<Derivative, DispatchError> {
R::get_derivative(&a)
}
}
pub struct DerivativeToOriginalConvert<R>(PhantomData<R>);
impl<Original, Derivative, R: DerivativesRegistry<Original, Derivative>>
Convert<Derivative, Result<Original, DispatchError>> for DerivativeToOriginalConvert<R>
{
fn convert(a: Derivative) -> Result<Original, DispatchError> {
R::get_original(&a)
}
}
pub struct RegisterDerivative<R, CreateOp>(PhantomData<(R, CreateOp)>);
impl<Original, Derivative, R, CreateOp> Create<DeriveAndReportId<Original, Derivative>>
for RegisterDerivative<R, CreateOp>
where
Original: Clone,
R: DerivativesRegistry<Original, Derivative>,
CreateOp: Create<DeriveAndReportId<Original, Derivative>>,
{
fn create(
id_assignment: DeriveAndReportId<Original, Derivative>,
) -> Result<Derivative, DispatchError> {
let original = id_assignment.params;
let derivative = CreateOp::create(DeriveAndReportId::from(original.clone()))?;
R::try_register_derivative(&original, &derivative)?;
Ok(derivative)
}
}
impl<Original, Derivative, R, Config, CreateOp>
Create<WithConfig<Config, DeriveAndReportId<Original, Derivative>>>
for RegisterDerivative<R, CreateOp>
where
Original: Clone,
R: DerivativesRegistry<Original, Derivative>,
Config: ConfigValueMarker,
CreateOp: Create<WithConfig<Config, DeriveAndReportId<Original, Derivative>>>,
{
fn create(
strategy: WithConfig<Config, DeriveAndReportId<Original, Derivative>>,
) -> Result<Derivative, DispatchError> {
let WithConfig { config, extra: id_assignment } = strategy;
let original = id_assignment.params;
let derivative =
CreateOp::create(WithConfig::new(config, DeriveAndReportId::from(original.clone())))?;
R::try_register_derivative(&original, &derivative)?;
Ok(derivative)
}
}
impl<R, CreateOp: AssetDefinition> AssetDefinition for RegisterDerivative<R, CreateOp> {
type Id = CreateOp::Id;
}
impl<R, CreateOp, Condition> Inspect<CanCreate<Condition>> for RegisterDerivative<R, CreateOp>
where
CreateOp: Inspect<CanCreate<Condition>>,
{
fn inspect(id: &Self::Id, can_create: CanCreate<Condition>) -> Result<bool, DispatchError> {
CreateOp::inspect(id, can_create)
}
}
pub trait IterDerivativesRegistry<Original, Derivative> {
fn iter_originals() -> impl Iterator<Item = Original>;
fn iter_derivatives() -> impl Iterator<Item = Derivative>;
fn iter() -> impl Iterator<Item = (Original, Derivative)>;
}
pub trait DerivativesExtra<Derivative, Extra> {
fn get_derivative_extra(derivative: &Derivative) -> Option<Extra>;
fn set_derivative_extra(derivative: &Derivative, extra: Option<Extra>) -> DispatchResult;
}
pub struct ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>(
PhantomData<(Derivative, Extra, Registry, CreateOp)>,
);
impl<Derivative, Extra, Registry, CreateOp>
ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
where
Extra: Incrementable,
Registry: DerivativesExtra<Derivative, Extra>,
{
fn get_derivative_extra(derivative: &Derivative) -> Result<Extra, DispatchError> {
Registry::get_derivative_extra(derivative)
.or(Extra::initial_value())
.ok_or(DispatchError::Other("ConcatIncrementalExtra: no derivative extra is found"))
}
}
impl<Derivative, Extra, ReportedId, Registry, CreateOp>
Create<DeriveAndReportId<Derivative, ReportedId>>
for ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
where
Extra: Incrementable,
Registry: DerivativesExtra<Derivative, Extra>,
CreateOp: Create<DeriveAndReportId<(Derivative, Extra), ReportedId>>,
{
fn create(
id_assignment: DeriveAndReportId<Derivative, ReportedId>,
) -> Result<ReportedId, DispatchError> {
let derivative = id_assignment.params;
let id = Registry::get_derivative_extra(&derivative).or(Extra::initial_value()).ok_or(
DispatchError::Other(
"ConcatIncrementalExtra: unable to initialize incremental derivative extra",
),
)?;
let next_id = id
.increment()
.ok_or(DispatchError::Other("ConcatIncrementalExtra: failed to increment the id"))?;
Registry::set_derivative_extra(&derivative, Some(next_id))?;
CreateOp::create(DeriveAndReportId::from((derivative, id)))
}
}
impl<Config, Derivative, Extra, ReportedId, Registry, CreateOp>
Create<WithConfig<Config, DeriveAndReportId<Derivative, ReportedId>>>
for ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
where
Config: ConfigValueMarker,
Extra: Incrementable,
Registry: DerivativesExtra<Derivative, Extra>,
CreateOp: Create<WithConfig<Config, DeriveAndReportId<(Derivative, Extra), ReportedId>>>,
{
fn create(
strategy: WithConfig<Config, DeriveAndReportId<Derivative, ReportedId>>,
) -> Result<ReportedId, DispatchError> {
let WithConfig { config, extra: id_assignment } = strategy;
let derivative = id_assignment.params;
let id = Self::get_derivative_extra(&derivative)?;
let next_id = id
.increment()
.ok_or(DispatchError::Other("ConcatIncrementalExtra: failed to increment the id"))?;
Registry::set_derivative_extra(&derivative, Some(next_id))?;
CreateOp::create(WithConfig::new(config, DeriveAndReportId::from((derivative, id))))
}
}
impl<Derivative, Extra, Registry, CreateOp> AssetDefinition
for ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
{
type Id = Derivative;
}
impl<Derivative, Extra, Registry, CreateOp> Inspect<CanCreate>
for ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
where
Derivative: Clone,
Extra: Incrementable,
Registry: DerivativesExtra<Derivative, Extra>,
CreateOp: AssetDefinition<Id = (Derivative, Extra)> + Inspect<CanCreate>,
{
fn inspect(id: &Self::Id, can_create: CanCreate) -> Result<bool, DispatchError> {
let extra = Self::get_derivative_extra(id)?;
CreateOp::inspect(&(id.clone(), extra), can_create)
}
}
pub struct MatchDerivativeInstances<Registry>(PhantomData<Registry>);
impl<Registry: DerivativesRegistry<NonFungibleAsset, DerivativeId>, DerivativeId>
MatchesInstance<DerivativeId> for MatchDerivativeInstances<Registry>
{
fn matches_instance(asset: &Asset) -> Result<DerivativeId, Error> {
match asset.fun {
Fungibility::NonFungible(asset_instance) => {
Registry::get_derivative(&(asset.id.clone(), asset_instance))
.map_err(|_| Error::AssetNotHandled)
},
Fungibility::Fungible(_) => Err(Error::AssetNotHandled),
}
}
}
pub struct EnsureNotDerivativeInstance<Registry, Matcher>(PhantomData<(Registry, Matcher)>);
impl<
Registry: DerivativesRegistry<NonFungibleAsset, DerivativeId>,
Matcher: MatchesInstance<DerivativeId>,
DerivativeId,
> MatchesInstance<DerivativeId> for EnsureNotDerivativeInstance<Registry, Matcher>
{
fn matches_instance(asset: &Asset) -> Result<DerivativeId, Error> {
let instance_id = Matcher::matches_instance(asset)?;
ensure!(Registry::get_original(&instance_id).is_err(), Error::AssetNotHandled);
Ok(instance_id)
}
}
parameter_types! {
pub OwnerConvertedLocationDefaultErr: DispatchError = DispatchError::Other("OwnerConvertedLocation: failed to convert the location");
}
pub struct OwnerConvertedLocation<CL, IdAssignment, Err = OwnerConvertedLocationDefaultErr>(
PhantomData<(CL, IdAssignment, Err)>,
);
impl<AccountId, CL, Err, ReportedId>
Convert<
AssetId,
Result<
WithConfig<ConfigValue<Owner<AccountId>>, DeriveAndReportId<AssetId, ReportedId>>,
DispatchError,
>,
> for OwnerConvertedLocation<CL, DeriveAndReportId<AssetId, ReportedId>, Err>
where
CL: ConvertLocation<AccountId>,
Err: TypedGet,
Err::Type: Into<DispatchError>,
{
fn convert(
AssetId(location): AssetId,
) -> Result<
WithConfig<ConfigValue<Owner<AccountId>>, DeriveAndReportId<AssetId, ReportedId>>,
DispatchError,
> {
CL::convert_location(&location)
.map(|account| {
WithConfig::new(ConfigValue(account), DeriveAndReportId::from(AssetId(location)))
})
.ok_or(Err::get().into())
}
}
impl<AccountId, CL, Err, ReportedId>
Convert<
AssetId,
Result<WithConfig<ConfigValue<Owner<AccountId>>, AutoId<ReportedId>>, DispatchError>,
> for OwnerConvertedLocation<CL, AutoId<ReportedId>, Err>
where
CL: ConvertLocation<AccountId>,
Err: TypedGet,
Err::Type: Into<DispatchError>,
{
fn convert(
AssetId(location): AssetId,
) -> Result<WithConfig<ConfigValue<Owner<AccountId>>, AutoId<ReportedId>>, DispatchError> {
CL::convert_location(&location)
.map(|account| WithConfig::new(ConfigValue(account), AutoId::auto()))
.ok_or(Err::get().into())
}
}