2021-09-06 08:52:44 +00:00
|
|
|
//! [Ethereum Name Service](https://docs.ens.domains/) support
|
2022-03-19 17:05:39 +00:00
|
|
|
//! Adapted from <https://github.com/hhatto/rust-ens/blob/master/src/lib.rs>
|
2020-05-31 16:01:34 +00:00
|
|
|
use ethers_core::{
|
2020-06-01 21:58:21 +00:00
|
|
|
types::{Address, NameOrAddress, Selector, TransactionRequest, H160, H256},
|
2020-05-31 16:01:34 +00:00
|
|
|
utils::keccak256,
|
|
|
|
};
|
2020-05-27 11:32:44 +00:00
|
|
|
|
2022-02-16 14:25:41 +00:00
|
|
|
use std::convert::TryInto;
|
|
|
|
|
2021-09-06 08:52:44 +00:00
|
|
|
/// ENS registry address (`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`)
|
2020-06-01 21:58:21 +00:00
|
|
|
pub const ENS_ADDRESS: Address = H160([
|
|
|
|
// cannot set type aliases as constructors
|
|
|
|
0, 0, 0, 0, 0, 12, 46, 7, 78, 198, 154, 13, 251, 41, 151, 186, 108, 125, 46, 30,
|
|
|
|
]);
|
|
|
|
|
2020-05-27 11:32:44 +00:00
|
|
|
// Selectors
|
|
|
|
const ENS_REVERSE_REGISTRAR_DOMAIN: &str = "addr.reverse";
|
|
|
|
|
2020-05-28 16:34:06 +00:00
|
|
|
/// resolver(bytes32)
|
2020-05-27 11:32:44 +00:00
|
|
|
const RESOLVER: Selector = [1, 120, 184, 191];
|
|
|
|
|
2020-05-28 16:34:06 +00:00
|
|
|
/// addr(bytes32)
|
2020-05-27 11:32:44 +00:00
|
|
|
pub const ADDR_SELECTOR: Selector = [59, 59, 87, 222];
|
|
|
|
|
2020-05-28 16:34:06 +00:00
|
|
|
/// name(bytes32)
|
2020-05-27 11:32:44 +00:00
|
|
|
pub const NAME_SELECTOR: Selector = [105, 31, 52, 49];
|
|
|
|
|
2022-02-16 14:25:41 +00:00
|
|
|
/// text(bytes32, string)
|
|
|
|
pub const FIELD_SELECTOR: Selector = [89, 209, 212, 60];
|
|
|
|
|
2022-08-17 21:13:51 +00:00
|
|
|
/// supportsInterface(bytes4 interfaceID)
|
|
|
|
pub const INTERFACE_SELECTOR: Selector = [1, 255, 201, 167];
|
|
|
|
|
2020-05-27 11:32:44 +00:00
|
|
|
/// Returns a transaction request for calling the `resolver` method on the ENS server
|
2022-09-19 17:51:04 +00:00
|
|
|
pub fn get_resolver<T: Into<NameOrAddress>>(ens_address: T, name: &str) -> TransactionRequest {
|
2020-05-27 11:32:44 +00:00
|
|
|
// keccak256('resolver(bytes32)')
|
|
|
|
let data = [&RESOLVER[..], &namehash(name).0].concat();
|
|
|
|
TransactionRequest {
|
|
|
|
data: Some(data.into()),
|
2022-09-19 17:51:04 +00:00
|
|
|
to: Some(ens_address.into()),
|
2020-05-27 11:32:44 +00:00
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-17 21:13:51 +00:00
|
|
|
/// Returns a transaction request for checking interface support
|
2022-09-19 17:51:04 +00:00
|
|
|
pub fn supports_interface<T: Into<NameOrAddress>>(
|
2022-08-17 21:13:51 +00:00
|
|
|
resolver_address: T,
|
|
|
|
selector: Selector,
|
|
|
|
) -> TransactionRequest {
|
|
|
|
let data = [&INTERFACE_SELECTOR[..], &selector[..], &[0; 28]].concat();
|
|
|
|
TransactionRequest {
|
|
|
|
data: Some(data.into()),
|
2022-09-19 17:51:04 +00:00
|
|
|
to: Some(resolver_address.into()),
|
2022-08-17 21:13:51 +00:00
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-28 16:34:06 +00:00
|
|
|
/// Returns a transaction request for calling
|
2022-09-19 17:51:04 +00:00
|
|
|
pub fn resolve<T: Into<NameOrAddress>>(
|
2020-05-27 11:32:44 +00:00
|
|
|
resolver_address: T,
|
|
|
|
selector: Selector,
|
|
|
|
name: &str,
|
2022-02-16 14:25:41 +00:00
|
|
|
parameters: Option<&[u8]>,
|
2020-05-27 11:32:44 +00:00
|
|
|
) -> TransactionRequest {
|
2022-02-16 14:25:41 +00:00
|
|
|
let data = [&selector[..], &namehash(name).0, parameters.unwrap_or_default()].concat();
|
2020-05-27 11:32:44 +00:00
|
|
|
TransactionRequest {
|
|
|
|
data: Some(data.into()),
|
2022-09-19 17:51:04 +00:00
|
|
|
to: Some(resolver_address.into()),
|
2020-05-27 11:32:44 +00:00
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-06 08:52:44 +00:00
|
|
|
/// Returns the reverse-registrar name of an address.
|
2020-05-27 11:32:44 +00:00
|
|
|
pub fn reverse_address(addr: Address) -> String {
|
2022-11-07 23:43:11 +00:00
|
|
|
format!("{addr:?}.{ENS_REVERSE_REGISTRAR_DOMAIN}")[2..].to_string()
|
2020-05-27 11:32:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the ENS namehash as specified in [EIP-137](https://eips.ethereum.org/EIPS/eip-137)
|
|
|
|
pub fn namehash(name: &str) -> H256 {
|
|
|
|
if name.is_empty() {
|
2021-10-29 12:29:35 +00:00
|
|
|
return H256::zero()
|
2020-05-27 11:32:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// iterate in reverse
|
|
|
|
name.rsplit('.')
|
2022-11-07 23:43:11 +00:00
|
|
|
.fold([0u8; 32], |node, label| keccak256([node, keccak256(label.as_bytes())].concat()))
|
2020-05-27 11:32:44 +00:00
|
|
|
.into()
|
|
|
|
}
|
|
|
|
|
2022-02-16 14:25:41 +00:00
|
|
|
/// Returns a number in bytes form with padding to fit in 32 bytes.
|
|
|
|
pub fn bytes_32ify(n: u64) -> Vec<u8> {
|
|
|
|
let b = n.to_be_bytes();
|
|
|
|
[[0; 32][b.len()..].to_vec(), b.to_vec()].concat()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the ENS record key hash [EIP-634](https://eips.ethereum.org/EIPS/eip-634)
|
|
|
|
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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:32:44 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
fn assert_hex(hash: H256, val: &str) {
|
2021-10-29 12:29:35 +00:00
|
|
|
let v = if let Some(stripped) = val.strip_prefix("0x") { stripped } else { val };
|
2020-05-27 11:32:44 +00:00
|
|
|
|
2020-12-31 17:19:14 +00:00
|
|
|
assert_eq!(hash.0.to_vec(), hex::decode(v).unwrap());
|
2020-05-27 11:32:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_namehash() {
|
|
|
|
for (name, expected) in &[
|
2021-10-29 12:29:35 +00:00
|
|
|
("", "0000000000000000000000000000000000000000000000000000000000000000"),
|
|
|
|
("foo.eth", "de9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f"),
|
|
|
|
("eth", "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"),
|
|
|
|
("alice.eth", "0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec"),
|
2020-05-27 11:32:44 +00:00
|
|
|
] {
|
|
|
|
assert_hex(namehash(name), expected);
|
|
|
|
}
|
|
|
|
}
|
2022-02-16 14:25:41 +00:00
|
|
|
|
|
|
|
#[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,
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
2020-05-27 11:32:44 +00:00
|
|
|
}
|