1use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
18use frame_support::traits::{
19 fungibles,
20 tokens::{PaymentStatus, Preservation},
21};
22use polkadot_runtime_common::impls::VersionedLocatableAsset;
23use sp_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> frame_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 VersionedLocatableAccount::V5 { location, account_id } if location.is_here() =>
107 account_id,
108 _ => return Err(()),
109 };
110 C::convert_location(account_id).ok_or(())
111 }
112 fn match_asset(asset: &VersionedLocatableAsset) -> Result<xcm::v5::Location, ()> {
113 match asset {
114 VersionedLocatableAsset::V4 { location, asset_id } if location.is_here() =>
115 asset_id.clone().try_into().map(|a: xcm::v5::AssetId| a.0).map_err(|_| ()),
116 VersionedLocatableAsset::V5 { location, asset_id } if location.is_here() =>
117 Ok(asset_id.clone().0),
118 _ => Err(()),
119 }
120 }
121}
122
123#[cfg(feature = "runtime-benchmarks")]
124pub mod benchmarks {
125 use super::*;
126 use core::marker::PhantomData;
127 use frame_support::traits::Get;
128 use pallet_treasury::ArgumentsFactory as TreasuryArgumentsFactory;
129 use sp_core::ConstU8;
130
131 pub struct LocalPayArguments<PalletId = ConstU8<0>>(PhantomData<PalletId>);
138 impl<PalletId: Get<u8>>
139 TreasuryArgumentsFactory<VersionedLocatableAsset, VersionedLocatableAccount>
140 for LocalPayArguments<PalletId>
141 {
142 fn create_asset_kind(seed: u32) -> VersionedLocatableAsset {
143 VersionedLocatableAsset::V5 {
144 location: Location::new(0, []),
145 asset_id: Location::new(
146 0,
147 [PalletInstance(PalletId::get()), GeneralIndex(seed.into())],
148 )
149 .into(),
150 }
151 }
152 fn create_beneficiary(seed: [u8; 32]) -> VersionedLocatableAccount {
153 VersionedLocatableAccount::V5 {
154 location: Location::new(0, []),
155 account_id: Location::new(0, [AccountId32 { network: None, id: seed }]),
156 }
157 }
158 }
159}