use alloy_signer::Signer as AlloySigner;
use alloy_signer_ledger::{HDPath, LedgerSigner as Inner};
use tokio::runtime::Runtime;
use crate::error::{Result, XmtpError};
use crate::types::{AccountIdentifier, IdentifierKind, Signer};
pub struct LedgerSigner {
inner: Inner,
rt: Runtime,
}
impl std::fmt::Debug for LedgerSigner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LedgerSigner")
.field("address", &self.address())
.finish_non_exhaustive()
}
}
impl LedgerSigner {
pub fn new(account_index: usize) -> Result<Self> {
Self::with_hd_path(HDPath::LedgerLive(account_index))
}
pub fn legacy(account_index: usize) -> Result<Self> {
Self::with_hd_path(HDPath::Legacy(account_index))
}
pub fn with_hd_path(hd_path: HDPath) -> Result<Self> {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.map_err(|e| XmtpError::Signing(e.to_string()))?;
let inner = rt
.block_on(Inner::new(hd_path, None))
.map_err(|e| XmtpError::Signing(e.to_string()))?;
Ok(Self { inner, rt })
}
#[must_use]
pub fn address(&self) -> String {
AlloySigner::address(&self.inner).to_checksum(None)
}
pub fn version(&self) -> Result<String> {
let ver = self
.rt
.block_on(self.inner.version())
.map_err(|e| XmtpError::Signing(e.to_string()))?;
Ok(ver.to_string())
}
}
impl Signer for LedgerSigner {
fn identifier(&self) -> AccountIdentifier {
AccountIdentifier {
address: AlloySigner::address(&self.inner).to_string().to_lowercase(),
kind: IdentifierKind::Ethereum,
}
}
fn sign(&self, text: &str) -> Result<Vec<u8>> {
let fut = self.inner.sign_message(text.as_bytes());
let sig = self
.rt
.block_on(fut)
.map_err(|e| XmtpError::Signing(e.to_string()))?;
Ok(sig.as_bytes().to_vec())
}
}