pallet_erc20_xcm_bridge/
erc20_matcher.rs1use sp_core::{Get, H160, U256};
20use xcm::latest::prelude::*;
21use xcm::latest::{Junction, Location};
22use xcm_executor::traits::{Error as MatchError, MatchesFungibles};
23
24pub(crate) struct Erc20Matcher<Erc20MultilocationPrefix>(
25 core::marker::PhantomData<Erc20MultilocationPrefix>,
26);
27
28impl<Erc20MultilocationPrefix: Get<Location>> MatchesFungibles<H160, U256>
29 for Erc20Matcher<Erc20MultilocationPrefix>
30{
31 fn matches_fungibles(multiasset: &Asset) -> Result<(H160, U256), MatchError> {
32 let (amount, id) = match (&multiasset.fun, &multiasset.id) {
33 (Fungible(ref amount), AssetId(ref id)) => (amount, id),
34 _ => return Err(MatchError::AssetNotHandled),
35 };
36 let contract_address = Self::matches_erc20_multilocation(id)
37 .map_err(|_| MatchError::AssetIdConversionFailed)?;
38 let amount = U256::from(*amount);
39
40 Ok((contract_address, amount))
41 }
42}
43
44impl<Erc20MultilocationPrefix: Get<Location>> Erc20Matcher<Erc20MultilocationPrefix> {
45 pub(crate) fn is_erc20_asset(multiasset: &Asset) -> bool {
46 match (&multiasset.fun, &multiasset.id) {
47 (Fungible(_), AssetId(ref id)) => Self::matches_erc20_multilocation(id).is_ok(),
48 _ => false,
49 }
50 }
51 fn matches_erc20_multilocation(multilocation: &Location) -> Result<H160, ()> {
52 let prefix = Erc20MultilocationPrefix::get();
53 if prefix.parent_count() != multilocation.parent_count()
54 || prefix
55 .interior()
56 .iter()
57 .enumerate()
58 .any(|(index, junction)| multilocation.interior().at(index) != Some(junction))
59 {
60 return Err(());
61 }
62 match multilocation.interior().at(prefix.interior().len()) {
63 Some(Junction::AccountKey20 {
64 key: contract_address,
65 ..
66 }) => Ok(H160(*contract_address)),
67 _ => Err(()),
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 macro_rules! assert_ok {
77 ( $x:expr, $y:expr $(,)? ) => {
78 let is = $x;
79 match is {
80 Ok(ok) => assert_eq!(ok, $y),
81 _ => assert!(false, "Expected Ok(_). Got Err(_)"),
82 }
83 };
84 }
85
86 frame_support::parameter_types! {
87 pub Erc20MultilocationPrefix: Location = Location {
88 parents:0,
89 interior: [PalletInstance(42u8)].into()
90 };
91 }
92
93 #[test]
94 fn should_match_valid_erc20_location() {
95 let location = Location {
96 parents: 0,
97 interior: [
98 PalletInstance(42u8),
99 AccountKey20 {
100 key: [0; 20],
101 network: None,
102 },
103 ]
104 .into(),
105 };
106
107 assert_ok!(
108 Erc20Matcher::<Erc20MultilocationPrefix>::matches_fungibles(&Asset::from((
109 location, 100u128
110 ))),
111 (H160([0; 20]), U256([100, 0, 0, 0]))
112 );
113 }
114
115 #[test]
116 fn should_match_valid_erc20_location_with_amount_greater_than_u64() {
117 let location = Location {
118 parents: 0,
119 interior: [
120 PalletInstance(42u8),
121 AccountKey20 {
122 key: [0; 20],
123 network: None,
124 },
125 ]
126 .into(),
127 };
128
129 assert_ok!(
130 Erc20Matcher::<Erc20MultilocationPrefix>::matches_fungibles(&Asset::from((
131 location,
132 100000000000000000u128
133 ))),
134 (H160([0; 20]), U256::from(100000000000000000u128))
135 );
136 }
137
138 #[test]
139 fn should_not_match_invalid_erc20_location() {
140 let invalid_location = Location {
141 parents: 0,
142 interior: [PalletInstance(42u8), GeneralIndex(0)].into(),
143 };
144
145 assert!(
146 Erc20Matcher::<Erc20MultilocationPrefix>::matches_fungibles(&Asset::from((
147 invalid_location,
148 100u128
149 )))
150 .is_err()
151 );
152 }
153}