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