1#![cfg_attr(not(feature = "std"), no_std)]
20
21use astar_primitives::xcm::XCM_SIZE_LIMIT;
22use fp_evm::PrecompileHandle;
23use frame_support::{
24 dispatch::{GetDispatchInfo, PostDispatchInfo},
25 pallet_prelude::Weight,
26 traits::{ConstU32, Get},
27};
28use sp_runtime::traits::{Dispatchable, MaybeEquivalence};
29type GetXcmSizeLimit = ConstU32<XCM_SIZE_LIMIT>;
30
31use pallet_evm::AddressMapping;
32use parity_scale_codec::DecodeLimit;
33use sp_core::{H160, H256, U256};
34
35use sp_std::marker::PhantomData;
36use sp_std::prelude::*;
37
38use xcm::{latest::prelude::*, VersionedAsset, VersionedAssets, VersionedLocation};
39
40use pallet_evm_precompile_assets_erc20::AddressToAssetId;
41use precompile_utils::prelude::*;
42#[cfg(test)]
43mod mock;
44#[cfg(test)]
45mod tests;
46
47const NATIVE_ADDRESS: H160 = H160::zero();
49
50const DEFAULT_PROOF_SIZE: u64 = 1024 * 256;
52
53pub type XBalanceOf<Runtime> = <Runtime as orml_xtokens::Config>::Balance;
54
55pub struct GetMaxAssets<R>(PhantomData<R>);
56
57impl<R> Get<u32> for GetMaxAssets<R>
58where
59 R: orml_xtokens::Config,
60{
61 fn get() -> u32 {
62 <R as orml_xtokens::Config>::MaxAssetsForTransfer::get() as u32
63 }
64}
65
66pub struct XcmPrecompile<Runtime, C>(PhantomData<(Runtime, C)>);
68
69#[precompile_utils::precompile]
70#[precompile::test_concrete_types(mock::Runtime, mock::AssetIdConverter<mock::AssetId>)]
71impl<Runtime, C> XcmPrecompile<Runtime, C>
72where
73 Runtime: pallet_evm::Config
74 + pallet_xcm::Config
75 + orml_xtokens::Config
76 + pallet_assets::Config
77 + AddressToAssetId<<Runtime as pallet_assets::Config>::AssetId>,
78 <<Runtime as frame_system::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin:
79 From<Option<Runtime::AccountId>>,
80 <Runtime as frame_system::Config>::AccountId: Into<[u8; 32]>,
81 <Runtime as frame_system::Config>::RuntimeCall: From<pallet_xcm::Call<Runtime>>
82 + From<orml_xtokens::Call<Runtime>>
83 + Dispatchable<PostInfo = PostDispatchInfo>
84 + GetDispatchInfo,
85 XBalanceOf<Runtime>: TryFrom<U256> + Into<U256> + From<u128>,
86 <Runtime as orml_xtokens::Config>::CurrencyId:
87 From<<Runtime as pallet_assets::Config>::AssetId>,
88 C: MaybeEquivalence<Location, <Runtime as pallet_assets::Config>::AssetId>,
89 <Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
90{
91 #[precompile::public("assets_withdraw(address[],uint256[],bytes32,bool,uint256,uint256)")]
92 fn assets_withdraw_native_v1(
93 handle: &mut impl PrecompileHandle,
94 assets: BoundedVec<Address, GetMaxAssets<Runtime>>,
95 amounts: BoundedVec<U256, GetMaxAssets<Runtime>>,
96 recipient_account_id: H256,
97 is_relay: bool,
98 parachain_id: U256,
99 fee_index: U256,
100 ) -> EvmResult<bool> {
101 let beneficiary: Junction = Junction::AccountId32 {
102 network: None,
103 id: recipient_account_id.into(),
104 }
105 .into();
106 Self::assets_withdraw_v1_internal(
107 handle,
108 assets.into(),
109 amounts.into(),
110 beneficiary,
111 is_relay,
112 parachain_id,
113 fee_index,
114 )
115 }
116
117 #[precompile::public("assets_withdraw(address[],uint256[],address,bool,uint256,uint256)")]
118 fn assets_withdraw_evm_v1(
119 handle: &mut impl PrecompileHandle,
120 assets: BoundedVec<Address, GetMaxAssets<Runtime>>,
121 amounts: BoundedVec<U256, GetMaxAssets<Runtime>>,
122 recipient_account_id: Address,
123 is_relay: bool,
124 parachain_id: U256,
125 fee_index: U256,
126 ) -> EvmResult<bool> {
127 let beneficiary: Junction = Junction::AccountKey20 {
128 network: None,
129 key: recipient_account_id.0.to_fixed_bytes(),
130 }
131 .into();
132 Self::assets_withdraw_v1_internal(
133 handle,
134 assets.into(),
135 amounts.into(),
136 beneficiary,
137 is_relay,
138 parachain_id,
139 fee_index,
140 )
141 }
142
143 fn assets_withdraw_v1_internal(
144 handle: &mut impl PrecompileHandle,
145 assets: Vec<Address>,
146 amounts: Vec<U256>,
147 beneficiary: Junction,
148 is_relay: bool,
149 parachain_id: U256,
150 fee_index: U256,
151 ) -> EvmResult<bool> {
152 let assets = assets
154 .iter()
155 .cloned()
156 .filter_map(|address| {
157 Runtime::address_to_asset_id(address.into()).and_then(|x| C::convert_back(&x))
158 })
159 .collect::<Vec<Location>>();
160
161 let amounts = amounts
162 .into_iter()
163 .map(|x| x.try_into())
164 .collect::<Result<Vec<u128>, _>>()
165 .map_err(|_| revert("error converting amounts, maybe value too large"))?;
166
167 if assets.len() != amounts.len() || assets.is_empty() {
171 return Err(revert("Assets resolution failure."));
172 }
173
174 let parachain_id: u32 = parachain_id
175 .try_into()
176 .map_err(|_| revert("error converting parachain_id, maybe value too large"))?;
177
178 let fee_item: u32 = fee_index
179 .try_into()
180 .map_err(|_| revert("error converting fee_index, maybe value too large"))?;
181
182 let mut destination = if is_relay {
183 Location::parent()
184 } else {
185 Junctions::from(Junction::Parachain(parachain_id)).into_exterior(1)
186 };
187
188 destination
189 .push_interior(beneficiary)
190 .map_err(|_| revert("error building destination multilocation"))?;
191
192 let assets = assets
193 .iter()
194 .cloned()
195 .zip(amounts.iter().cloned())
196 .map(Into::into)
197 .collect::<Vec<Asset>>();
198
199 Self::ensure_dot_transfer_policy(&assets, &destination)?;
200
201 log::trace!(target: "xcm-precompile:assets_withdraw", "Processed arguments: assets {:?}, destination: {:?}", assets, destination);
202
203 let origin = Some(Runtime::AddressMapping::into_account_id(
205 handle.context().caller,
206 ))
207 .into();
208
209 let call = orml_xtokens::Call::<Runtime>::transfer_multiassets {
210 assets: Box::new(VersionedAssets::V5(assets.into())),
211 fee_item,
212 dest: Box::new(VersionedLocation::V5(destination)),
213 dest_weight_limit: WeightLimit::Unlimited,
214 };
215
216 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
218 Ok(true)
219 }
220
221 #[precompile::public("remote_transact(uint256,bool,address,uint256,bytes,uint64)")]
222 fn remote_transact_v1(
223 handle: &mut impl PrecompileHandle,
224 para_id: U256,
225 is_relay: bool,
226 fee_asset_addr: Address,
227 fee_amount: U256,
228 remote_call: UnboundedBytes,
229 transact_weight: u64,
230 ) -> EvmResult<bool> {
231 let para_id: u32 = para_id
233 .try_into()
234 .map_err(|_| revert("error converting para_id, maybe value too large"))?;
235
236 let fee_amount: u128 = fee_amount
237 .try_into()
238 .map_err(|_| revert("error converting fee_amount, maybe value too large"))?;
239
240 let remote_call: Vec<u8> = remote_call.into();
241
242 log::trace!(target: "xcm-precompile:remote_transact", "Raw arguments: para_id: {}, is_relay: {}, fee_asset_addr: {:?}, \
243 fee_amount: {:?}, remote_call: {:?}, transact_weight: {}",
244 para_id, is_relay, fee_asset_addr, fee_amount, remote_call, transact_weight);
245
246 let dest = if is_relay {
248 Location::parent()
249 } else {
250 Junctions::from(Junction::Parachain(para_id)).into_exterior(1)
251 };
252
253 let fee_asset = {
254 let address: H160 = fee_asset_addr.into();
255
256 if address == NATIVE_ADDRESS {
258 Here.into()
259 } else {
260 let fee_asset_id = Runtime::address_to_asset_id(address)
261 .ok_or(revert("Failed to resolve fee asset id from address"))?;
262 C::convert_back(&fee_asset_id).ok_or(revert(
263 "Failed to resolve fee asset multilocation from local id",
264 ))?
265 }
266 };
267
268 let context = <Runtime as pallet_xcm::Config>::UniversalLocation::get();
269 let fee_multilocation: Asset = (fee_asset, fee_amount).into();
270 let fee_multilocation = fee_multilocation
271 .reanchored(&dest, &context)
272 .map_err(|_| revert("Failed to reanchor fee asset"))?;
273
274 let xcm = Xcm(vec![
276 WithdrawAsset(fee_multilocation.clone().into()),
277 BuyExecution {
278 fees: fee_multilocation.clone().into(),
279 weight_limit: WeightLimit::Unlimited,
280 },
281 Transact {
282 origin_kind: OriginKind::SovereignAccount,
283 fallback_max_weight: Some(Weight::from_parts(transact_weight, DEFAULT_PROOF_SIZE)),
284 call: remote_call.into(),
285 },
286 ]);
287
288 log::trace!(target: "xcm-precompile:remote_transact", "Processed arguments: dest: {:?}, fee asset: {:?}, XCM: {:?}", dest, fee_multilocation, xcm);
289
290 let origin = Some(Runtime::AddressMapping::into_account_id(
292 handle.context().caller,
293 ))
294 .into();
295 let call = pallet_xcm::Call::<Runtime>::send {
296 dest: Box::new(dest.into()),
297 message: Box::new(xcm::VersionedXcm::V5(xcm)),
298 };
299
300 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
302
303 Ok(true)
304 }
305
306 fn assets_reserve_transfer_v1_internal(
307 handle: &mut impl PrecompileHandle,
308 assets: Vec<Address>,
309 amounts: Vec<U256>,
310 beneficiary: Junction,
311 is_relay: bool,
312 parachain_id: U256,
313 fee_item: U256,
314 ) -> EvmResult<bool> {
315 let assets: Vec<Location> = assets
316 .iter()
317 .cloned()
318 .filter_map(|address| {
319 let address: H160 = address.into();
320 if address == NATIVE_ADDRESS {
322 Some(Here.into())
323 } else {
324 Runtime::address_to_asset_id(address).and_then(|x| C::convert_back(&x))
325 }
326 })
327 .collect();
328
329 let amounts: Vec<u128> = amounts
330 .into_iter()
331 .map(|x| x.try_into())
332 .collect::<Result<Vec<u128>, _>>()
333 .map_err(|_| revert("error converting amounts, maybe value too large"))?;
334
335 if assets.len() != amounts.len() || assets.is_empty() {
339 return Err(revert("Assets resolution failure."));
340 }
341
342 let parachain_id: u32 = parachain_id
343 .try_into()
344 .map_err(|_| revert("error converting parachain_id, maybe value too large"))?;
345
346 let fee_item: u32 = fee_item
347 .try_into()
348 .map_err(|_| revert("error converting fee_index, maybe value too large"))?;
349
350 let mut destination = if is_relay {
352 Location::parent()
353 } else {
354 Junctions::from(Junction::Parachain(parachain_id)).into_exterior(1)
355 };
356
357 destination
358 .push_interior(beneficiary)
359 .map_err(|_| revert("error building destination multilocation"))?;
360
361 let assets = assets
362 .iter()
363 .cloned()
364 .zip(amounts.iter().cloned())
365 .map(Into::into)
366 .collect::<Vec<Asset>>();
367
368 Self::ensure_dot_transfer_policy(&assets, &destination)?;
369 log::trace!(target: "xcm-precompile:assets_reserve_transfer", "Processed arguments: assets {:?}, destination: {:?}", assets, destination);
370
371 let origin = Some(Runtime::AddressMapping::into_account_id(
373 handle.context().caller,
374 ))
375 .into();
376
377 let call = orml_xtokens::Call::<Runtime>::transfer_multiassets {
378 assets: Box::new(VersionedAssets::V5(assets.into())),
379 fee_item,
380 dest: Box::new(VersionedLocation::V5(destination)),
381 dest_weight_limit: WeightLimit::Unlimited,
382 };
383
384 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
386
387 Ok(true)
388 }
389
390 #[precompile::public(
391 "assets_reserve_transfer(address[],uint256[],bytes32,bool,uint256,uint256)"
392 )]
393 fn assets_reserve_transfer_native_v1(
394 handle: &mut impl PrecompileHandle,
395 assets: BoundedVec<Address, GetMaxAssets<Runtime>>,
396 amounts: BoundedVec<U256, GetMaxAssets<Runtime>>,
397 recipient_account_id: H256,
398 is_relay: bool,
399 parachain_id: U256,
400 fee_index: U256,
401 ) -> EvmResult<bool> {
402 let beneficiary: Junction = Junction::AccountId32 {
403 network: None,
404 id: recipient_account_id.into(),
405 }
406 .into();
407 Self::assets_reserve_transfer_v1_internal(
408 handle,
409 assets.into(),
410 amounts.into(),
411 beneficiary,
412 is_relay,
413 parachain_id,
414 fee_index,
415 )
416 }
417
418 #[precompile::public(
419 "assets_reserve_transfer(address[],uint256[],address,bool,uint256,uint256)"
420 )]
421 fn assets_reserve_transfer_evm_v1(
422 handle: &mut impl PrecompileHandle,
423 assets: BoundedVec<Address, GetMaxAssets<Runtime>>,
424 amounts: BoundedVec<U256, GetMaxAssets<Runtime>>,
425 recipient_account_id: Address,
426 is_relay: bool,
427 parachain_id: U256,
428 fee_index: U256,
429 ) -> EvmResult<bool> {
430 let beneficiary: Junction = Junction::AccountKey20 {
431 network: None,
432 key: recipient_account_id.0.to_fixed_bytes(),
433 }
434 .into();
435 Self::assets_reserve_transfer_v1_internal(
436 handle,
437 assets.into(),
438 amounts.into(),
439 beneficiary,
440 is_relay,
441 parachain_id,
442 fee_index,
443 )
444 }
445
446 #[precompile::public("send_xcm((uint8,bytes[]),bytes)")]
447 fn send_xcm(
448 handle: &mut impl PrecompileHandle,
449 dest: Location,
450 xcm_call: BoundedBytes<GetXcmSizeLimit>,
451 ) -> EvmResult<bool> {
452 let dest: Location = dest.into();
454 let xcm_call: Vec<u8> = xcm_call.into();
455
456 log::trace!(target:"xcm-precompile::send_xcm", "Raw arguments: dest: {:?}, xcm_call: {:?}", dest, xcm_call);
457
458 let xcm = xcm::VersionedXcm::<()>::decode_all_with_depth_limit(
459 xcm::MAX_XCM_DECODE_DEPTH,
460 &mut xcm_call.as_slice(),
461 )
462 .map_err(|_| revert("Failed to decode xcm instructions"))?;
463
464 let origin = Some(Runtime::AddressMapping::into_account_id(
466 handle.context().caller,
467 ))
468 .into();
469 let call = pallet_xcm::Call::<Runtime>::send {
470 dest: Box::new(dest.into()),
471 message: Box::new(xcm),
472 };
473 log::trace!(target: "xcm-send_xcm", "Processed arguments: XCM call: {:?}", call);
474 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
476
477 Ok(true)
478 }
479
480 #[precompile::public("transfer(address,uint256,(uint8,bytes[]),(uint64,uint64))")]
481 fn transfer(
482 handle: &mut impl PrecompileHandle,
483 currency_address: Address,
484 amount_of_tokens: U256,
485 destination: Location,
486 weight: WeightV2,
487 ) -> EvmResult<bool> {
488 let amount_of_tokens: u128 = amount_of_tokens
490 .try_into()
491 .map_err(|_| revert("error converting amount_of_tokens, maybe value too large"))?;
492
493 let dest_weight_limit = if weight.is_zero() {
494 WeightLimit::Unlimited
495 } else {
496 WeightLimit::Limited(weight.get_weight())
497 };
498
499 let call = {
500 if currency_address == Address::from(NATIVE_ADDRESS) {
501 log::trace!(target: "xcm-precompile::transfer", "Raw arguments: currency_address: {:?} (this is native token), amount_of_tokens: {:?}, destination: {:?}, \
502 weight: {:?}",
503 currency_address, amount_of_tokens, destination, weight );
504
505 orml_xtokens::Call::<Runtime>::transfer_multiasset {
506 asset: Box::new(VersionedAsset::V5(
507 (Location::here(), amount_of_tokens).into(),
508 )),
509 dest: Box::new(VersionedLocation::V5(destination)),
510 dest_weight_limit,
511 }
512 } else {
513 let asset_id = Runtime::address_to_asset_id(currency_address.into())
514 .ok_or(revert("Failed to resolve fee asset id from address"))?;
515
516 log::trace!(target: "xcm-precompile::transfer", "Raw arguments: currency_address: {:?}, amount_of_tokens: {:?}, destination: {:?}, \
517 weight: {:?}, calculated asset_id: {:?}",
518 currency_address, amount_of_tokens, destination, weight, asset_id);
519
520 orml_xtokens::Call::<Runtime>::transfer {
521 currency_id: asset_id.into(),
522 amount: amount_of_tokens.into(),
523 dest: Box::new(VersionedLocation::V5(destination)),
524 dest_weight_limit,
525 }
526 }
527 };
528
529 let origin = Some(Runtime::AddressMapping::into_account_id(
530 handle.context().caller,
531 ))
532 .into();
533
534 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
536
537 Ok(true)
538 }
539
540 #[precompile::public(
541 "transfer_with_fee(address,uint256,uint256,(uint8,bytes[]),(uint64,uint64))"
542 )]
543 fn transfer_with_fee(
544 handle: &mut impl PrecompileHandle,
545 currency_address: Address,
546 amount_of_tokens: U256,
547 fee: U256,
548 destination: Location,
549 weight: WeightV2,
550 ) -> EvmResult<bool> {
551 let amount_of_tokens: u128 = amount_of_tokens
553 .try_into()
554 .map_err(|_| revert("error converting amount_of_tokens, maybe value too large"))?;
555 let fee: u128 = fee.try_into().map_err(|_| revert("can't convert fee"))?;
556
557 let dest_weight_limit = if weight.is_zero() {
558 WeightLimit::Unlimited
559 } else {
560 WeightLimit::Limited(weight.get_weight())
561 };
562
563 let call = {
564 if currency_address == Address::from(NATIVE_ADDRESS) {
565 log::trace!(target: "xcm-precompile::transfer_with_fee", "Raw arguments: currency_address: {:?} (this is native token), amount_of_tokens: {:?}, destination: {:?}, \
566 weight: {:?}, fee {:?}",
567 currency_address, amount_of_tokens, destination, weight, fee );
568
569 orml_xtokens::Call::<Runtime>::transfer_multiasset_with_fee {
570 asset: Box::new(VersionedAsset::V5(
571 (Location::here(), amount_of_tokens).into(),
572 )),
573 fee: Box::new(VersionedAsset::V5((Location::here(), fee).into())),
574 dest: Box::new(VersionedLocation::V5(destination)),
575 dest_weight_limit,
576 }
577 } else {
578 let asset_id = Runtime::address_to_asset_id(currency_address.into())
579 .ok_or(revert("Failed to resolve fee asset id from address"))?;
580
581 log::trace!(target: "xcm-precompile::transfer_with_fee", "Raw arguments: currency_address: {:?}, amount_of_tokens: {:?}, destination: {:?}, \
582 weight: {:?}, calculated asset_id: {:?}, fee: {:?}",
583 currency_address, amount_of_tokens, destination, weight, asset_id, fee);
584
585 orml_xtokens::Call::<Runtime>::transfer_with_fee {
586 currency_id: asset_id.into(),
587 amount: amount_of_tokens.into(),
588 fee: fee.into(),
589 dest: Box::new(VersionedLocation::V5(destination)),
590 dest_weight_limit,
591 }
592 }
593 };
594
595 let origin = Some(Runtime::AddressMapping::into_account_id(
596 handle.context().caller,
597 ))
598 .into();
599
600 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
602
603 Ok(true)
604 }
605
606 #[precompile::public(
607 "transfer_multiasset((uint8,bytes[]),uint256,(uint8,bytes[]),(uint64,uint64))"
608 )]
609 fn transfer_multiasset(
610 handle: &mut impl PrecompileHandle,
611 asset_location: Location,
612 amount_of_tokens: U256,
613 destination: Location,
614 weight: WeightV2,
615 ) -> EvmResult<bool> {
616 let amount_of_tokens: u128 = amount_of_tokens
618 .try_into()
619 .map_err(|_| revert("error converting amount_of_tokens, maybe value too large"))?;
620
621 let dest_weight_limit = if weight.is_zero() {
622 WeightLimit::Unlimited
623 } else {
624 WeightLimit::Limited(weight.get_weight())
625 };
626
627 log::trace!(target: "xcm-precompile::transfer_multiasset", "Raw arguments: asset_location: {:?}, amount_of_tokens: {:?}, destination: {:?}, \
628 weight: {:?}",
629 asset_location, amount_of_tokens, destination, weight);
630
631 let call = orml_xtokens::Call::<Runtime>::transfer_multiasset {
632 asset: Box::new(VersionedAsset::V5(
633 (asset_location, amount_of_tokens).into(),
634 )),
635 dest: Box::new(VersionedLocation::V5(destination)),
636 dest_weight_limit,
637 };
638
639 let origin = Some(Runtime::AddressMapping::into_account_id(
640 handle.context().caller,
641 ))
642 .into();
643
644 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
646
647 Ok(true)
648 }
649
650 #[precompile::public(
651 "transfer_multiasset_with_fee((uint8,bytes[]),uint256,uint256,(uint8,bytes[]),(uint64,uint64))"
652 )]
653 fn transfer_multiasset_with_fee(
654 handle: &mut impl PrecompileHandle,
655 asset_location: Location,
656 amount_of_tokens: U256,
657 fee: U256,
658 destination: Location,
659 weight: WeightV2,
660 ) -> EvmResult<bool> {
661 let amount_of_tokens: u128 = amount_of_tokens
663 .try_into()
664 .map_err(|_| revert("error converting amount_of_tokens, maybe value too large"))?;
665 let fee: u128 = fee.try_into().map_err(|_| revert("can't convert fee"))?;
666
667 let dest_weight_limit = if weight.is_zero() {
668 WeightLimit::Unlimited
669 } else {
670 WeightLimit::Limited(weight.get_weight())
671 };
672
673 log::trace!(target: "xcm-precompile::transfer_multiasset_with_fee", "Raw arguments: asset_location: {:?}, amount_of_tokens: {:?}, fee{:?}, destination: {:?}, \
674 weight: {:?}",
675 asset_location, amount_of_tokens, fee, destination, weight);
676
677 let call = orml_xtokens::Call::<Runtime>::transfer_multiasset_with_fee {
678 asset: Box::new(VersionedAsset::V5(
679 (asset_location.clone(), amount_of_tokens).into(),
680 )),
681 fee: Box::new(VersionedAsset::V5((asset_location, fee).into())),
682 dest: Box::new(VersionedLocation::V5(destination)),
683 dest_weight_limit,
684 };
685
686 let origin = Some(Runtime::AddressMapping::into_account_id(
687 handle.context().caller,
688 ))
689 .into();
690
691 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
693
694 Ok(true)
695 }
696
697 #[precompile::public(
698 "transfer_multi_currencies((address,uint256)[],uint32,(uint8,bytes[]),(uint64,uint64))"
699 )]
700 fn transfer_multi_currencies(
701 handle: &mut impl PrecompileHandle,
702 currencies: BoundedVec<Currency, GetMaxAssets<Runtime>>,
703 fee_item: u32,
704 destination: Location,
705 weight: WeightV2,
706 ) -> EvmResult<bool> {
707 let currencies: Vec<_> = currencies.into();
708 let currencies = currencies
709 .into_iter()
710 .map(|currency| {
711 let currency_address: H160 = currency.get_address().into();
712 let amount = currency
713 .get_amount()
714 .try_into()
715 .map_err(|_| revert("value too large: in currency"))?;
716
717 Ok((
718 Runtime::address_to_asset_id(currency_address.into())
719 .ok_or(revert("can't convert into currency id"))?
720 .into(),
721 amount,
722 ))
723 })
724 .collect::<EvmResult<_>>()?;
725 let dest_weight_limit = if weight.is_zero() {
726 WeightLimit::Unlimited
727 } else {
728 WeightLimit::Limited(weight.get_weight())
729 };
730
731 log::trace!(target: "xcm-precompile::transfer_multi_currencies", "Raw arguments: currencies: {:?}, fee_item{:?}, destination: {:?}, \
732 weight: {:?}",
733 currencies, fee_item, destination, weight);
734
735 let call = orml_xtokens::Call::<Runtime>::transfer_multicurrencies {
736 currencies,
737 fee_item,
738 dest: Box::new(VersionedLocation::V5(destination)),
739 dest_weight_limit,
740 };
741
742 let origin = Some(Runtime::AddressMapping::into_account_id(
743 handle.context().caller,
744 ))
745 .into();
746
747 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
749
750 Ok(true)
751 }
752
753 #[precompile::public(
754 "transfer_multi_assets(((uint8,bytes[]),uint256)[],uint32,(uint8,bytes[]),(uint64,uint64))"
755 )]
756 fn transfer_multi_assets(
757 handle: &mut impl PrecompileHandle,
758 assets: BoundedVec<EvmMultiAsset, GetMaxAssets<Runtime>>,
759 fee_item: u32,
760 destination: Location,
761 weight: WeightV2,
762 ) -> EvmResult<bool> {
763 let assets: Vec<_> = assets.into();
764
765 let dest_weight_limit = if weight.is_zero() {
766 WeightLimit::Unlimited
767 } else {
768 WeightLimit::Limited(weight.get_weight())
769 };
770
771 log::trace!(target: "xcm-precompile::transfer_multi_assets", "Raw arguments: assets: {:?}, fee_item{:?}, destination: {:?}, \
772 weight: {:?}",
773 assets, fee_item, destination, weight);
774
775 let multiasset_vec: EvmResult<Vec<Asset>> = assets
776 .into_iter()
777 .map(|evm_multiasset| {
778 let to_balance: u128 = evm_multiasset
779 .get_amount()
780 .try_into()
781 .map_err(|_| revert("value too large in assets"))?;
782 Ok((evm_multiasset.get_location(), to_balance).into())
783 })
784 .collect();
785
786 let multiassets = Assets::from_sorted_and_deduplicated(multiasset_vec?).map_err(|_| {
789 revert("In field Assets, Provided assets either not sorted nor deduplicated")
790 })?;
791
792 let call = orml_xtokens::Call::<Runtime>::transfer_multiassets {
793 assets: Box::new(VersionedAssets::V5(multiassets)),
794 fee_item,
795 dest: Box::new(VersionedLocation::V5(destination)),
796 dest_weight_limit,
797 };
798
799 let origin = Some(Runtime::AddressMapping::into_account_id(
800 handle.context().caller,
801 ))
802 .into();
803
804 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
806
807 Ok(true)
808 }
809
810 fn ensure_dot_transfer_policy(assets: &[Asset], destination: &Location) -> EvmResult<()> {
815 let dest_chain = {
816 let mut d = destination.clone();
817 d.interior.take_last();
820 d
821 };
822
823 if dest_chain != Location::parent() {
824 return Ok(());
825 }
826
827 let deprecated_dot_location = Location::new(1, Junctions::Here);
828
829 for asset in assets {
830 let AssetId(location) = &asset.id;
831 if location == &deprecated_dot_location {
832 return Err(revert(
833 "DOT cannot be sent directly to the relay. \
834 Route via AssetHub (parachain 1000).",
835 ));
836 }
837 }
838
839 Ok(())
840 }
841}
842
843#[derive(Debug, Clone, solidity::Codec)]
844pub struct WeightV2 {
845 ref_time: u64,
846 proof_size: u64,
847}
848
849impl WeightV2 {
850 pub fn from(ref_time: u64, proof_size: u64) -> Self {
851 WeightV2 {
852 ref_time,
853 proof_size,
854 }
855 }
856
857 pub fn get_weight(&self) -> Weight {
858 Weight::from_parts(self.ref_time, self.proof_size)
859 }
860
861 pub fn is_zero(&self) -> bool {
862 self.ref_time == 0u64
863 }
864}
865
866#[derive(Debug, Clone, solidity::Codec)]
867pub struct Currency {
868 address: Address,
869 amount: U256,
870}
871
872impl Currency {
873 pub fn get_address(&self) -> Address {
874 self.address
875 }
876
877 pub fn get_amount(&self) -> U256 {
878 self.amount
879 }
880}
881
882impl From<(Address, U256)> for Currency {
883 fn from(tuple: (Address, U256)) -> Self {
884 Currency {
885 address: tuple.0,
886 amount: tuple.1,
887 }
888 }
889}
890
891#[derive(Debug, Clone, solidity::Codec)]
892pub struct EvmMultiAsset {
893 location: Location,
894 amount: U256,
895}
896
897impl From<(Location, U256)> for EvmMultiAsset {
898 fn from(tuple: (Location, U256)) -> Self {
899 EvmMultiAsset {
900 location: tuple.0,
901 amount: tuple.1,
902 }
903 }
904}
905
906impl EvmMultiAsset {
907 pub fn get_location(&self) -> Location {
908 self.location.clone()
909 }
910
911 pub fn get_amount(&self) -> U256 {
912 self.amount
913 }
914}