1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright 2023 Centrifuge Foundation (centrifuge.io).
//
// This file is part of the Centrifuge chain project.
// Centrifuge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version (see http://www.gnu.org/licenses).
// Centrifuge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

use frame_support::pallet_prelude::RuntimeDebug;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_core::{crypto::AccountId32, H160};
use sp_runtime::{traits::AccountIdConversion, TypeId};

use crate::EVMChainId;

const MAX_ADDRESS_SIZE: usize = 32;

/// By just clamping the value to a smaller address
pub fn account_to_eth_address(address: AccountId32) -> H160 {
	let bytes: [u8; 32] = address.into();
	H160::from(
		*(bytes)
			.split_first_chunk::<20>()
			.expect("always fit, qed")
			.0,
	)
}

/// By adding chain information to the new added bytes
pub fn eth_address_to_account(chain_id: u64, address: H160) -> AccountId32 {
	// We use a custom encoding here rather than relying on
	// `AccountIdConversion` for a couple of reasons:
	// 1. We have very few bytes to spare, so choosing our own fields is nice
	// 2. AccountIdConversion puts the tag first, which can unbalance the storage
	//    trees if users create many H160-derived accounts. We put the tag last
	//    here.
	let tag = b"EVM";
	let mut bytes = [0; 32];
	bytes[0..20].copy_from_slice(&address.0);
	bytes[20..28].copy_from_slice(&chain_id.to_be_bytes());
	bytes[28..31].copy_from_slice(tag);
	AccountId32::new(bytes)
}

/// A Domain is a chain or network we can send a message to.
#[derive(Encode, Decode, Clone, Copy, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)]
pub enum Domain {
	/// Referring to the Centrifuge Chain.
	Centrifuge,
	/// An EVM domain, identified by its EVM Chain Id
	Evm(EVMChainId),
}

impl TypeId for Domain {
	const TYPE_ID: [u8; 4] = crate::ids::DOMAIN_ID;
}

impl Domain {
	pub fn into_account<AccountId: Encode + Decode>(&self) -> AccountId {
		self.into_account_truncating()
	}

	pub fn get_evm_chain_id(&self) -> Option<EVMChainId> {
		match self {
			Domain::Centrifuge => None,
			Domain::Evm(id) => Some(*id),
		}
	}
}

#[derive(Encode, Decode, Clone, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)]
pub enum DomainAddress {
	/// A centrifuge based account
	Centrifuge(AccountId32),
	/// An EVM chain address
	Evm(EVMChainId, H160),
}

impl TypeId for DomainAddress {
	const TYPE_ID: [u8; 4] = crate::ids::DOMAIN_ADDRESS_ID;
}

impl From<DomainAddress> for Domain {
	fn from(x: DomainAddress) -> Self {
		match x {
			DomainAddress::Centrifuge(_) => Domain::Centrifuge,
			DomainAddress::Evm(chain_id, _) => Domain::Evm(chain_id),
		}
	}
}

impl DomainAddress {
	pub fn new(domain: Domain, address: [u8; MAX_ADDRESS_SIZE]) -> Self {
		match domain {
			Domain::Centrifuge => DomainAddress::Centrifuge(address.into()),
			Domain::Evm(chain_id) => {
				DomainAddress::Evm(chain_id, account_to_eth_address(address.into()))
			}
		}
	}

	pub fn domain(&self) -> Domain {
		self.clone().into()
	}
}

impl DomainAddress {
	/// Returns the current address as an centrifuge address
	pub fn account(&self) -> AccountId32 {
		match self.clone() {
			Self::Centrifuge(x) => x,
			Self::Evm(chain_id, x) => eth_address_to_account(chain_id, x),
		}
	}

	/// Returns the current address as an ethrerum address,
	/// clamping the inner address if needed.
	pub fn h160(&self) -> H160 {
		match self.clone() {
			Self::Centrifuge(x) => account_to_eth_address(x),
			Self::Evm(_, x) => x,
		}
	}

	/// Returns the current address as plain bytes
	pub fn bytes(&self) -> [u8; MAX_ADDRESS_SIZE] {
		self.account().into()
	}
}