pallet_evm_precompile_xcm_utils/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
20
21use fp_evm::PrecompileHandle;
22use frame_support::traits::ConstU32;
23use frame_support::{
24 dispatch::{GetDispatchInfo, PostDispatchInfo},
25 traits::OriginTrait,
26};
27use pallet_evm::AddressMapping;
28use parity_scale_codec::{Decode, DecodeLimit, MaxEncodedLen};
29use precompile_utils::precompile_set::SelectorFilter;
30use precompile_utils::prelude::*;
31use sp_core::{H160, U256};
32use sp_runtime::traits::Dispatchable;
33use sp_std::boxed::Box;
34use sp_std::marker::PhantomData;
35use sp_std::vec;
36use sp_std::vec::Vec;
37use sp_weights::Weight;
38use xcm::{latest::prelude::*, VersionedXcm, MAX_XCM_DECODE_DEPTH};
39use xcm_executor::traits::ConvertOrigin;
40use xcm_executor::traits::WeightBounds;
41use xcm_executor::traits::WeightTrader;
42
43use xcm_primitives::DEFAULT_PROOF_SIZE;
44
45pub type XcmOriginOf<XcmConfig> =
46 <<XcmConfig as xcm_executor::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin;
47pub type XcmAccountIdOf<XcmConfig> =
48 <<<XcmConfig as xcm_executor::Config>::RuntimeCall as Dispatchable>
49 ::RuntimeOrigin as OriginTrait>::AccountId;
50
51pub type CallOf<Runtime> = <Runtime as pallet_xcm::Config>::RuntimeCall;
52pub const XCM_SIZE_LIMIT: u32 = 2u32.pow(16);
53type GetXcmSizeLimit = ConstU32<XCM_SIZE_LIMIT>;
54
55#[cfg(test)]
56mod mock;
57#[cfg(test)]
58mod tests;
59
60#[derive(Debug)]
61pub struct AllExceptXcmExecute<Runtime, XcmConfig>(PhantomData<(Runtime, XcmConfig)>);
62
63impl<Runtime, XcmConfig> SelectorFilter for AllExceptXcmExecute<Runtime, XcmConfig>
64where
65 Runtime: pallet_evm::Config + frame_system::Config + pallet_xcm::Config,
66 XcmOriginOf<XcmConfig>: OriginTrait,
67 XcmAccountIdOf<XcmConfig>: Into<H160>,
68 XcmConfig: xcm_executor::Config,
69 <Runtime as frame_system::Config>::RuntimeCall:
70 Dispatchable<PostInfo = PostDispatchInfo> + Decode + GetDispatchInfo,
71 <<Runtime as frame_system::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin:
72 From<Option<Runtime::AccountId>>,
73 <Runtime as frame_system::Config>::RuntimeCall: From<pallet_xcm::Call<Runtime>>,
74 <Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
75{
76 fn is_allowed(_caller: H160, selector: Option<u32>) -> bool {
77 match selector {
78 None => true,
79 Some(selector) => {
80 !XcmUtilsPrecompileCall::<Runtime, XcmConfig>::xcm_execute_selectors()
81 .contains(&selector)
82 }
83 }
84 }
85
86 fn description() -> String {
87 "Allowed for all callers for all selectors except 'execute'".into()
88 }
89}
90
91pub struct XcmUtilsPrecompile<Runtime, XcmConfig>(PhantomData<(Runtime, XcmConfig)>);
93
94#[precompile_utils::precompile]
95impl<Runtime, XcmConfig> XcmUtilsPrecompile<Runtime, XcmConfig>
96where
97 Runtime: pallet_evm::Config + frame_system::Config + pallet_xcm::Config,
98 XcmOriginOf<XcmConfig>: OriginTrait,
99 XcmAccountIdOf<XcmConfig>: Into<H160>,
100 XcmConfig: xcm_executor::Config,
101 <Runtime as frame_system::Config>::RuntimeCall:
102 Dispatchable<PostInfo = PostDispatchInfo> + Decode + GetDispatchInfo,
103 <<Runtime as frame_system::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin:
104 From<Option<Runtime::AccountId>>,
105 <Runtime as frame_system::Config>::RuntimeCall: From<pallet_xcm::Call<Runtime>>,
106 <Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
107{
108 #[precompile::public("multilocationToAddress((uint8,bytes[]))")]
109 #[precompile::view]
110 fn multilocation_to_address(
111 handle: &mut impl PrecompileHandle,
112 location: Location,
113 ) -> EvmResult<Address> {
114 handle.record_db_read::<Runtime>(32 + Location::max_encoded_len())?;
117
118 let origin =
119 XcmConfig::OriginConverter::convert_origin(location, OriginKind::SovereignAccount)
120 .map_err(|_| {
121 RevertReason::custom("Failed multilocation conversion").in_field("location")
122 })?;
123
124 let account: H160 = origin
125 .into_signer()
126 .ok_or(
127 RevertReason::custom("Failed multilocation conversion").in_field("multilocation"),
128 )?
129 .into();
130 Ok(Address(account))
131 }
132
133 #[precompile::public("getUnitsPerSecond((uint8,bytes[]))")]
134 #[precompile::view]
135 fn get_units_per_second(
136 handle: &mut impl PrecompileHandle,
137 location: Location,
138 ) -> EvmResult<U256> {
139 handle.record_db_read::<Runtime>(32 + Location::max_encoded_len())?;
142
143 let multiasset: xcm::latest::Asset = (location.clone(), u128::MAX).into();
146 let weight_per_second = 1_000_000_000_000u64;
147
148 let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
149
150 let ctx = XcmContext {
151 origin: Some(location),
152 message_id: XcmHash::default(),
153 topic: None,
154 };
155 let unused = trader
157 .buy_weight(
158 Weight::from_parts(weight_per_second, DEFAULT_PROOF_SIZE),
159 vec![multiasset.clone()].into(),
160 &ctx,
161 )
162 .map_err(|_| {
163 RevertReason::custom("Asset not supported as fee payment").in_field("multilocation")
164 })?;
165
166 if let Some(amount) = unused
168 .fungible
169 .get(&multiasset.id)
170 .map(|&value| u128::MAX.saturating_sub(value))
171 {
172 Ok(amount.into())
173 } else {
174 Err(revert(
175 "Weight was too expensive to be bought with this asset",
176 ))
177 }
178 }
179
180 #[precompile::public("weightMessage(bytes)")]
181 #[precompile::view]
182 fn weight_message(
183 _handle: &mut impl PrecompileHandle,
184 message: BoundedBytes<GetXcmSizeLimit>,
185 ) -> EvmResult<u64> {
186 let message: Vec<u8> = message.into();
187
188 let msg =
189 VersionedXcm::<<XcmConfig as xcm_executor::Config>::RuntimeCall>::decode_all_with_depth_limit(
190 MAX_XCM_DECODE_DEPTH,
191 &mut message.as_slice(),
192 )
193 .map(Xcm::<<XcmConfig as xcm_executor::Config>::RuntimeCall>::try_from);
194
195 let result = match msg {
196 Ok(Ok(mut x)) => XcmConfig::Weigher::weight(&mut x, Weight::MAX)
197 .map_err(|_| revert("failed weighting")),
198 _ => Err(RevertReason::custom("Failed decoding")
199 .in_field("message")
200 .into()),
201 };
202
203 Ok(result?.ref_time())
204 }
205
206 #[precompile::public("xcmExecute(bytes,uint64)")]
207 fn xcm_execute(
208 handle: &mut impl PrecompileHandle,
209 message: BoundedBytes<GetXcmSizeLimit>,
210 weight: u64,
211 ) -> EvmResult {
212 let message: Vec<u8> = message.into();
213
214 let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
215
216 let message: Vec<_> = message.to_vec();
217 let xcm = xcm::VersionedXcm::<CallOf<Runtime>>::decode_all_with_depth_limit(
218 xcm::MAX_XCM_DECODE_DEPTH,
219 &mut message.as_slice(),
220 )
221 .map_err(|_e| RevertReason::custom("Failed xcm decoding").in_field("message"))?;
222
223 let call = pallet_xcm::Call::<Runtime>::execute {
224 message: Box::new(xcm),
225 max_weight: Weight::from_parts(weight, DEFAULT_PROOF_SIZE),
226 };
227
228 RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
229
230 Ok(())
231 }
232
233 #[precompile::public("xcmSend((uint8,bytes[]),bytes)")]
234 fn xcm_send(
235 handle: &mut impl PrecompileHandle,
236 dest: Location,
237 message: BoundedBytes<GetXcmSizeLimit>,
238 ) -> EvmResult {
239 let message: Vec<u8> = message.into();
240
241 let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
242
243 let message: Vec<_> = message.to_vec();
244 let xcm = xcm::VersionedXcm::<()>::decode_all_with_depth_limit(
245 xcm::MAX_XCM_DECODE_DEPTH,
246 &mut message.as_slice(),
247 )
248 .map_err(|_e| RevertReason::custom("Failed xcm decoding").in_field("message"))?;
249
250 let call = pallet_xcm::Call::<Runtime>::send {
251 dest: Box::new(dest.into()),
252 message: Box::new(xcm),
253 };
254
255 RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
256
257 Ok(())
258 }
259}