fix(providers): validate address resolver (#1605)
* validate address resolver * use Selector type instead * specify which ens_name on resolver error * pad supportsInterface argument
This commit is contained in:
parent
ffcf10d6b3
commit
d22ab5de0a
|
@ -28,6 +28,9 @@ pub const NAME_SELECTOR: Selector = [105, 31, 52, 49];
|
||||||
/// text(bytes32, string)
|
/// text(bytes32, string)
|
||||||
pub const FIELD_SELECTOR: Selector = [89, 209, 212, 60];
|
pub const FIELD_SELECTOR: Selector = [89, 209, 212, 60];
|
||||||
|
|
||||||
|
/// supportsInterface(bytes4 interfaceID)
|
||||||
|
pub const INTERFACE_SELECTOR: Selector = [1, 255, 201, 167];
|
||||||
|
|
||||||
/// Returns a transaction request for calling the `resolver` method on the ENS server
|
/// Returns a transaction request for calling the `resolver` method on the ENS server
|
||||||
pub fn get_resolver<T: Into<Address>>(ens_address: T, name: &str) -> TransactionRequest {
|
pub fn get_resolver<T: Into<Address>>(ens_address: T, name: &str) -> TransactionRequest {
|
||||||
// keccak256('resolver(bytes32)')
|
// keccak256('resolver(bytes32)')
|
||||||
|
@ -39,6 +42,19 @@ pub fn get_resolver<T: Into<Address>>(ens_address: T, name: &str) -> Transaction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a transaction request for checking interface support
|
||||||
|
pub fn supports_interface<T: Into<Address>>(
|
||||||
|
resolver_address: T,
|
||||||
|
selector: Selector,
|
||||||
|
) -> TransactionRequest {
|
||||||
|
let data = [&INTERFACE_SELECTOR[..], &selector[..], &[0; 28]].concat();
|
||||||
|
TransactionRequest {
|
||||||
|
data: Some(data.into()),
|
||||||
|
to: Some(NameOrAddress::Address(resolver_address.into())),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a transaction request for calling
|
/// Returns a transaction request for calling
|
||||||
pub fn resolve<T: Into<Address>>(
|
pub fn resolve<T: Into<Address>>(
|
||||||
resolver_address: T,
|
resolver_address: T,
|
||||||
|
|
|
@ -1263,6 +1263,11 @@ impl<P: JsonRpcClient> Provider<P> {
|
||||||
return Err(ProviderError::EnsError(ens_name.to_string()))
|
return Err(ProviderError::EnsError(ens_name.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let ParamType::Address = param {
|
||||||
|
// Reverse resolver reverts when calling `supportsInterface(bytes4)`
|
||||||
|
self.validate_resolver(resolver_address, selector, ens_name).await?;
|
||||||
|
}
|
||||||
|
|
||||||
// resolve
|
// resolve
|
||||||
let data = self
|
let data = self
|
||||||
.call(&ens::resolve(resolver_address, selector, ens_name, parameters).into(), None)
|
.call(&ens::resolve(resolver_address, selector, ens_name, parameters).into(), None)
|
||||||
|
@ -1271,6 +1276,39 @@ impl<P: JsonRpcClient> Provider<P> {
|
||||||
Ok(decode_bytes(param, data))
|
Ok(decode_bytes(param, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates that the resolver supports `selector`.
|
||||||
|
async fn validate_resolver(
|
||||||
|
&self,
|
||||||
|
resolver_address: Address,
|
||||||
|
selector: Selector,
|
||||||
|
ens_name: &str,
|
||||||
|
) -> Result<(), ProviderError> {
|
||||||
|
let data =
|
||||||
|
self.call(&ens::supports_interface(resolver_address, selector).into(), None).await?;
|
||||||
|
|
||||||
|
if data.is_empty() {
|
||||||
|
return Err(ProviderError::EnsError(format!(
|
||||||
|
"`{}` resolver ({:?}) is invalid.",
|
||||||
|
ens_name, resolver_address
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
let supports_selector = abi::decode(&[ParamType::Bool], data.as_ref())
|
||||||
|
.map(|token| token[0].clone().into_bool().unwrap_or_default())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if !supports_selector {
|
||||||
|
return Err(ProviderError::EnsError(format!(
|
||||||
|
"`{}` resolver ({:?}) does not support selector {}.",
|
||||||
|
ens_name,
|
||||||
|
resolver_address,
|
||||||
|
hex::encode(&selector)
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// Anvil and Ganache-only function for mining empty blocks
|
/// Anvil and Ganache-only function for mining empty blocks
|
||||||
pub async fn mine(&self, num_blocks: usize) -> Result<(), ProviderError> {
|
pub async fn mine(&self, num_blocks: usize) -> Result<(), ProviderError> {
|
||||||
|
@ -2133,4 +2171,19 @@ mod tests {
|
||||||
assert_eq!(tx.gas_price(), Some(gas_price));
|
assert_eq!(tx.gas_price(), Some(gas_price));
|
||||||
assert!(tx.access_list().is_none());
|
assert!(tx.access_list().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn mainnet_lookup_address_invalid_resolver() {
|
||||||
|
let provider = crate::MAINNET.provider();
|
||||||
|
|
||||||
|
let err = provider
|
||||||
|
.lookup_address("0x30c9223d9e3d23e0af1073a38e0834b055bf68ed".parse().unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap_err();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&err.to_string(),
|
||||||
|
"ens name not found: `ox63616e.eth` resolver (0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) is invalid."
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue