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 log::trace!(target: "xcm-precompile:assets_withdraw", "Processed arguments: assets {:?}, destination: {:?}", assets, destination);
200
201 let origin = Some(Runtime::AddressMapping::into_account_id(
203 handle.context().caller,
204 ))
205 .into();
206
207 let call = orml_xtokens::Call::<Runtime>::transfer_multiassets {
208 assets: Box::new(VersionedAssets::V5(assets.into())),
209 fee_item,
210 dest: Box::new(VersionedLocation::V5(destination)),
211 dest_weight_limit: WeightLimit::Unlimited,
212 };
213
214 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
216 Ok(true)
217 }
218
219 #[precompile::public("remote_transact(uint256,bool,address,uint256,bytes,uint64)")]
220 fn remote_transact_v1(
221 handle: &mut impl PrecompileHandle,
222 para_id: U256,
223 is_relay: bool,
224 fee_asset_addr: Address,
225 fee_amount: U256,
226 remote_call: UnboundedBytes,
227 transact_weight: u64,
228 ) -> EvmResult<bool> {
229 let para_id: u32 = para_id
231 .try_into()
232 .map_err(|_| revert("error converting para_id, maybe value too large"))?;
233
234 let fee_amount: u128 = fee_amount
235 .try_into()
236 .map_err(|_| revert("error converting fee_amount, maybe value too large"))?;
237
238 let remote_call: Vec<u8> = remote_call.into();
239
240 log::trace!(target: "xcm-precompile:remote_transact", "Raw arguments: para_id: {}, is_relay: {}, fee_asset_addr: {:?}, \
241 fee_amount: {:?}, remote_call: {:?}, transact_weight: {}",
242 para_id, is_relay, fee_asset_addr, fee_amount, remote_call, transact_weight);
243
244 let dest = if is_relay {
246 Location::parent()
247 } else {
248 Junctions::from(Junction::Parachain(para_id)).into_exterior(1)
249 };
250
251 let fee_asset = {
252 let address: H160 = fee_asset_addr.into();
253
254 if address == NATIVE_ADDRESS {
256 Here.into()
257 } else {
258 let fee_asset_id = Runtime::address_to_asset_id(address)
259 .ok_or(revert("Failed to resolve fee asset id from address"))?;
260 C::convert_back(&fee_asset_id).ok_or(revert(
261 "Failed to resolve fee asset multilocation from local id",
262 ))?
263 }
264 };
265
266 let context = <Runtime as pallet_xcm::Config>::UniversalLocation::get();
267 let fee_multilocation: Asset = (fee_asset, fee_amount).into();
268 let fee_multilocation = fee_multilocation
269 .reanchored(&dest, &context)
270 .map_err(|_| revert("Failed to reanchor fee asset"))?;
271
272 let xcm = Xcm(vec![
274 WithdrawAsset(fee_multilocation.clone().into()),
275 BuyExecution {
276 fees: fee_multilocation.clone().into(),
277 weight_limit: WeightLimit::Unlimited,
278 },
279 Transact {
280 origin_kind: OriginKind::SovereignAccount,
281 fallback_max_weight: Some(Weight::from_parts(transact_weight, DEFAULT_PROOF_SIZE)),
282 call: remote_call.into(),
283 },
284 ]);
285
286 log::trace!(target: "xcm-precompile:remote_transact", "Processed arguments: dest: {:?}, fee asset: {:?}, XCM: {:?}", dest, fee_multilocation, xcm);
287
288 let origin = Some(Runtime::AddressMapping::into_account_id(
290 handle.context().caller,
291 ))
292 .into();
293 let call = pallet_xcm::Call::<Runtime>::send {
294 dest: Box::new(dest.into()),
295 message: Box::new(xcm::VersionedXcm::V5(xcm)),
296 };
297
298 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
300
301 Ok(true)
302 }
303
304 fn assets_reserve_transfer_v1_internal(
305 handle: &mut impl PrecompileHandle,
306 assets: Vec<Address>,
307 amounts: Vec<U256>,
308 beneficiary: Junction,
309 is_relay: bool,
310 parachain_id: U256,
311 fee_item: U256,
312 ) -> EvmResult<bool> {
313 let assets: Vec<Location> = assets
314 .iter()
315 .cloned()
316 .filter_map(|address| {
317 let address: H160 = address.into();
318 if address == NATIVE_ADDRESS {
320 Some(Here.into())
321 } else {
322 Runtime::address_to_asset_id(address).and_then(|x| C::convert_back(&x))
323 }
324 })
325 .collect();
326
327 let amounts: Vec<u128> = amounts
328 .into_iter()
329 .map(|x| x.try_into())
330 .collect::<Result<Vec<u128>, _>>()
331 .map_err(|_| revert("error converting amounts, maybe value too large"))?;
332
333 if assets.len() != amounts.len() || assets.is_empty() {
337 return Err(revert("Assets resolution failure."));
338 }
339
340 let parachain_id: u32 = parachain_id
341 .try_into()
342 .map_err(|_| revert("error converting parachain_id, maybe value too large"))?;
343
344 let fee_item: u32 = fee_item
345 .try_into()
346 .map_err(|_| revert("error converting fee_index, maybe value too large"))?;
347
348 let mut destination = if is_relay {
350 Location::parent()
351 } else {
352 Junctions::from(Junction::Parachain(parachain_id)).into_exterior(1)
353 };
354
355 destination
356 .push_interior(beneficiary)
357 .map_err(|_| revert("error building destination multilocation"))?;
358
359 let assets = assets
360 .iter()
361 .cloned()
362 .zip(amounts.iter().cloned())
363 .map(Into::into)
364 .collect::<Vec<Asset>>();
365
366 log::trace!(target: "xcm-precompile:assets_reserve_transfer", "Processed arguments: assets {:?}, destination: {:?}", assets, destination);
367
368 let origin = Some(Runtime::AddressMapping::into_account_id(
370 handle.context().caller,
371 ))
372 .into();
373
374 let call = orml_xtokens::Call::<Runtime>::transfer_multiassets {
375 assets: Box::new(VersionedAssets::V5(assets.into())),
376 fee_item,
377 dest: Box::new(VersionedLocation::V5(destination)),
378 dest_weight_limit: WeightLimit::Unlimited,
379 };
380
381 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
383
384 Ok(true)
385 }
386
387 #[precompile::public(
388 "assets_reserve_transfer(address[],uint256[],bytes32,bool,uint256,uint256)"
389 )]
390 fn assets_reserve_transfer_native_v1(
391 handle: &mut impl PrecompileHandle,
392 assets: BoundedVec<Address, GetMaxAssets<Runtime>>,
393 amounts: BoundedVec<U256, GetMaxAssets<Runtime>>,
394 recipient_account_id: H256,
395 is_relay: bool,
396 parachain_id: U256,
397 fee_index: U256,
398 ) -> EvmResult<bool> {
399 let beneficiary: Junction = Junction::AccountId32 {
400 network: None,
401 id: recipient_account_id.into(),
402 }
403 .into();
404 Self::assets_reserve_transfer_v1_internal(
405 handle,
406 assets.into(),
407 amounts.into(),
408 beneficiary,
409 is_relay,
410 parachain_id,
411 fee_index,
412 )
413 }
414
415 #[precompile::public(
416 "assets_reserve_transfer(address[],uint256[],address,bool,uint256,uint256)"
417 )]
418 fn assets_reserve_transfer_evm_v1(
419 handle: &mut impl PrecompileHandle,
420 assets: BoundedVec<Address, GetMaxAssets<Runtime>>,
421 amounts: BoundedVec<U256, GetMaxAssets<Runtime>>,
422 recipient_account_id: Address,
423 is_relay: bool,
424 parachain_id: U256,
425 fee_index: U256,
426 ) -> EvmResult<bool> {
427 let beneficiary: Junction = Junction::AccountKey20 {
428 network: None,
429 key: recipient_account_id.0.to_fixed_bytes(),
430 }
431 .into();
432 Self::assets_reserve_transfer_v1_internal(
433 handle,
434 assets.into(),
435 amounts.into(),
436 beneficiary,
437 is_relay,
438 parachain_id,
439 fee_index,
440 )
441 }
442
443 #[precompile::public("send_xcm((uint8,bytes[]),bytes)")]
444 fn send_xcm(
445 handle: &mut impl PrecompileHandle,
446 dest: Location,
447 xcm_call: BoundedBytes<GetXcmSizeLimit>,
448 ) -> EvmResult<bool> {
449 let dest: Location = dest.into();
451 let xcm_call: Vec<u8> = xcm_call.into();
452
453 log::trace!(target:"xcm-precompile::send_xcm", "Raw arguments: dest: {:?}, xcm_call: {:?}", dest, xcm_call);
454
455 let xcm = xcm::VersionedXcm::<()>::decode_all_with_depth_limit(
456 xcm::MAX_XCM_DECODE_DEPTH,
457 &mut xcm_call.as_slice(),
458 )
459 .map_err(|_| revert("Failed to decode xcm instructions"))?;
460
461 let origin = Some(Runtime::AddressMapping::into_account_id(
463 handle.context().caller,
464 ))
465 .into();
466 let call = pallet_xcm::Call::<Runtime>::send {
467 dest: Box::new(dest.into()),
468 message: Box::new(xcm),
469 };
470 log::trace!(target: "xcm-send_xcm", "Processed arguments: XCM call: {:?}", call);
471 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
473
474 Ok(true)
475 }
476
477 #[precompile::public("transfer(address,uint256,(uint8,bytes[]),(uint64,uint64))")]
478 fn transfer(
479 handle: &mut impl PrecompileHandle,
480 currency_address: Address,
481 amount_of_tokens: U256,
482 destination: Location,
483 weight: WeightV2,
484 ) -> EvmResult<bool> {
485 let amount_of_tokens: u128 = amount_of_tokens
487 .try_into()
488 .map_err(|_| revert("error converting amount_of_tokens, maybe value too large"))?;
489
490 let dest_weight_limit = if weight.is_zero() {
491 WeightLimit::Unlimited
492 } else {
493 WeightLimit::Limited(weight.get_weight())
494 };
495
496 let call = {
497 if currency_address == Address::from(NATIVE_ADDRESS) {
498 log::trace!(target: "xcm-precompile::transfer", "Raw arguments: currency_address: {:?} (this is native token), amount_of_tokens: {:?}, destination: {:?}, \
499 weight: {:?}",
500 currency_address, amount_of_tokens, destination, weight );
501
502 orml_xtokens::Call::<Runtime>::transfer_multiasset {
503 asset: Box::new(VersionedAsset::V5(
504 (Location::here(), amount_of_tokens).into(),
505 )),
506 dest: Box::new(VersionedLocation::V5(destination)),
507 dest_weight_limit,
508 }
509 } else {
510 let asset_id = Runtime::address_to_asset_id(currency_address.into())
511 .ok_or(revert("Failed to resolve fee asset id from address"))?;
512
513 log::trace!(target: "xcm-precompile::transfer", "Raw arguments: currency_address: {:?}, amount_of_tokens: {:?}, destination: {:?}, \
514 weight: {:?}, calculated asset_id: {:?}",
515 currency_address, amount_of_tokens, destination, weight, asset_id);
516
517 orml_xtokens::Call::<Runtime>::transfer {
518 currency_id: asset_id.into(),
519 amount: amount_of_tokens.into(),
520 dest: Box::new(VersionedLocation::V5(destination)),
521 dest_weight_limit,
522 }
523 }
524 };
525
526 let origin = Some(Runtime::AddressMapping::into_account_id(
527 handle.context().caller,
528 ))
529 .into();
530
531 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
533
534 Ok(true)
535 }
536
537 #[precompile::public(
538 "transfer_with_fee(address,uint256,uint256,(uint8,bytes[]),(uint64,uint64))"
539 )]
540 fn transfer_with_fee(
541 handle: &mut impl PrecompileHandle,
542 currency_address: Address,
543 amount_of_tokens: U256,
544 fee: U256,
545 destination: Location,
546 weight: WeightV2,
547 ) -> EvmResult<bool> {
548 let amount_of_tokens: u128 = amount_of_tokens
550 .try_into()
551 .map_err(|_| revert("error converting amount_of_tokens, maybe value too large"))?;
552 let fee: u128 = fee.try_into().map_err(|_| revert("can't convert fee"))?;
553
554 let dest_weight_limit = if weight.is_zero() {
555 WeightLimit::Unlimited
556 } else {
557 WeightLimit::Limited(weight.get_weight())
558 };
559
560 let call = {
561 if currency_address == Address::from(NATIVE_ADDRESS) {
562 log::trace!(target: "xcm-precompile::transfer_with_fee", "Raw arguments: currency_address: {:?} (this is native token), amount_of_tokens: {:?}, destination: {:?}, \
563 weight: {:?}, fee {:?}",
564 currency_address, amount_of_tokens, destination, weight, fee );
565
566 orml_xtokens::Call::<Runtime>::transfer_multiasset_with_fee {
567 asset: Box::new(VersionedAsset::V5(
568 (Location::here(), amount_of_tokens).into(),
569 )),
570 fee: Box::new(VersionedAsset::V5((Location::here(), fee).into())),
571 dest: Box::new(VersionedLocation::V5(destination)),
572 dest_weight_limit,
573 }
574 } else {
575 let asset_id = Runtime::address_to_asset_id(currency_address.into())
576 .ok_or(revert("Failed to resolve fee asset id from address"))?;
577
578 log::trace!(target: "xcm-precompile::transfer_with_fee", "Raw arguments: currency_address: {:?}, amount_of_tokens: {:?}, destination: {:?}, \
579 weight: {:?}, calculated asset_id: {:?}, fee: {:?}",
580 currency_address, amount_of_tokens, destination, weight, asset_id, fee);
581
582 orml_xtokens::Call::<Runtime>::transfer_with_fee {
583 currency_id: asset_id.into(),
584 amount: amount_of_tokens.into(),
585 fee: fee.into(),
586 dest: Box::new(VersionedLocation::V5(destination)),
587 dest_weight_limit,
588 }
589 }
590 };
591
592 let origin = Some(Runtime::AddressMapping::into_account_id(
593 handle.context().caller,
594 ))
595 .into();
596
597 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
599
600 Ok(true)
601 }
602
603 #[precompile::public(
604 "transfer_multiasset((uint8,bytes[]),uint256,(uint8,bytes[]),(uint64,uint64))"
605 )]
606 fn transfer_multiasset(
607 handle: &mut impl PrecompileHandle,
608 asset_location: Location,
609 amount_of_tokens: U256,
610 destination: Location,
611 weight: WeightV2,
612 ) -> EvmResult<bool> {
613 let amount_of_tokens: u128 = amount_of_tokens
615 .try_into()
616 .map_err(|_| revert("error converting amount_of_tokens, maybe value too large"))?;
617
618 let dest_weight_limit = if weight.is_zero() {
619 WeightLimit::Unlimited
620 } else {
621 WeightLimit::Limited(weight.get_weight())
622 };
623
624 log::trace!(target: "xcm-precompile::transfer_multiasset", "Raw arguments: asset_location: {:?}, amount_of_tokens: {:?}, destination: {:?}, \
625 weight: {:?}",
626 asset_location, amount_of_tokens, destination, weight);
627
628 let call = orml_xtokens::Call::<Runtime>::transfer_multiasset {
629 asset: Box::new(VersionedAsset::V5(
630 (asset_location, amount_of_tokens).into(),
631 )),
632 dest: Box::new(VersionedLocation::V5(destination)),
633 dest_weight_limit,
634 };
635
636 let origin = Some(Runtime::AddressMapping::into_account_id(
637 handle.context().caller,
638 ))
639 .into();
640
641 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
643
644 Ok(true)
645 }
646
647 #[precompile::public(
648 "transfer_multiasset_with_fee((uint8,bytes[]),uint256,uint256,(uint8,bytes[]),(uint64,uint64))"
649 )]
650 fn transfer_multiasset_with_fee(
651 handle: &mut impl PrecompileHandle,
652 asset_location: Location,
653 amount_of_tokens: U256,
654 fee: U256,
655 destination: Location,
656 weight: WeightV2,
657 ) -> EvmResult<bool> {
658 let amount_of_tokens: u128 = amount_of_tokens
660 .try_into()
661 .map_err(|_| revert("error converting amount_of_tokens, maybe value too large"))?;
662 let fee: u128 = fee.try_into().map_err(|_| revert("can't convert fee"))?;
663
664 let dest_weight_limit = if weight.is_zero() {
665 WeightLimit::Unlimited
666 } else {
667 WeightLimit::Limited(weight.get_weight())
668 };
669
670 log::trace!(target: "xcm-precompile::transfer_multiasset_with_fee", "Raw arguments: asset_location: {:?}, amount_of_tokens: {:?}, fee{:?}, destination: {:?}, \
671 weight: {:?}",
672 asset_location, amount_of_tokens, fee, destination, weight);
673
674 let call = orml_xtokens::Call::<Runtime>::transfer_multiasset_with_fee {
675 asset: Box::new(VersionedAsset::V5(
676 (asset_location.clone(), amount_of_tokens).into(),
677 )),
678 fee: Box::new(VersionedAsset::V5((asset_location, fee).into())),
679 dest: Box::new(VersionedLocation::V5(destination)),
680 dest_weight_limit,
681 };
682
683 let origin = Some(Runtime::AddressMapping::into_account_id(
684 handle.context().caller,
685 ))
686 .into();
687
688 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
690
691 Ok(true)
692 }
693
694 #[precompile::public(
695 "transfer_multi_currencies((address,uint256)[],uint32,(uint8,bytes[]),(uint64,uint64))"
696 )]
697 fn transfer_multi_currencies(
698 handle: &mut impl PrecompileHandle,
699 currencies: BoundedVec<Currency, GetMaxAssets<Runtime>>,
700 fee_item: u32,
701 destination: Location,
702 weight: WeightV2,
703 ) -> EvmResult<bool> {
704 let currencies: Vec<_> = currencies.into();
705 let currencies = currencies
706 .into_iter()
707 .map(|currency| {
708 let currency_address: H160 = currency.get_address().into();
709 let amount = currency
710 .get_amount()
711 .try_into()
712 .map_err(|_| revert("value too large: in currency"))?;
713
714 Ok((
715 Runtime::address_to_asset_id(currency_address.into())
716 .ok_or(revert("can't convert into currency id"))?
717 .into(),
718 amount,
719 ))
720 })
721 .collect::<EvmResult<_>>()?;
722 let dest_weight_limit = if weight.is_zero() {
723 WeightLimit::Unlimited
724 } else {
725 WeightLimit::Limited(weight.get_weight())
726 };
727
728 log::trace!(target: "xcm-precompile::transfer_multi_currencies", "Raw arguments: currencies: {:?}, fee_item{:?}, destination: {:?}, \
729 weight: {:?}",
730 currencies, fee_item, destination, weight);
731
732 let call = orml_xtokens::Call::<Runtime>::transfer_multicurrencies {
733 currencies,
734 fee_item,
735 dest: Box::new(VersionedLocation::V5(destination)),
736 dest_weight_limit,
737 };
738
739 let origin = Some(Runtime::AddressMapping::into_account_id(
740 handle.context().caller,
741 ))
742 .into();
743
744 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
746
747 Ok(true)
748 }
749
750 #[precompile::public(
751 "transfet_multi_assets(((uint8,bytes[]),uint256)[],uint32,(uint8,bytes[]),(uint64,uint64))"
752 )]
753 fn transfer_multi_assets(
754 handle: &mut impl PrecompileHandle,
755 assets: BoundedVec<EvmMultiAsset, GetMaxAssets<Runtime>>,
756 fee_item: u32,
757 destination: Location,
758 weight: WeightV2,
759 ) -> EvmResult<bool> {
760 let assets: Vec<_> = assets.into();
761
762 let dest_weight_limit = if weight.is_zero() {
763 WeightLimit::Unlimited
764 } else {
765 WeightLimit::Limited(weight.get_weight())
766 };
767
768 log::trace!(target: "xcm-precompile::transfer_multi_assets", "Raw arguments: assets: {:?}, fee_item{:?}, destination: {:?}, \
769 weight: {:?}",
770 assets, fee_item, destination, weight);
771
772 let multiasset_vec: EvmResult<Vec<Asset>> = assets
773 .into_iter()
774 .map(|evm_multiasset| {
775 let to_balance: u128 = evm_multiasset
776 .get_amount()
777 .try_into()
778 .map_err(|_| revert("value too large in assets"))?;
779 Ok((evm_multiasset.get_location(), to_balance).into())
780 })
781 .collect();
782
783 let multiassets = Assets::from_sorted_and_deduplicated(multiasset_vec?).map_err(|_| {
786 revert("In field Assets, Provided assets either not sorted nor deduplicated")
787 })?;
788
789 let call = orml_xtokens::Call::<Runtime>::transfer_multiassets {
790 assets: Box::new(VersionedAssets::V5(multiassets)),
791 fee_item,
792 dest: Box::new(VersionedLocation::V5(destination)),
793 dest_weight_limit,
794 };
795
796 let origin = Some(Runtime::AddressMapping::into_account_id(
797 handle.context().caller,
798 ))
799 .into();
800
801 RuntimeHelper::<Runtime>::try_dispatch(handle, origin, call, 0)?;
803
804 Ok(true)
805 }
806}
807
808#[derive(Debug, Clone, solidity::Codec)]
809pub struct WeightV2 {
810 ref_time: u64,
811 proof_size: u64,
812}
813
814impl WeightV2 {
815 pub fn from(ref_time: u64, proof_size: u64) -> Self {
816 WeightV2 {
817 ref_time,
818 proof_size,
819 }
820 }
821
822 pub fn get_weight(&self) -> Weight {
823 Weight::from_parts(self.ref_time, self.proof_size)
824 }
825
826 pub fn is_zero(&self) -> bool {
827 self.ref_time == 0u64
828 }
829}
830
831#[derive(Debug, Clone, solidity::Codec)]
832pub struct Currency {
833 address: Address,
834 amount: U256,
835}
836
837impl Currency {
838 pub fn get_address(&self) -> Address {
839 self.address
840 }
841
842 pub fn get_amount(&self) -> U256 {
843 self.amount
844 }
845}
846
847impl From<(Address, U256)> for Currency {
848 fn from(tuple: (Address, U256)) -> Self {
849 Currency {
850 address: tuple.0,
851 amount: tuple.1,
852 }
853 }
854}
855
856#[derive(Debug, Clone, solidity::Codec)]
857pub struct EvmMultiAsset {
858 location: Location,
859 amount: U256,
860}
861
862impl From<(Location, U256)> for EvmMultiAsset {
863 fn from(tuple: (Location, U256)) -> Self {
864 EvmMultiAsset {
865 location: tuple.0,
866 amount: tuple.1,
867 }
868 }
869}
870
871impl EvmMultiAsset {
872 pub fn get_location(&self) -> Location {
873 self.location.clone()
874 }
875
876 pub fn get_amount(&self) -> U256 {
877 self.amount
878 }
879}