Skip to main content

altius_tx_sdk/
signer.rs

1//! Signer implementation for Altius transactions
2
3use crate::Result;
4use crate::transaction::{Signer as SignerTrait, Signature as TxSignature};
5use alloy_primitives::{Address, B256};
6use alloy_signer::SignerSync;
7use alloy_signer_local::PrivateKeySigner;
8use rand::RngCore;
9
10/// A transaction signer using real ECDSA signing
11#[derive(Debug)]
12pub struct EcdsaSigner {
13    signer: PrivateKeySigner,
14}
15
16impl SignerTrait for EcdsaSigner {
17    fn sign_hash(&self, hash: &B256) -> std::result::Result<TxSignature, Box<dyn std::error::Error>> {
18        // Sign the hash with real ECDSA using sign_hash_sync
19        let signature = self.signer.sign_hash_sync(hash)?;
20        // v is returned as bool from alloy, convert to u8 (0 or 1)
21        let v = if signature.v() { 1 } else { 0 };
22        Ok(TxSignature::new(
23            signature.r().into(),
24            signature.s().into(),
25            v,
26        ))
27    }
28
29    fn address(&self) -> Address {
30        self.signer.address()
31    }
32}
33
34impl EcdsaSigner {
35    /// Access the underlying PrivateKeySigner for advanced use cases (e.g. EIP-1559 signing).
36    pub fn inner(&self) -> &PrivateKeySigner {
37        &self.signer
38    }
39
40    /// Create a signer from a private key hex string
41    pub fn from_private_key(private_key: &str) -> Result<Self> {
42        let key = private_key.trim_start_matches("0x");
43        let bytes = hex::decode(key).map_err(|e| crate::Error::InvalidHex(e.to_string()))?;
44
45        if bytes.len() != 32 {
46            return Err(crate::Error::InvalidPrivateKey("private key must be 32 bytes".into()));
47        }
48
49        let signer = PrivateKeySigner::from_slice(&bytes)
50            .map_err(|e| crate::Error::InvalidPrivateKey(e.to_string()))?;
51
52        Ok(Self { signer })
53    }
54}
55
56/// Re-export SignerTrait as Signer for API compatibility
57pub use crate::transaction::Signer as Signer;
58
59/// A wallet with address and optional private key
60#[derive(Debug, Clone)]
61pub struct Wallet {
62    pub address: String,
63    pub private_key: Option<String>,
64}
65
66impl Wallet {
67    pub fn generate() -> Result<Self> {
68        let mut key = [0u8; 32];
69        rand::thread_rng().fill_bytes(&mut key);
70
71        let signer = EcdsaSigner::from_private_key(&hex::encode(key))?;
72        Ok(Self {
73            address: signer.address().to_string(),
74            private_key: Some(format!("0x{}", hex::encode(key))),
75        })
76    }
77
78    pub fn from_private_key(private_key: &str) -> Result<Self> {
79        let signer = EcdsaSigner::from_private_key(private_key)?;
80        Ok(Self {
81            address: signer.address().to_string(),
82            private_key: Some(private_key.to_string()),
83        })
84    }
85}
86
87/// Generate a new private key
88pub fn generate_private_key() -> String {
89    let mut key = [0u8; 32];
90    rand::thread_rng().fill_bytes(&mut key);
91    format!("0x{}", hex::encode(key))
92}
93
94/// Convert private key to address
95pub fn private_key_to_address(private_key: &str) -> Result<String> {
96    let signer = EcdsaSigner::from_private_key(private_key)?;
97    Ok(signer.address().to_string())
98}