alloy_signer/
utils.rs

1//! Utility functions for working with Ethereum signatures.
2
3use alloy_primitives::{keccak256, Address};
4use elliptic_curve::sec1::ToEncodedPoint;
5use k256::ecdsa::{SigningKey, VerifyingKey};
6
7/// Converts an ECDSA private key to its corresponding Ethereum Address.
8#[inline]
9pub fn secret_key_to_address(secret_key: &SigningKey) -> Address {
10    public_key_to_address(secret_key.verifying_key())
11}
12
13/// Converts an ECDSA public key to its corresponding Ethereum address.
14#[inline]
15pub fn public_key_to_address(pubkey: &VerifyingKey) -> Address {
16    let affine = pubkey.as_ref();
17    let encoded = affine.to_encoded_point(false);
18    raw_public_key_to_address(&encoded.as_bytes()[1..])
19}
20
21/// Convert a raw, uncompressed public key to its corresponding Ethereum address.
22///
23/// ### Warning
24///
25/// This method **does not** verify that the public key is valid. It is the
26/// caller's responsibility to pass a valid public key. Passing an invalid
27/// public key will produce an unspendable output.
28///
29/// # Panics
30///
31/// This function panics if the input is not **exactly** 64 bytes.
32#[inline]
33#[track_caller]
34pub fn raw_public_key_to_address(pubkey: &[u8]) -> Address {
35    assert_eq!(pubkey.len(), 64, "raw public key must be 64 bytes");
36    let digest = keccak256(pubkey);
37    Address::from_slice(&digest[12..])
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use alloy_primitives::hex;
44
45    // Only tests for correctness, no edge cases. Uses examples from https://docs.ethers.org/v5/api/utils/address/#utils-computeAddress
46    #[test]
47    fn test_public_key_to_address() {
48        let addr = "0Ac1dF02185025F65202660F8167210A80dD5086".parse::<Address>().unwrap();
49
50        // Compressed
51        let pubkey = VerifyingKey::from_sec1_bytes(
52            &hex::decode("0376698beebe8ee5c74d8cc50ab84ac301ee8f10af6f28d0ffd6adf4d6d3b9b762")
53                .unwrap(),
54        )
55        .unwrap();
56        assert_eq!(public_key_to_address(&pubkey), addr);
57
58        // Uncompressed
59        let pubkey= VerifyingKey::from_sec1_bytes(&hex::decode("0476698beebe8ee5c74d8cc50ab84ac301ee8f10af6f28d0ffd6adf4d6d3b9b762d46ca56d3dad2ce13213a6f42278dabbb53259f2d92681ea6a0b98197a719be3").unwrap()).unwrap();
60        assert_eq!(public_key_to_address(&pubkey), addr);
61    }
62
63    #[test]
64    fn test_raw_public_key_to_address() {
65        let addr = "0Ac1dF02185025F65202660F8167210A80dD5086".parse::<Address>().unwrap();
66
67        let pubkey_bytes = hex::decode("76698beebe8ee5c74d8cc50ab84ac301ee8f10af6f28d0ffd6adf4d6d3b9b762d46ca56d3dad2ce13213a6f42278dabbb53259f2d92681ea6a0b98197a719be3").unwrap();
68
69        assert_eq!(raw_public_key_to_address(&pubkey_bytes), addr);
70    }
71
72    #[test]
73    #[should_panic]
74    fn test_raw_public_key_to_address_panics() {
75        raw_public_key_to_address(&[]);
76    }
77}