referrerpolicy=no-referrer-when-downgrade

staging_xcm_builder/
test_utils.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot 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// Polkadot 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 Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17// Shared test utilities and implementations for the XCM Builder.
18
19use alloc::vec::Vec;
20use core::fmt::Debug;
21use frame_support::{
22	parameter_types,
23	traits::{Contains, CrateVersion, PalletInfoData, PalletsInfoAccess},
24};
25pub use xcm::latest::{prelude::*, Weight};
26use xcm_executor::traits::{ClaimAssets, DropAssets, VersionChangeNotifier};
27pub use xcm_executor::{
28	traits::{
29		AssetExchange, AssetLock, ConvertOrigin, Enact, LockError, OnResponse, TransactAsset,
30	},
31	AssetsInHolding, Config,
32};
33
34parameter_types! {
35	pub static SubscriptionRequests: Vec<(Location, Option<(QueryId, Weight)>)> = vec![];
36	pub static MaxAssetsIntoHolding: u32 = 4;
37}
38
39pub struct TestSubscriptionService;
40
41impl VersionChangeNotifier for TestSubscriptionService {
42	fn start(
43		location: &Location,
44		query_id: QueryId,
45		max_weight: Weight,
46		_context: &XcmContext,
47	) -> XcmResult {
48		let mut r = SubscriptionRequests::get();
49		r.push((location.clone(), Some((query_id, max_weight))));
50		SubscriptionRequests::set(r);
51		Ok(())
52	}
53	fn stop(location: &Location, _context: &XcmContext) -> XcmResult {
54		let mut r = SubscriptionRequests::get();
55		r.retain(|(l, _q)| l != location);
56		r.push((location.clone(), None));
57		SubscriptionRequests::set(r);
58		Ok(())
59	}
60	fn is_subscribed(location: &Location) -> bool {
61		let r = SubscriptionRequests::get();
62		r.iter().any(|(l, q)| l == location && q.is_some())
63	}
64}
65
66pub struct TestHolding(AssetsInHolding);
67impl Clone for TestHolding {
68	fn clone(&self) -> Self {
69		TestHolding(AssetsInHolding {
70			fungible: self
71				.0
72				.fungible
73				.iter()
74				.map(|(id, accounting)| (id.clone(), accounting.unsafe_clone()))
75				.collect(),
76			non_fungible: self.0.non_fungible.clone(),
77		})
78	}
79}
80
81impl Debug for TestHolding {
82	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83		f.debug_struct("TestHolding")
84			.field("fungible_assets", &self.0.fungible_assets_iter().collect::<Vec<_>>())
85			.field("non_fungible_assets", &self.0.non_fungible_assets_iter().collect::<Vec<_>>())
86			.finish()
87	}
88}
89
90impl PartialEq for TestHolding {
91	fn eq(&self, other: &Self) -> bool {
92		let self_fungible: Vec<_> = self.0.fungible_assets_iter().collect();
93		let other_fungible: Vec<_> = other.0.fungible_assets_iter().collect();
94		let self_non_fungible: Vec<_> = self.0.non_fungible_assets_iter().collect();
95		let other_non_fungible: Vec<_> = other.0.non_fungible_assets_iter().collect();
96
97		self_fungible == other_fungible && self_non_fungible == other_non_fungible
98	}
99}
100
101parameter_types! {
102	pub static TrappedAssets: Vec<(Location, TestHolding)> = vec![];
103}
104
105pub struct TestAssetTrap();
106
107impl DropAssets for TestAssetTrap {
108	fn drop_assets(origin: &Location, assets: AssetsInHolding, _context: &XcmContext) -> Weight {
109		let mut t: Vec<(Location, TestHolding)> = TrappedAssets::get();
110		t.push((origin.clone(), TestHolding(assets)));
111		TrappedAssets::set(t);
112		Weight::from_parts(5, 5)
113	}
114}
115
116impl ClaimAssets for TestAssetTrap {
117	fn claim_assets(
118		origin: &Location,
119		ticket: &Location,
120		what: &Assets,
121		_context: &XcmContext,
122	) -> Option<AssetsInHolding> {
123		let mut t: Vec<(Location, TestHolding)> = TrappedAssets::get();
124		if let (0, [GeneralIndex(i)]) = ticket.unpack() {
125			if let Some((l, a)) = t.get(*i as usize) {
126				for asset in what.inner() {
127					if l == origin && a.0.contains_asset(asset) {
128						let (_, claimed) = t.swap_remove(*i as usize);
129						TrappedAssets::set(t);
130						return Some(claimed.0);
131					}
132				}
133			}
134		}
135		None
136	}
137}
138
139pub struct TestAssetExchanger;
140
141impl AssetExchange for TestAssetExchanger {
142	fn exchange_asset(
143		_origin: Option<&Location>,
144		_give: AssetsInHolding,
145		_want: &Assets,
146		_maximal: bool,
147	) -> Result<AssetsInHolding, AssetsInHolding> {
148		Ok(AssetsInHolding::new())
149	}
150
151	fn quote_exchange_price(give: &Assets, _want: &Assets, _maximal: bool) -> Option<Assets> {
152		Some(give.clone())
153	}
154}
155
156pub struct TestPalletsInfo;
157impl PalletsInfoAccess for TestPalletsInfo {
158	fn count() -> usize {
159		2
160	}
161	fn infos() -> Vec<PalletInfoData> {
162		vec![
163			PalletInfoData {
164				index: 0,
165				name: "System",
166				module_name: "pallet_system",
167				crate_version: CrateVersion { major: 1, minor: 10, patch: 1 },
168			},
169			PalletInfoData {
170				index: 1,
171				name: "Balances",
172				module_name: "pallet_balances",
173				crate_version: CrateVersion { major: 1, minor: 42, patch: 69 },
174			},
175		]
176	}
177}
178
179pub struct TestUniversalAliases;
180impl Contains<(Location, Junction)> for TestUniversalAliases {
181	fn contains(aliases: &(Location, Junction)) -> bool {
182		&aliases.0 == &Here.into_location() && &aliases.1 == &GlobalConsensus(ByGenesis([0; 32]))
183	}
184}
185
186parameter_types! {
187	pub static LockedAssets: Vec<(Location, Asset)> = vec![];
188}
189
190pub struct TestLockTicket(Location, Asset);
191impl Enact for TestLockTicket {
192	fn enact(self) -> Result<(), LockError> {
193		let mut locked_assets = LockedAssets::get();
194		locked_assets.push((self.0, self.1));
195		LockedAssets::set(locked_assets);
196		Ok(())
197	}
198}
199pub struct TestUnlockTicket(Location, Asset);
200impl Enact for TestUnlockTicket {
201	fn enact(self) -> Result<(), LockError> {
202		let mut locked_assets = LockedAssets::get();
203		if let Some((idx, _)) = locked_assets
204			.iter()
205			.enumerate()
206			.find(|(_, (origin, asset))| origin == &self.0 && asset == &self.1)
207		{
208			locked_assets.remove(idx);
209		}
210		LockedAssets::set(locked_assets);
211		Ok(())
212	}
213}
214pub struct TestReduceTicket;
215impl Enact for TestReduceTicket {
216	fn enact(self) -> Result<(), LockError> {
217		Ok(())
218	}
219}
220
221pub struct TestAssetLocker;
222impl AssetLock for TestAssetLocker {
223	type LockTicket = TestLockTicket;
224	type UnlockTicket = TestUnlockTicket;
225	type ReduceTicket = TestReduceTicket;
226
227	fn prepare_lock(
228		unlocker: Location,
229		asset: Asset,
230		_owner: Location,
231	) -> Result<TestLockTicket, LockError> {
232		Ok(TestLockTicket(unlocker, asset))
233	}
234
235	fn prepare_unlock(
236		unlocker: Location,
237		asset: Asset,
238		_owner: Location,
239	) -> Result<TestUnlockTicket, LockError> {
240		Ok(TestUnlockTicket(unlocker, asset))
241	}
242
243	fn note_unlockable(
244		_locker: Location,
245		_asset: Asset,
246		_owner: Location,
247	) -> Result<(), LockError> {
248		Ok(())
249	}
250
251	fn prepare_reduce_unlockable(
252		_locker: Location,
253		_asset: Asset,
254		_owner: Location,
255	) -> Result<TestReduceTicket, LockError> {
256		Ok(TestReduceTicket)
257	}
258}