teyrchains_common/
pay.rs

1// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
2// This file is part of Pezcumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17use 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/// Versioned locatable account type which contains both an XCM `location` and `account_id` to
28/// identify an account which exists on some chain.
29#[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
47/// Pay on the local chain with `fungibles` implementation if the beneficiary and the asset are both
48/// local.
49pub 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		// We use `QueryId::MAX` as a constant identifier for these payments since they are always
77		// processed immediately and successfully on the local chain. The `QueryId` type is used to
78		// maintain compatibility with XCM payment implementations.
79		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		// only applicable for the local accounts
103		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	/// Provides factory methods for the `AssetKind` and the `Beneficiary` that are applicable for
136	/// the payout made by [`LocalPay`].
137	///
138	/// ### Parameters:
139	/// - `PalletId`: The ID of the assets registry pezpallet.
140	/// - `AssetId`: The ID of the asset that will be created for the benchmark within `PalletId`.
141	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}