1use crate::{ensure, Config, Error, HoldReason, OriginalAccount};
21use alloc::vec::Vec;
22use core::marker::PhantomData;
23use frame_support::traits::{fungible::MutateHold, tokens::Precision};
24use sp_core::{Get, H160};
25use sp_io::hashing::keccak_256;
26use sp_runtime::{AccountId32, DispatchResult, Saturating};
27
28pub trait AddressMapper<T: Config>: private::Sealed {
45 fn to_address(account_id: &T::AccountId) -> H160;
47
48 fn to_account_id(address: &H160) -> T::AccountId;
50
51 fn to_fallback_account_id(address: &H160) -> T::AccountId;
58
59 fn map(account_id: &T::AccountId) -> DispatchResult;
64
65 fn map_no_deposit(account_id: &T::AccountId) -> DispatchResult {
68 Self::map(account_id)
69 }
70
71 fn unmap(account_id: &T::AccountId) -> DispatchResult;
76
77 fn is_mapped(account_id: &T::AccountId) -> bool;
82}
83
84mod private {
85 pub trait Sealed {}
86 impl<T> Sealed for super::AccountId32Mapper<T> {}
87 impl<T> Sealed for super::H160Mapper<T> {}
88 impl<T> Sealed for super::TestAccountMapper<T> {}
89}
90
91pub struct AccountId32Mapper<T>(PhantomData<T>);
98
99#[allow(dead_code)]
103pub struct H160Mapper<T>(PhantomData<T>);
104
105pub struct TestAccountMapper<T>(PhantomData<T>);
107
108impl<T> AddressMapper<T> for AccountId32Mapper<T>
109where
110 T: Config<AccountId = AccountId32>,
111{
112 fn to_address(account_id: &AccountId32) -> H160 {
113 let account_bytes: &[u8; 32] = account_id.as_ref();
114 if is_eth_derived(account_id) {
115 H160::from_slice(&account_bytes[..20])
118 } else {
119 let account_hash = keccak_256(account_bytes);
122 H160::from_slice(&account_hash[12..])
123 }
124 }
125
126 fn to_account_id(address: &H160) -> AccountId32 {
127 <OriginalAccount<T>>::get(address).unwrap_or_else(|| Self::to_fallback_account_id(address))
128 }
129
130 fn to_fallback_account_id(address: &H160) -> AccountId32 {
131 let mut account_id = AccountId32::new([0xEE; 32]);
132 let account_bytes: &mut [u8; 32] = account_id.as_mut();
133 account_bytes[..20].copy_from_slice(address.as_bytes());
134 account_id
135 }
136
137 fn map(account_id: &T::AccountId) -> DispatchResult {
138 ensure!(!Self::is_mapped(account_id), <Error<T>>::AccountAlreadyMapped);
139
140 let deposit = T::DepositPerByte::get()
142 .saturating_mul(52u32.into())
143 .saturating_add(T::DepositPerItem::get());
144 T::Currency::hold(&HoldReason::AddressMapping.into(), account_id, deposit)?;
145
146 <OriginalAccount<T>>::insert(Self::to_address(account_id), account_id);
147 Ok(())
148 }
149
150 fn map_no_deposit(account_id: &T::AccountId) -> DispatchResult {
151 ensure!(!Self::is_mapped(account_id), <Error<T>>::AccountAlreadyMapped);
152 <OriginalAccount<T>>::insert(Self::to_address(account_id), account_id);
153 Ok(())
154 }
155
156 fn unmap(account_id: &T::AccountId) -> DispatchResult {
157 <OriginalAccount<T>>::remove(Self::to_address(account_id));
159 T::Currency::release_all(
160 &HoldReason::AddressMapping.into(),
161 account_id,
162 Precision::BestEffort,
163 )?;
164 Ok(())
165 }
166
167 fn is_mapped(account_id: &T::AccountId) -> bool {
168 is_eth_derived(account_id) ||
169 <OriginalAccount<T>>::contains_key(Self::to_address(account_id))
170 }
171}
172
173impl<T> AddressMapper<T> for TestAccountMapper<T>
174where
175 T: Config<AccountId = u64>,
176{
177 fn to_address(account_id: &T::AccountId) -> H160 {
178 let mut bytes = [0u8; 20];
179 bytes[12..].copy_from_slice(&account_id.to_be_bytes());
180 H160::from(bytes)
181 }
182
183 fn to_account_id(address: &H160) -> T::AccountId {
184 Self::to_fallback_account_id(address)
185 }
186
187 fn to_fallback_account_id(address: &H160) -> T::AccountId {
188 u64::from_be_bytes(address.as_ref()[12..].try_into().unwrap())
189 }
190
191 fn map(_account_id: &T::AccountId) -> DispatchResult {
192 Ok(())
193 }
194
195 fn unmap(_account_id: &T::AccountId) -> DispatchResult {
196 Ok(())
197 }
198
199 fn is_mapped(_account_id: &T::AccountId) -> bool {
200 true
201 }
202}
203
204pub fn is_eth_derived(account_id: &AccountId32) -> bool {
211 let account_bytes: &[u8; 32] = account_id.as_ref();
212 &account_bytes[20..] == &[0xEE; 12]
213}
214
215impl<T> AddressMapper<T> for H160Mapper<T>
216where
217 T: Config,
218 crate::AccountIdOf<T>: AsRef<[u8; 20]> + From<H160>,
219{
220 fn to_address(account_id: &T::AccountId) -> H160 {
221 H160::from_slice(account_id.as_ref())
222 }
223
224 fn to_account_id(address: &H160) -> T::AccountId {
225 Self::to_fallback_account_id(address)
226 }
227
228 fn to_fallback_account_id(address: &H160) -> T::AccountId {
229 (*address).into()
230 }
231
232 fn map(_account_id: &T::AccountId) -> DispatchResult {
233 Ok(())
234 }
235
236 fn unmap(_account_id: &T::AccountId) -> DispatchResult {
237 Ok(())
238 }
239
240 fn is_mapped(_account_id: &T::AccountId) -> bool {
241 true
242 }
243}
244
245pub fn create1(deployer: &H160, nonce: u64) -> H160 {
247 let mut list = rlp::RlpStream::new_list(2);
248 list.append(&deployer.as_bytes());
249 list.append(&nonce);
250 let hash = keccak_256(&list.out());
251 H160::from_slice(&hash[12..])
252}
253
254pub fn create2(deployer: &H160, code: &[u8], input_data: &[u8], salt: &[u8; 32]) -> H160 {
256 let init_code_hash = {
257 let init_code: Vec<u8> = code.into_iter().chain(input_data).cloned().collect();
258 keccak_256(init_code.as_ref())
259 };
260 let mut bytes = [0; 85];
261 bytes[0] = 0xff;
262 bytes[1..21].copy_from_slice(deployer.as_bytes());
263 bytes[21..53].copy_from_slice(salt);
264 bytes[53..85].copy_from_slice(&init_code_hash);
265 let hash = keccak_256(&bytes);
266 H160::from_slice(&hash[12..])
267}
268
269#[cfg(test)]
270mod test {
271 use super::*;
272 use crate::{
273 test_utils::*,
274 tests::{ExtBuilder, Test},
275 AddressMapper, Error,
276 };
277 use frame_support::{
278 assert_err,
279 traits::fungible::{InspectHold, Mutate},
280 };
281 use pretty_assertions::assert_eq;
282 use sp_core::{hex2array, H160};
283
284 #[test]
285 fn create1_works() {
286 assert_eq!(
287 create1(&ALICE_ADDR, 1u64),
288 H160(hex2array!("c851da37e4e8d3a20d8d56be2963934b4ad71c3b")),
289 )
290 }
291
292 #[test]
293 fn create2_works() {
294 assert_eq!(
295 create2(
296 &ALICE_ADDR,
297 &hex2array!("600060005560016000"),
298 &hex2array!("55"),
299 &hex2array!("1234567890123456789012345678901234567890123456789012345678901234")
300 ),
301 H160(hex2array!("7f31e795e5836a19a8f919ab5a9de9a197ecd2b6")),
302 )
303 }
304
305 #[test]
306 fn fallback_map_works() {
307 assert!(<Test as Config>::AddressMapper::is_mapped(&ALICE));
308 assert_eq!(
309 ALICE_FALLBACK,
310 <Test as Config>::AddressMapper::to_fallback_account_id(&ALICE_ADDR)
311 );
312 assert_eq!(ALICE_ADDR, <Test as Config>::AddressMapper::to_address(&ALICE_FALLBACK));
313 }
314
315 #[test]
316 fn map_works() {
317 ExtBuilder::default().build().execute_with(|| {
318 <Test as Config>::Currency::set_balance(&EVE, 1_000_000);
319 assert!(!<Test as Config>::AddressMapper::is_mapped(&EVE));
321 assert_eq!(EVE_FALLBACK, <Test as Config>::AddressMapper::to_account_id(&EVE_ADDR));
322 assert_eq!(
323 <Test as Config>::Currency::balance_on_hold(
324 &HoldReason::AddressMapping.into(),
325 &EVE
326 ),
327 0
328 );
329
330 <Test as Config>::AddressMapper::map(&EVE).unwrap();
332 assert!(<Test as Config>::AddressMapper::is_mapped(&EVE));
333 assert_eq!(EVE, <Test as Config>::AddressMapper::to_account_id(&EVE_ADDR));
334 assert!(
335 <Test as Config>::Currency::balance_on_hold(
336 &HoldReason::AddressMapping.into(),
337 &EVE
338 ) > 0
339 );
340 });
341 }
342
343 #[test]
344 fn map_fallback_account_fails() {
345 ExtBuilder::default().build().execute_with(|| {
346 assert!(<Test as Config>::AddressMapper::is_mapped(&ALICE));
347 assert_err!(
349 <Test as Config>::AddressMapper::map(&ALICE),
350 <Error<Test>>::AccountAlreadyMapped,
351 );
352 assert_eq!(
353 <Test as Config>::Currency::balance_on_hold(
354 &HoldReason::AddressMapping.into(),
355 &ALICE
356 ),
357 0
358 );
359 });
360 }
361
362 #[test]
363 fn double_map_fails() {
364 ExtBuilder::default().build().execute_with(|| {
365 assert!(!<Test as Config>::AddressMapper::is_mapped(&EVE));
366 <Test as Config>::Currency::set_balance(&EVE, 1_000_000);
367 <Test as Config>::AddressMapper::map(&EVE).unwrap();
368 assert!(<Test as Config>::AddressMapper::is_mapped(&EVE));
369 let deposit = <Test as Config>::Currency::balance_on_hold(
370 &HoldReason::AddressMapping.into(),
371 &EVE,
372 );
373 assert_err!(
374 <Test as Config>::AddressMapper::map(&EVE),
375 <Error<Test>>::AccountAlreadyMapped,
376 );
377 assert!(<Test as Config>::AddressMapper::is_mapped(&EVE));
378 assert_eq!(
379 <Test as Config>::Currency::balance_on_hold(
380 &HoldReason::AddressMapping.into(),
381 &EVE
382 ),
383 deposit
384 );
385 });
386 }
387
388 #[test]
389 fn unmap_works() {
390 ExtBuilder::default().build().execute_with(|| {
391 <Test as Config>::Currency::set_balance(&EVE, 1_000_000);
392 <Test as Config>::AddressMapper::map(&EVE).unwrap();
393 assert!(<Test as Config>::AddressMapper::is_mapped(&EVE));
394 assert!(
395 <Test as Config>::Currency::balance_on_hold(
396 &HoldReason::AddressMapping.into(),
397 &EVE
398 ) > 0
399 );
400
401 <Test as Config>::AddressMapper::unmap(&EVE).unwrap();
402 assert!(!<Test as Config>::AddressMapper::is_mapped(&EVE));
403 assert_eq!(
404 <Test as Config>::Currency::balance_on_hold(
405 &HoldReason::AddressMapping.into(),
406 &EVE
407 ),
408 0
409 );
410
411 <Test as Config>::AddressMapper::unmap(&EVE).unwrap();
413 assert!(!<Test as Config>::AddressMapper::is_mapped(&EVE));
414 assert_eq!(
415 <Test as Config>::Currency::balance_on_hold(
416 &HoldReason::AddressMapping.into(),
417 &EVE
418 ),
419 0
420 );
421 });
422 }
423}