xenith-core 0.1.0

Transport-agnostic traits, types, and errors for xenith cross-chain state sync
Documentation
use async_trait::async_trait;
use bytes::Bytes;

use crate::{ChainId, SendOptions, XenithError};

/// Abstraction over a transaction signing key.
///
/// Implementations hold a private key and can sign raw EVM transactions ready
/// for broadcast. Transports that submit on-chain transactions (e.g. the
/// LayerZero live transport) accept an `Arc<dyn TransactionSigner>` in their
/// constructor so the signer is shared across async tasks.
///
/// # Example
///
/// ```rust,no_run
/// use xenith_core::{NoopSigner, TransactionSigner};
///
/// let signer = NoopSigner::new();
/// println!("address: {:?}", signer.address());
/// ```
#[async_trait]
pub trait TransactionSigner: Send + Sync {
    /// Returns the signer's Ethereum address as raw bytes.
    fn address(&self) -> [u8; 20];

    /// Sign a transaction and return raw signed transaction bytes ready for broadcast.
    ///
    /// The caller is responsible for nonce management; pass `options.nonce` explicitly
    /// or leave it as `None` to use 0 (a TODO for v0.2 automatic nonce fetching).
    async fn sign_transaction(
        &self,
        to: [u8; 20],
        calldata: Bytes,
        options: &SendOptions,
        chain_id: ChainId,
    ) -> Result<Bytes, XenithError>;
}

/// A no-op signer for testing and development. Returns calldata unchanged without
/// signing. Transactions built with this signer will be rejected by any real EVM node.
///
/// For production use, see `K256Signer` in `xenith-layerzero`.
///
/// # Example
///
/// ```
/// use xenith_core::{NoopSigner, TransactionSigner};
/// let signer = NoopSigner::new();
/// assert_eq!(signer.address(), [0u8; 20]);
/// ```
#[derive(Debug, Default)]
pub struct NoopSigner;

impl NoopSigner {
    /// Create a new no-op signer.
    pub fn new() -> Self {
        Self
    }
}

#[async_trait]
impl TransactionSigner for NoopSigner {
    fn address(&self) -> [u8; 20] {
        [0u8; 20]
    }

    async fn sign_transaction(
        &self,
        _to: [u8; 20],
        calldata: Bytes,
        _options: &SendOptions,
        _chain_id: ChainId,
    ) -> Result<Bytes, XenithError> {
        // Returns calldata unchanged. This is intentionally a no-op.
        // Use K256Signer from xenith-layerzero for real transaction signing.
        Ok(calldata)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn noop_signer_address_is_zero() {
        let s = NoopSigner::new();
        assert_eq!(s.address(), [0u8; 20]);
    }
}