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

            
17
use frame_support::traits::{ContainsPair, Get, OriginTrait};
18
use sp_runtime::traits::TryConvert;
19
use sp_std::{convert::TryInto, marker::PhantomData};
20
use xcm::latest::{Asset, AssetId, Fungibility, Junction, Location, NetworkId};
21

            
22
pub 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
28
pub struct AccountIdToLocation<AccountId>(sp_std::marker::PhantomData<AccountId>);
29
impl<AccountId> sp_runtime::traits::Convert<AccountId, Location> for AccountIdToLocation<AccountId>
30
where
31
	AccountId: Into<[u8; 20]>,
32
{
33
127
	fn convert(account: AccountId) -> Location {
34
127
		Location {
35
127
			parents: 0,
36
127
			interior: [Junction::AccountKey20 {
37
127
				network: None,
38
127
				key: account.into(),
39
127
			}]
40
127
			.into(),
41
127
		}
42
127
	}
43
}
44

            
45
// Convert a local Origin (i.e., a signed 20 byte account Origin)  to a Multilocation
46
pub struct SignedToAccountId20<Origin, AccountId, Network>(
47
	sp_std::marker::PhantomData<(Origin, AccountId, Network)>,
48
);
49
impl<Origin: OriginTrait + Clone, AccountId: Into<[u8; 20]>, Network: Get<NetworkId>>
50
	TryConvert<Origin, Location> for SignedToAccountId20<Origin, AccountId, Network>
51
where
52
	Origin::PalletsOrigin: From<frame_system::RawOrigin<AccountId>>
53
		+ TryInto<frame_system::RawOrigin<AccountId>, Error = Origin::PalletsOrigin>,
54
{
55
140
	fn try_convert(o: Origin) -> Result<Location, Origin> {
56
140
		o.try_with_caller(|caller| match caller.try_into() {
57
137
			Ok(frame_system::RawOrigin::Signed(who)) => Ok(Junction::AccountKey20 {
58
137
				key: who.into(),
59
137
				network: Some(Network::get()),
60
137
			}
61
137
			.into()),
62
3
			Ok(other) => Err(other.into()),
63
			Err(other) => Err(other),
64
140
		})
65
140
	}
66
}
67

            
68
/// A `ContainsPair` implementation. Filters multi native assets whose
69
/// reserve is same with `origin`.
70
pub struct MultiNativeAsset<ReserveProvider>(PhantomData<ReserveProvider>);
71
impl<ReserveProvider> ContainsPair<Asset, Location> for MultiNativeAsset<ReserveProvider>
72
where
73
	ReserveProvider: Reserve,
74
{
75
406
	fn contains(asset: &Asset, origin: &Location) -> bool {
76
406
		if let Some(ref reserve) = ReserveProvider::reserve(asset) {
77
406
			if reserve == origin {
78
321
				return true;
79
85
			}
80
		}
81
85
		false
82
406
	}
83
}
84

            
85
// Provide reserve in relative path view
86
// Self tokens are represeneted as Here
87
pub struct RelativeReserveProvider;
88

            
89
impl Reserve for RelativeReserveProvider {
90
11104
	fn reserve(asset: &Asset) -> Option<Location> {
91
11104
		let AssetId(location) = &asset.id;
92
11104
		if location.parents == 0
93
1088
			&& !matches!(location.first_interior(), Some(Junction::Parachain(_)))
94
		{
95
1088
			Some(Location::here())
96
		} else {
97
10016
			Some(location.chain_location())
98
		}
99
11104
	}
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).
108
pub struct AbsoluteAndRelativeReserve<AbsoluteMultiLocation>(PhantomData<AbsoluteMultiLocation>);
109
impl<AbsoluteMultiLocation> Reserve for AbsoluteAndRelativeReserve<AbsoluteMultiLocation>
110
where
111
	AbsoluteMultiLocation: Get<Location>,
112
{
113
368
	fn reserve(asset: &Asset) -> Option<Location> {
114
368
		RelativeReserveProvider::reserve(asset).map(|relative_reserve| {
115
368
			if relative_reserve == AbsoluteMultiLocation::get() {
116
				Location::here()
117
			} else {
118
368
				relative_reserve
119
			}
120
368
		})
121
368
	}
122
}
123

            
124
/// Matches foreign assets from a given origin.
125
/// Foreign assets are assets bridged from other consensus systems. i.e parents > 1.
126
pub struct IsBridgedConcreteAssetFrom<Origin>(PhantomData<Origin>);
127
impl<Origin> ContainsPair<Asset, Location> for IsBridgedConcreteAssetFrom<Origin>
128
where
129
	Origin: Get<Location>,
130
{
131
131
	fn contains(asset: &Asset, origin: &Location) -> bool {
132
131
		let loc = Origin::get();
133
131
		&loc == origin
134
			&& matches!(
135
				asset,
136
				Asset {
137
					id: AssetId(Location { parents: 2, .. }),
138
					fun: Fungibility::Fungible(_)
139
				},
140
			)
141
131
	}
142
}