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