use crate::Result;
use crate::transaction::{Signer as SignerTrait, Signature as TxSignature};
use alloy_primitives::{Address, B256};
use alloy_signer::SignerSync;
use alloy_signer_local::PrivateKeySigner;
use rand::RngCore;
#[derive(Debug)]
pub struct EcdsaSigner {
signer: PrivateKeySigner,
}
impl SignerTrait for EcdsaSigner {
fn sign_hash(&self, hash: &B256) -> std::result::Result<TxSignature, Box<dyn std::error::Error>> {
let signature = self.signer.sign_hash_sync(hash)?;
let v = if signature.v() { 1 } else { 0 };
Ok(TxSignature::new(
signature.r().into(),
signature.s().into(),
v,
))
}
fn address(&self) -> Address {
self.signer.address()
}
}
impl EcdsaSigner {
pub fn from_private_key(private_key: &str) -> Result<Self> {
let key = private_key.trim_start_matches("0x");
let bytes = hex::decode(key).map_err(|e| crate::Error::InvalidHex(e.to_string()))?;
if bytes.len() != 32 {
return Err(crate::Error::InvalidPrivateKey("private key must be 32 bytes".into()));
}
let signer = PrivateKeySigner::from_slice(&bytes)
.map_err(|e| crate::Error::InvalidPrivateKey(e.to_string()))?;
Ok(Self { signer })
}
}
pub use crate::transaction::Signer as Signer;
#[derive(Debug, Clone)]
pub struct Wallet {
pub address: String,
pub private_key: Option<String>,
}
impl Wallet {
pub fn generate() -> Result<Self> {
let mut key = [0u8; 32];
rand::thread_rng().fill_bytes(&mut key);
let signer = EcdsaSigner::from_private_key(&hex::encode(key))?;
Ok(Self {
address: signer.address().to_string(),
private_key: Some(format!("0x{}", hex::encode(key))),
})
}
pub fn from_private_key(private_key: &str) -> Result<Self> {
let signer = EcdsaSigner::from_private_key(private_key)?;
Ok(Self {
address: signer.address().to_string(),
private_key: Some(private_key.to_string()),
})
}
}
pub fn generate_private_key() -> String {
let mut key = [0u8; 32];
rand::thread_rng().fill_bytes(&mut key);
format!("0x{}", hex::encode(key))
}
pub fn private_key_to_address(private_key: &str) -> Result<String> {
let signer = EcdsaSigner::from_private_key(private_key)?;
Ok(signer.address().to_string())
}