referrerpolicy=no-referrer-when-downgrade

snowbridge_test_utils/
mock_xcm.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
3
4use codec::Encode;
5use core::cell::RefCell;
6use xcm::prelude::*;
7use xcm_executor::{
8	traits::{FeeManager, FeeReason, TransactAsset},
9	AssetsInHolding,
10};
11
12thread_local! {
13	pub static IS_WAIVED: RefCell<Vec<FeeReason>> = RefCell::new(vec![]);
14	pub static SENDER_OVERRIDE: RefCell<Option<(
15		fn(
16			&mut Option<Location>,
17			&mut Option<Xcm<()>>,
18		) -> Result<(Xcm<()>, Assets), SendError>,
19		fn(
20			Xcm<()>,
21		) -> Result<XcmHash, SendError>,
22	)>> = RefCell::new(None);
23	pub static CHARGE_FEES_OVERRIDE: RefCell<Option<
24		fn(Location, Assets) -> xcm::latest::Result
25	>> = RefCell::new(None);
26}
27
28#[allow(dead_code)]
29pub fn set_fee_waiver(waived: Vec<FeeReason>) {
30	IS_WAIVED.with(|l| l.replace(waived));
31}
32
33#[allow(dead_code)]
34pub fn set_sender_override(
35	validate: fn(&mut Option<Location>, &mut Option<Xcm<()>>) -> SendResult<Xcm<()>>,
36	deliver: fn(Xcm<()>) -> Result<XcmHash, SendError>,
37) {
38	SENDER_OVERRIDE.with(|x| x.replace(Some((validate, deliver))));
39}
40
41#[allow(dead_code)]
42pub fn clear_sender_override() {
43	SENDER_OVERRIDE.with(|x| x.replace(None));
44}
45
46#[allow(dead_code)]
47pub fn set_charge_fees_override(charge_fees: fn(Location, Assets) -> xcm::latest::Result) {
48	CHARGE_FEES_OVERRIDE.with(|x| x.replace(Some(charge_fees)));
49}
50
51#[allow(dead_code)]
52pub fn clear_charge_fees_override() {
53	CHARGE_FEES_OVERRIDE.with(|x| x.replace(None));
54}
55
56/// Mock XCM sender with an overridable `validate` and `deliver` function.
57pub struct MockXcmSender;
58
59impl SendXcm for MockXcmSender {
60	type Ticket = Xcm<()>;
61
62	fn validate(
63		dest: &mut Option<Location>,
64		xcm: &mut Option<Xcm<()>>,
65	) -> SendResult<Self::Ticket> {
66		let r: SendResult<Self::Ticket> = SENDER_OVERRIDE.with(|s| {
67			if let Some((ref f, _)) = &*s.borrow() {
68				f(dest, xcm)
69			} else {
70				Ok((xcm.take().unwrap(), Assets::default()))
71			}
72		});
73		r
74	}
75
76	fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
77		let r: Result<XcmHash, SendError> = SENDER_OVERRIDE.with(|s| {
78			if let Some((_, ref f)) = &*s.borrow() {
79				f(ticket)
80			} else {
81				let hash = ticket.using_encoded(sp_core::hashing::blake2_256);
82				Ok(hash)
83			}
84		});
85		r
86	}
87}
88
89/// Mock XCM transactor that always succeeds
90pub struct SuccessfulTransactor;
91impl TransactAsset for SuccessfulTransactor {
92	fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
93		Ok(())
94	}
95
96	fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
97		Ok(())
98	}
99
100	fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult {
101		Ok(())
102	}
103
104	fn withdraw_asset(
105		_what: &Asset,
106		_who: &Location,
107		_context: Option<&XcmContext>,
108	) -> Result<AssetsInHolding, XcmError> {
109		Ok(AssetsInHolding::default())
110	}
111
112	fn internal_transfer_asset(
113		_what: &Asset,
114		_from: &Location,
115		_to: &Location,
116		_context: &XcmContext,
117	) -> Result<AssetsInHolding, XcmError> {
118		Ok(AssetsInHolding::default())
119	}
120}
121
122pub enum Weightless {}
123impl PreparedMessage for Weightless {
124	fn weight_of(&self) -> Weight {
125		unreachable!();
126	}
127}
128
129/// Mock the XCM executor with an overridable `charge_fees` function.
130pub struct MockXcmExecutor;
131impl<C> ExecuteXcm<C> for MockXcmExecutor {
132	type Prepared = Weightless;
133	fn prepare(_: Xcm<C>, _: Weight) -> Result<Self::Prepared, InstructionError> {
134		unreachable!()
135	}
136	fn execute(_: impl Into<Location>, _: Self::Prepared, _: &mut XcmHash, _: Weight) -> Outcome {
137		unreachable!()
138	}
139	fn charge_fees(location: impl Into<Location>, assets: Assets) -> xcm::latest::Result {
140		let r: xcm::latest::Result = CHARGE_FEES_OVERRIDE.with(|s| {
141			if let Some(ref f) = &*s.borrow() {
142				f(location.into(), assets)
143			} else {
144				Ok(())
145			}
146		});
147		r
148	}
149}
150
151impl FeeManager for MockXcmExecutor {
152	fn is_waived(_: Option<&Location>, r: FeeReason) -> bool {
153		IS_WAIVED.with(|l| l.borrow().contains(&r))
154	}
155
156	fn handle_fee(_: Assets, _: Option<&XcmContext>, _: FeeReason) {}
157}