use ethers_core::{
types::{Address, NameOrAddress, Selector, TransactionRequest, H160, H256},
utils::keccak256,
};
pub const ENS_ADDRESS: Address = H160([
0, 0, 0, 0, 0, 12, 46, 7, 78, 198, 154, 13, 251, 41, 151, 186, 108, 125, 46, 30,
]);
const ENS_REVERSE_REGISTRAR_DOMAIN: &str = "addr.reverse";
const RESOLVER: Selector = [1, 120, 184, 191];
pub const ADDR_SELECTOR: Selector = [59, 59, 87, 222];
pub const NAME_SELECTOR: Selector = [105, 31, 52, 49];
pub const FIELD_SELECTOR: Selector = [89, 209, 212, 60];
pub const INTERFACE_SELECTOR: Selector = [1, 255, 201, 167];
pub fn get_resolver<T: Into<NameOrAddress>>(ens_address: T, name: &str) -> TransactionRequest {
let data = [&RESOLVER[..], &namehash(name).0].concat();
TransactionRequest {
data: Some(data.into()),
to: Some(ens_address.into()),
..Default::default()
}
}
pub fn supports_interface<T: Into<NameOrAddress>>(
resolver_address: T,
selector: Selector,
) -> TransactionRequest {
let data = [&INTERFACE_SELECTOR[..], &selector[..], &[0; 28]].concat();
TransactionRequest {
data: Some(data.into()),
to: Some(resolver_address.into()),
..Default::default()
}
}
pub fn resolve<T: Into<NameOrAddress>>(
resolver_address: T,
selector: Selector,
name: &str,
parameters: Option<&[u8]>,
) -> TransactionRequest {
let data = [&selector[..], &namehash(name).0, parameters.unwrap_or_default()].concat();
TransactionRequest {
data: Some(data.into()),
to: Some(resolver_address.into()),
..Default::default()
}
}
pub fn reverse_address(addr: Address) -> String {
format!("{addr:?}.{ENS_REVERSE_REGISTRAR_DOMAIN}")[2..].to_string()
}
pub fn namehash(name: &str) -> H256 {
if name.is_empty() {
return H256::zero()
}
let name = name.replace('\u{fe0f}', "");
name.rsplit('.')
.fold([0u8; 32], |node, label| keccak256([node, keccak256(label.as_bytes())].concat()))
.into()
}
pub fn bytes_32ify(n: u64) -> Vec<u8> {
let b = n.to_be_bytes();
[[0; 32][b.len()..].to_vec(), b.to_vec()].concat()
}
pub fn parameterhash(name: &str) -> Vec<u8> {
let bytes = name.as_bytes();
let key_bytes =
[&bytes_32ify(64), &bytes_32ify(bytes.len().try_into().unwrap()), bytes].concat();
match key_bytes.len() % 32 {
0 => key_bytes,
n => [key_bytes, [0; 32][n..].to_vec()].concat(),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_hex(hash: H256, val: &str) {
assert_eq!(hash.0.to_vec(), hex::decode(val).unwrap());
}
#[test]
fn test_namehash() {
for (name, expected) in &[
("", "0000000000000000000000000000000000000000000000000000000000000000"),
("foo.eth", "de9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f"),
("eth", "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"),
("alice.eth", "0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec"),
("ret↩️rn.eth", "0x3de5f4c02db61b221e7de7f1c40e29b6e2f07eb48d65bf7e304715cd9ed33b24"),
] {
assert_hex(namehash(name), expected);
}
}
#[test]
fn test_parametershash() {
assert_eq!(
parameterhash("avatar").to_vec(),
vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 6, 97, 118, 97, 116, 97, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
);
}
}