use alloy::network::TransactionBuilder;
use alloy::primitives::{Address, B256, Bytes};
use alloy::rpc::types::TransactionRequest;
use alloy::sol_types::SolCall;
use super::wallet::EvmWallet;
use crate::wallet::WalletError;
const ENS_REGISTRY: Address =
alloy::primitives::address!("00000000000C2E074eC69A0dFb2997BA6C7d2e1e");
mod abi {
alloy::sol! {
function resolver(bytes32 node) external view returns (address);
function addr(bytes32 node) external view returns (address);
function name(bytes32 node) external view returns (string);
}
}
fn namehash(name: &str) -> B256 {
let mut node = B256::ZERO;
if name.is_empty() {
return node;
}
for label in name.rsplit('.') {
let label_hash = alloy::primitives::keccak256(label.as_bytes());
let mut buf = [0u8; 64];
buf[..32].copy_from_slice(node.as_slice());
buf[32..].copy_from_slice(label_hash.as_slice());
node = alloy::primitives::keccak256(buf);
}
node
}
impl EvmWallet {
pub async fn resolve_ens(&self, name: &str) -> Result<Option<Address>, WalletError> {
if self.chain_id() != 1 {
return Err(WalletError::provider(
"ENS resolution is only available on Ethereum mainnet".to_owned(),
));
}
let node = namehash(name);
let calldata = abi::resolverCall { node }.abi_encode();
let tx = TransactionRequest::default()
.with_to(ENS_REGISTRY)
.with_input(Bytes::from(calldata));
let result = self.call(tx).await?;
let resolver = abi::resolverCall::abi_decode_returns(&result)
.map_err(|e| WalletError::provider(format!("ENS resolver decode failed: {e}")))?;
if resolver.is_zero() {
return Ok(None);
}
let calldata = abi::addrCall { node }.abi_encode();
let tx = TransactionRequest::default()
.with_to(resolver)
.with_input(Bytes::from(calldata));
let result = self.call(tx).await?;
let addr = abi::addrCall::abi_decode_returns(&result)
.map_err(|e| WalletError::provider(format!("ENS addr decode failed: {e}")))?;
if addr.is_zero() {
Ok(None)
} else {
Ok(Some(addr))
}
}
pub async fn reverse_ens(&self, address: Address) -> Result<Option<String>, WalletError> {
if self.chain_id() != 1 {
return Err(WalletError::provider(
"ENS resolution is only available on Ethereum mainnet".to_owned(),
));
}
let addr_hex = alloy::primitives::hex::encode(address.as_slice());
let reverse_name = format!("{addr_hex}.addr.reverse");
let node = namehash(&reverse_name);
let calldata = abi::resolverCall { node }.abi_encode();
let tx = TransactionRequest::default()
.with_to(ENS_REGISTRY)
.with_input(Bytes::from(calldata));
let result = self.call(tx).await?;
let resolver = abi::resolverCall::abi_decode_returns(&result)
.map_err(|e| WalletError::provider(format!("ENS resolver decode failed: {e}")))?;
if resolver.is_zero() {
return Ok(None);
}
let calldata = abi::nameCall { node }.abi_encode();
let tx = TransactionRequest::default()
.with_to(resolver)
.with_input(Bytes::from(calldata));
let result = self.call(tx).await?;
let name = abi::nameCall::abi_decode_returns(&result)
.map_err(|e| WalletError::provider(format!("ENS name decode failed: {e}")))?;
if name.is_empty() {
Ok(None)
} else {
Ok(Some(name))
}
}
}