Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

feat: add fn parse_checksummed #2372

Merged
merged 4 commits into from
Apr 25, 2023
Merged

Conversation

izayl
Copy link
Contributor

@izayl izayl commented Apr 22, 2023

Motivation

as ethers-rs provide to_checksum function, but there not provide a way to verify a checksum-like address.
so I add a utilities function to verify a checksum address is valid.

Solution

PR Checklist

  • Added Tests
  • Added Documentation
  • Breaking changes

Comment on lines 441 to 442
// addr is not checksummed address
true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be false, no?

Copy link
Contributor Author

@izayl izayl Apr 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right, I have fixed it

ethers-core/src/utils/mod.rs Outdated Show resolved Hide resolved
ethers-core/src/utils/mod.rs Outdated Show resolved Hide resolved
@@ -421,6 +421,27 @@ pub fn to_checksum(addr: &Address, chain_id: Option<u8>) -> String {
})
}

/// checks if the given address is a valid checksum address
pub fn verify_checksum(addr: &str, chain_id: Option<u8>) -> bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't love the name,
also not a fan of the to_checksum function, should have been named to_checksum_address
but, to keep it consistent, this is fine I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update ethers-core/src/utils/mod.rs

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>

refactor: remove char case branch
Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

smol style nit

Comment on lines 429 to 438
let address = addr.parse::<Address>();

// wrong address string
if address.is_err() {
return false
}

let checksum_addr = to_checksum(&address.unwrap(), chain_id);

return checksum_addr.strip_prefix("0x").unwrap_or(&checksum_addr) == addr
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use

if let Ok() {
let checksum_addr = to_checksum(&address.unwrap(), chain_id);

    return checksum_addr.strip_prefix("0x").unwrap_or(&checksum_addr) == addr

}
false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Collaborator

@prestwich prestwich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API change

/// checks if the given address is a valid checksum address
///
/// Returns `true` if addr is a valid checksum address, `false` otherwise.
pub fn verify_checksum(addr: &str, chain_id: Option<u8>) -> bool {
Copy link
Collaborator

@prestwich prestwich Apr 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why return a bool instead of the address? We're doing the parsing work regardless, and the user can always call is_some if they need a bool

basically shouldn't this be parse_checksummed instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like this?

/// Returns a `Result<Address>`: `Ok(Address)` if addr is checksummed, `Err()` otherwise
pub fn parse_checksummed(addr: &str, chain_id: Option<u8>) -> Result<Address>

Copy link
Collaborator

@prestwich prestwich Apr 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function signature is awesome 👌

I'd write docs similar to this:

/// Parses an [EIP-1191](https://eips.ethereum.org/EIPS/eip-1191) checksum address. Returns `Ok(address)` if the checksummed address is valid, `Err()` otherwise. If `chain_id` is `None`, falls back to [EIP-55](https://eips.ethereum.org/EIPS/eip-55) address checksum method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@prestwich refactored, pls check it

@izayl izayl changed the title feat: add fn verify_checksum feat: add fn parse_checksummed Apr 25, 2023
return Err(ConversionError::InvalidAddressChecksum)
}
}
Err(ConversionError::TextTooLong)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than returning text too long, I think we can do the following

  1. add a hex error to the ConversionError enum
enum ConversionError { 
    ...,
    /// Hex error, usually from address parsing
    #[error(transparent)]
    FromHexError(#[from]hex::FromHexError)
}
  1. change this function to use a ?
        let addr: Address = addr.strip_prefix("0x").unwrap_or(addr).parse()?
        if checksum_addr.strip_prefix("0x").unwrap_or(&checksum_addr) == addr {
            return Ok(address)
        } else {
            return Err(ConversionError::InvalidAddressChecksum)
        }


Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the FromHexError is from rustc_hex crate, should I add a it?

Copy link
Contributor Author

@izayl izayl Apr 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leave it to parity to make our lives more complicated 🤣 🙄

I think you should be able to make it work as follows:

  1. reference the fully qualified associated type <Address as std::str::FromStr>::Err in the enum variant
  2. instead of doing a #[from] in the error variant, use map_err to convert
// variant specifies that it takes "whatever error the `parse()` implementation says is the error"
FromHexError(<Address as std::str::FromStr>::Err)

// map err on the first line of the function
let addr: Address = addr.strip_prefix("0x").unwrap_or(addr).parse().map_err(ConversionError::FromHexError)?;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry this is a pain. if it gets too much let me know and i'll do the tidying

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@prestwich I've add this FromHexError

@prestwich
Copy link
Collaborator

changes are good. 1 more small ergonomics tweak 🙏

Copy link
Collaborator

@prestwich prestwich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving, pending CI run completion. Thanks for the contribution!

@prestwich prestwich self-assigned this Apr 25, 2023
@prestwich prestwich merged commit 1ee6b8f into gakonst:master Apr 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants