feat(core): add utility methods to NameOrAddress (#1720)
* feat: NameOrAddress impls * feat: add to_addr to TypedTransaction * fix: use NameOrAddress for ens * chore: clippy * dont clone
This commit is contained in:
parent
59a82a1c8f
commit
db75e3628e
|
@ -7,7 +7,7 @@ use crate::{
|
|||
|
||||
use ethers_core::{
|
||||
abi::{Abi, Detokenize, Error, EventExt, Function, Tokenize},
|
||||
types::{Address, Filter, NameOrAddress, Selector, ValueOrArray},
|
||||
types::{Address, Filter, Selector, ValueOrArray},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "legacy"))]
|
||||
|
@ -232,13 +232,13 @@ impl<M: Middleware> Contract<M> {
|
|||
|
||||
#[cfg(feature = "legacy")]
|
||||
let tx = TransactionRequest {
|
||||
to: Some(NameOrAddress::Address(self.address)),
|
||||
to: Some(self.address.into()),
|
||||
data: Some(data),
|
||||
..Default::default()
|
||||
};
|
||||
#[cfg(not(feature = "legacy"))]
|
||||
let tx = Eip1559TransactionRequest {
|
||||
to: Some(NameOrAddress::Address(self.address)),
|
||||
to: Some(self.address.into()),
|
||||
data: Some(data),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use crate::types::Address;
|
||||
use rlp::{Decodable, Encodable, RlpStream};
|
||||
use serde::{ser::Error as SerializationError, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::{cmp::Ordering, convert::Infallible, str::FromStr};
|
||||
|
||||
/// ENS name or Ethereum Address. Not RLP encoded/serialized if it's a name
|
||||
/// ENS name or Ethereum Address. Not RLP encoded/serialized if it's a name.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum NameOrAddress {
|
||||
/// An ENS Name (format does not get checked)
|
||||
|
@ -25,7 +24,7 @@ impl Encodable for &NameOrAddress {
|
|||
|
||||
impl Encodable for NameOrAddress {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
if let NameOrAddress::Address(inner) = self {
|
||||
if let Self::Address(inner) = self {
|
||||
inner.rlp_append(s);
|
||||
}
|
||||
}
|
||||
|
@ -39,29 +38,17 @@ impl Decodable for NameOrAddress {
|
|||
}
|
||||
|
||||
// the data needs to be 20 bytes long
|
||||
match 20.cmp(&rlp.size()) {
|
||||
match rlp.size().cmp(&20usize) {
|
||||
Ordering::Less => Err(rlp::DecoderError::RlpIsTooShort),
|
||||
Ordering::Greater => Err(rlp::DecoderError::RlpIsTooBig),
|
||||
Ordering::Equal => {
|
||||
let rlp_data = rlp.data()?;
|
||||
Ok(NameOrAddress::Address(Address::from_slice(rlp_data)))
|
||||
Ok(Self::Address(Address::from_slice(rlp_data)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for NameOrAddress {
|
||||
fn from(s: &str) -> Self {
|
||||
NameOrAddress::Name(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Address> for NameOrAddress {
|
||||
fn from(s: Address) -> Self {
|
||||
NameOrAddress::Address(s)
|
||||
}
|
||||
}
|
||||
|
||||
// Only serialize the Address variant since it doesn't make sense to ever serialize
|
||||
// an ENS name
|
||||
impl Serialize for NameOrAddress {
|
||||
|
@ -70,8 +57,8 @@ impl Serialize for NameOrAddress {
|
|||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
NameOrAddress::Address(addr) => addr.serialize(serializer),
|
||||
NameOrAddress::Name(name) => Err(SerializationError::custom(format!(
|
||||
Self::Address(addr) => addr.serialize(serializer),
|
||||
Self::Name(name) => Err(SerializationError::custom(format!(
|
||||
"cannot serialize ENS name {}, must be address",
|
||||
name
|
||||
))),
|
||||
|
@ -85,8 +72,57 @@ impl<'de> Deserialize<'de> for NameOrAddress {
|
|||
D: Deserializer<'de>,
|
||||
{
|
||||
let inner = Address::deserialize(deserializer)?;
|
||||
Ok(Self::Address(inner))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(NameOrAddress::Address(inner))
|
||||
impl From<&str> for NameOrAddress {
|
||||
fn from(s: &str) -> Self {
|
||||
Self::from_str(s).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for NameOrAddress {
|
||||
fn from(s: String) -> Self {
|
||||
Self::Name(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for NameOrAddress {
|
||||
fn from(s: &String) -> Self {
|
||||
Self::Name(s.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Address> for NameOrAddress {
|
||||
fn from(s: Address) -> Self {
|
||||
Self::Address(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for NameOrAddress {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self::Name(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl NameOrAddress {
|
||||
/// Maps Address(a) to Some(a) and Name to None.
|
||||
pub fn as_address(&self) -> Option<&Address> {
|
||||
match self {
|
||||
Self::Address(a) => Some(a),
|
||||
Self::Name(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps Name(n) to Some(n) and Address to None.
|
||||
pub fn as_name(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::Address(_) => None,
|
||||
Self::Name(n) => Some(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,6 +104,10 @@ impl TypedTransaction {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_addr(&self) -> Option<&Address> {
|
||||
self.to().and_then(|t| t.as_address())
|
||||
}
|
||||
|
||||
pub fn set_to<T: Into<NameOrAddress>>(&mut self, to: T) -> &mut Self {
|
||||
let to = to.into();
|
||||
match self {
|
||||
|
|
|
@ -545,9 +545,7 @@ mod tests {
|
|||
"0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f",
|
||||
];
|
||||
|
||||
let addresses = raw_addresses
|
||||
.iter()
|
||||
.map(|addr| NameOrAddress::Address(Address::from_str(addr).unwrap()));
|
||||
let addresses = raw_addresses.iter().map(|addr| addr.parse::<Address>().unwrap().into());
|
||||
|
||||
// decoding will do sender recovery and we don't expect any of these to error, so we should
|
||||
// check that the address matches for each decoded transaction
|
||||
|
|
|
@ -177,17 +177,15 @@ impl DsProxy {
|
|||
impl Transformer for DsProxy {
|
||||
fn transform(&self, tx: &mut TypedTransaction) -> Result<(), TransformerError> {
|
||||
// the target address cannot be None.
|
||||
let target = match tx.to() {
|
||||
Some(NameOrAddress::Address(addr)) => addr,
|
||||
_ => return Err(TransformerError::MissingField("to".into())),
|
||||
};
|
||||
let target =
|
||||
*tx.to_addr().ok_or_else(|| TransformerError::MissingField("to".to_string()))?;
|
||||
|
||||
// fetch the data field.
|
||||
let data = tx.data().cloned().unwrap_or_else(|| vec![].into());
|
||||
|
||||
// encode data as the ABI encoded data for DSProxy's execute method.
|
||||
let selector = id("execute(address,bytes)");
|
||||
let encoded_data = self.contract.encode_with_selector(selector, (*target, data))?;
|
||||
let encoded_data = self.contract.encode_with_selector(selector, (target, data))?;
|
||||
|
||||
// update appropriate fields of the proxy tx.
|
||||
tx.set_data(encoded_data);
|
||||
|
|
|
@ -32,31 +32,31 @@ pub const FIELD_SELECTOR: Selector = [89, 209, 212, 60];
|
|||
pub const INTERFACE_SELECTOR: Selector = [1, 255, 201, 167];
|
||||
|
||||
/// 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<NameOrAddress>>(ens_address: T, name: &str) -> TransactionRequest {
|
||||
// keccak256('resolver(bytes32)')
|
||||
let data = [&RESOLVER[..], &namehash(name).0].concat();
|
||||
TransactionRequest {
|
||||
data: Some(data.into()),
|
||||
to: Some(NameOrAddress::Address(ens_address.into())),
|
||||
to: Some(ens_address.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a transaction request for checking interface support
|
||||
pub fn supports_interface<T: Into<Address>>(
|
||||
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(NameOrAddress::Address(resolver_address.into())),
|
||||
to: Some(resolver_address.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a transaction request for calling
|
||||
pub fn resolve<T: Into<Address>>(
|
||||
pub fn resolve<T: Into<NameOrAddress>>(
|
||||
resolver_address: T,
|
||||
selector: Selector,
|
||||
name: &str,
|
||||
|
@ -65,7 +65,7 @@ pub fn resolve<T: Into<Address>>(
|
|||
let data = [&selector[..], &namehash(name).0, parameters.unwrap_or_default()].concat();
|
||||
TransactionRequest {
|
||||
data: Some(data.into()),
|
||||
to: Some(NameOrAddress::Address(resolver_address.into())),
|
||||
to: Some(resolver_address.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ async fn main() -> Result<()> {
|
|||
// fork mainnet
|
||||
let anvil =
|
||||
Anvil::new().fork("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27").spawn();
|
||||
let from = anvil.addresses()[0].clone();
|
||||
let from = anvil.addresses()[0];
|
||||
// connect to the network
|
||||
let provider = Provider::<Http>::try_from(anvil.endpoint()).unwrap().with_sender(from);
|
||||
|
||||
|
|
Loading…
Reference in New Issue