xcm_primitives/
origin_conversion.rs

1// Copyright 2019-2025 PureStake Inc.
2// This file is part of Moonbeam.
3
4// Moonbeam is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Moonbeam is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16
17use frame_support::traits::{ContainsPair, Get, OriginTrait};
18use sp_runtime::traits::TryConvert;
19use sp_std::{convert::TryInto, marker::PhantomData};
20use xcm::latest::{Asset, AssetId, Fungibility, Junction, Location, NetworkId};
21
22pub trait Reserve {
23	/// Returns assets reserve location.
24	fn reserve(asset: &Asset) -> Option<Location>;
25}
26
27/// Instructs how to convert a 20 byte accountId into a Location
28pub struct AccountIdToLocation<AccountId>(sp_std::marker::PhantomData<AccountId>);
29impl<AccountId> sp_runtime::traits::Convert<AccountId, Location> for AccountIdToLocation<AccountId>
30where
31	AccountId: Into<[u8; 20]>,
32{
33	fn convert(account: AccountId) -> Location {
34		Location {
35			parents: 0,
36			interior: [Junction::AccountKey20 {
37				network: None,
38				key: account.into(),
39			}]
40			.into(),
41		}
42	}
43}
44
45// Convert a local Origin (i.e., a signed 20 byte account Origin)  to a Multilocation
46pub struct SignedToAccountId20<Origin, AccountId, Network>(
47	sp_std::marker::PhantomData<(Origin, AccountId, Network)>,
48);
49impl<Origin: OriginTrait + Clone, AccountId: Into<[u8; 20]>, Network: Get<NetworkId>>
50	TryConvert<Origin, Location> for SignedToAccountId20<Origin, AccountId, Network>
51where
52	Origin::PalletsOrigin: From<frame_system::RawOrigin<AccountId>>
53		+ TryInto<frame_system::RawOrigin<AccountId>, Error = Origin::PalletsOrigin>,
54{
55	fn try_convert(o: Origin) -> Result<Location, Origin> {
56		o.try_with_caller(|caller| match caller.try_into() {
57			Ok(frame_system::RawOrigin::Signed(who)) => Ok(Junction::AccountKey20 {
58				key: who.into(),
59				network: Some(Network::get()),
60			}
61			.into()),
62			Ok(other) => Err(other.into()),
63			Err(other) => Err(other),
64		})
65	}
66}
67
68/// A `ContainsPair` implementation. Filters multi native assets whose
69/// reserve is same with `origin`.
70pub struct MultiNativeAsset<ReserveProvider>(PhantomData<ReserveProvider>);
71impl<ReserveProvider> ContainsPair<Asset, Location> for MultiNativeAsset<ReserveProvider>
72where
73	ReserveProvider: Reserve,
74{
75	fn contains(asset: &Asset, origin: &Location) -> bool {
76		if let Some(ref reserve) = ReserveProvider::reserve(asset) {
77			if reserve == origin {
78				return true;
79			}
80		}
81		false
82	}
83}
84
85// Provide reserve in relative path view
86// Self tokens are represeneted as Here
87pub struct RelativeReserveProvider;
88
89impl Reserve for RelativeReserveProvider {
90	fn reserve(asset: &Asset) -> Option<Location> {
91		let AssetId(location) = &asset.id;
92		if location.parents == 0
93			&& !matches!(location.first_interior(), Some(Junction::Parachain(_)))
94		{
95			Some(Location::here())
96		} else {
97			Some(location.chain_location())
98		}
99	}
100}
101
102/// This struct offers uses RelativeReserveProvider to output relative views of multilocations
103/// However, additionally accepts a Location that aims at representing the chain part
104/// (parent: 1, Parachain(paraId)) of the absolute representation of our chain.
105/// If a token reserve matches against this absolute view, we return  Some(Location::here())
106/// This helps users by preventing errors when they try to transfer a token through xtokens
107/// to our chain (either inserting the relative or the absolute value).
108pub struct AbsoluteAndRelativeReserve<AbsoluteMultiLocation>(PhantomData<AbsoluteMultiLocation>);
109impl<AbsoluteMultiLocation> Reserve for AbsoluteAndRelativeReserve<AbsoluteMultiLocation>
110where
111	AbsoluteMultiLocation: Get<Location>,
112{
113	fn reserve(asset: &Asset) -> Option<Location> {
114		RelativeReserveProvider::reserve(asset).map(|relative_reserve| {
115			if relative_reserve == AbsoluteMultiLocation::get() {
116				Location::here()
117			} else {
118				relative_reserve
119			}
120		})
121	}
122}
123
124/// Matches foreign assets from a given origin.
125/// Foreign assets are assets bridged from other consensus systems. i.e parents > 1.
126pub struct IsBridgedConcreteAssetFrom<Origin>(PhantomData<Origin>);
127impl<Origin> ContainsPair<Asset, Location> for IsBridgedConcreteAssetFrom<Origin>
128where
129	Origin: Get<Location>,
130{
131	fn contains(asset: &Asset, origin: &Location) -> bool {
132		let loc = Origin::get();
133		&loc == origin
134			&& matches!(
135				asset,
136				Asset {
137					id: AssetId(Location { parents: 2, .. }),
138					fun: Fungibility::Fungible(_)
139				},
140			)
141	}
142}