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