1use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
18use pezframe_support::traits::{
19 fungibles,
20 tokens::{PaymentStatus, Preservation},
21};
22use pezkuwi_runtime_common::impls::VersionedLocatableAsset;
23use pezsp_runtime::{traits::TypedGet, DispatchError, RuntimeDebug};
24use xcm::latest::prelude::*;
25use xcm_executor::traits::ConvertLocation;
26
27#[derive(
30 Encode,
31 Decode,
32 Eq,
33 PartialEq,
34 Clone,
35 RuntimeDebug,
36 scale_info::TypeInfo,
37 MaxEncodedLen,
38 DecodeWithMemTracking,
39)]
40pub enum VersionedLocatableAccount {
41 #[codec(index = 4)]
42 V4 { location: xcm::v4::Location, account_id: xcm::v4::Location },
43 #[codec(index = 5)]
44 V5 { location: xcm::v5::Location, account_id: xcm::v5::Location },
45}
46
47pub struct LocalPay<F, A, C>(core::marker::PhantomData<(F, A, C)>);
50impl<A, F, C> pezframe_support::traits::tokens::Pay for LocalPay<F, A, C>
51where
52 A: TypedGet,
53 F: fungibles::Mutate<A::Type, AssetId = xcm::v5::Location> + fungibles::Create<A::Type>,
54 C: ConvertLocation<A::Type>,
55 A::Type: Eq + Clone,
56{
57 type Balance = F::Balance;
58 type Beneficiary = VersionedLocatableAccount;
59 type AssetKind = VersionedLocatableAsset;
60 type Id = QueryId;
61 type Error = DispatchError;
62 fn pay(
63 who: &Self::Beneficiary,
64 asset: Self::AssetKind,
65 amount: Self::Balance,
66 ) -> Result<Self::Id, Self::Error> {
67 let who = Self::match_location(who).map_err(|_| DispatchError::Unavailable)?;
68 let asset = Self::match_asset(&asset).map_err(|_| DispatchError::Unavailable)?;
69 <F as fungibles::Mutate<_>>::transfer(
70 asset,
71 &A::get(),
72 &who,
73 amount,
74 Preservation::Expendable,
75 )?;
76 Ok(Self::Id::MAX)
80 }
81 fn check_payment(_: Self::Id) -> PaymentStatus {
82 PaymentStatus::Success
83 }
84 #[cfg(feature = "runtime-benchmarks")]
85 fn ensure_successful(_: &Self::Beneficiary, asset: Self::AssetKind, amount: Self::Balance) {
86 let asset = Self::match_asset(&asset).expect("invalid asset");
87 <F as fungibles::Create<_>>::create(asset.clone(), A::get(), true, amount).unwrap();
88 <F as fungibles::Mutate<_>>::mint_into(asset, &A::get(), amount).unwrap();
89 }
90 #[cfg(feature = "runtime-benchmarks")]
91 fn ensure_concluded(_: Self::Id) {}
92}
93
94impl<A, F, C> LocalPay<F, A, C>
95where
96 A: TypedGet,
97 F: fungibles::Mutate<A::Type> + fungibles::Create<A::Type>,
98 C: ConvertLocation<A::Type>,
99 A::Type: Eq + Clone,
100{
101 fn match_location(who: &VersionedLocatableAccount) -> Result<A::Type, ()> {
102 let account_id = match who {
104 VersionedLocatableAccount::V4 { location, account_id } if location.is_here() => {
105 &account_id.clone().try_into().map_err(|_| ())?
106 },
107 VersionedLocatableAccount::V5 { location, account_id } if location.is_here() => {
108 account_id
109 },
110 _ => return Err(()),
111 };
112 C::convert_location(account_id).ok_or(())
113 }
114 fn match_asset(asset: &VersionedLocatableAsset) -> Result<xcm::v5::Location, ()> {
115 match asset {
116 VersionedLocatableAsset::V4 { location, asset_id } if location.is_here() => {
117 asset_id.clone().try_into().map(|a: xcm::v5::AssetId| a.0).map_err(|_| ())
118 },
119 VersionedLocatableAsset::V5 { location, asset_id } if location.is_here() => {
120 Ok(asset_id.clone().0)
121 },
122 _ => Err(()),
123 }
124 }
125}
126
127#[cfg(feature = "runtime-benchmarks")]
128pub mod benchmarks {
129 use super::*;
130 use core::marker::PhantomData;
131 use pezframe_support::traits::Get;
132 use pezpallet_treasury::ArgumentsFactory as TreasuryArgumentsFactory;
133 use pezsp_core::ConstU8;
134
135 pub struct LocalPayArguments<PalletId = ConstU8<0>>(PhantomData<PalletId>);
142 impl<PalletId: Get<u8>>
143 TreasuryArgumentsFactory<VersionedLocatableAsset, VersionedLocatableAccount>
144 for LocalPayArguments<PalletId>
145 {
146 fn create_asset_kind(seed: u32) -> VersionedLocatableAsset {
147 VersionedLocatableAsset::V5 {
148 location: Location::new(0, []),
149 asset_id: Location::new(
150 0,
151 [PalletInstance(PalletId::get()), GeneralIndex(seed.into())],
152 )
153 .into(),
154 }
155 }
156 fn create_beneficiary(seed: [u8; 32]) -> VersionedLocatableAccount {
157 VersionedLocatableAccount::V5 {
158 location: Location::new(0, []),
159 account_id: Location::new(0, [AccountId32 { network: None, id: seed }]),
160 }
161 }
162 }
163}