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
135
136
137
138
139
140
141
use ethers_core::{
types::{Address, NameOrAddress, Selector, TransactionRequest, H160, H256},
utils::keccak256,
};
use std::convert::TryInto;
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()
}
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) {
let v = if let Some(stripped) = val.strip_prefix("0x") { stripped } else { val };
assert_eq!(hash.0.to_vec(), hex::decode(v).unwrap());
}
#[test]
fn test_namehash() {
for (name, expected) in &[
("", "0000000000000000000000000000000000000000000000000000000000000000"),
("foo.eth", "de9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f"),
("eth", "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"),
("alice.eth", "0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec"),
] {
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,
]
);
}
}