use super::error::SignerError;
use async_trait::async_trait;
use std::any::Any;
use std::sync::Arc;
pub trait SignerBase: Send + Sync + std::fmt::Debug + Any {
fn locale(&self) -> String {
"en".to_string()
}
fn user_id(&self) -> Option<String> {
None
}
fn as_any(&self) -> &dyn Any;
}
#[async_trait]
pub trait SolanaSigner: SignerBase {
fn address(&self) -> String;
fn pubkey(&self) -> String;
async fn sign_and_send_transaction(&self, tx: &mut Vec<u8>) -> Result<String, SignerError>;
async fn sign_and_send_with_retry(&self, tx: &mut Vec<u8>) -> Result<String, SignerError> {
self.sign_and_send_transaction(tx).await
}
fn client(&self) -> Arc<dyn std::any::Any + Send + Sync>;
}
#[async_trait]
pub trait EvmSigner: SignerBase {
fn chain_id(&self) -> u64;
fn address(&self) -> String;
async fn sign_and_send_transaction(&self, tx: serde_json::Value)
-> Result<String, SignerError>;
async fn sign_and_send_with_retry(&self, tx: serde_json::Value) -> Result<String, SignerError> {
self.sign_and_send_transaction(tx).await
}
fn client(&self) -> Result<Arc<dyn super::traits::EvmClient>, SignerError>;
}
#[async_trait]
pub trait MultiChainSigner: SignerBase {
fn primary_chain(&self) -> Chain;
fn supports_chain(&self, chain: &Chain) -> bool;
fn as_solana(&self) -> Option<&dyn SolanaSigner>;
fn as_evm(&self) -> Option<&dyn EvmSigner>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Chain {
Solana,
Evm {
chain_id: u64,
},
}
pub trait UnifiedSigner: SignerBase {
fn supports_solana(&self) -> bool;
fn supports_evm(&self) -> bool;
fn as_solana(&self) -> Option<&dyn SolanaSigner>;
fn as_evm(&self) -> Option<&dyn EvmSigner>;
fn as_multi_chain(&self) -> Option<&dyn MultiChainSigner>;
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct MockSigner {
locale: String,
user_id: Option<String>,
}
impl MockSigner {
fn new() -> Self {
Self {
locale: "en".to_string(),
user_id: None,
}
}
fn with_locale(mut self, locale: &str) -> Self {
self.locale = locale.to_string();
self
}
fn with_user_id(mut self, user_id: &str) -> Self {
self.user_id = Some(user_id.to_string());
self
}
}
impl SignerBase for MockSigner {
fn locale(&self) -> String {
self.locale.clone()
}
fn user_id(&self) -> Option<String> {
self.user_id.clone()
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl UnifiedSigner for MockSigner {
fn supports_solana(&self) -> bool {
false
}
fn supports_evm(&self) -> bool {
false
}
fn as_solana(&self) -> Option<&dyn SolanaSigner> {
None
}
fn as_evm(&self) -> Option<&dyn EvmSigner> {
None
}
fn as_multi_chain(&self) -> Option<&dyn MultiChainSigner> {
None
}
}
#[test]
fn test_chain_solana() {
let chain = Chain::Solana;
assert_eq!(chain, Chain::Solana);
}
#[test]
fn test_chain_evm() {
let chain = Chain::Evm { chain_id: 1 };
assert_eq!(chain, Chain::Evm { chain_id: 1 });
}
#[test]
fn test_chain_evm_different_ids() {
let chain1 = Chain::Evm { chain_id: 1 };
let chain2 = Chain::Evm { chain_id: 42 };
assert_ne!(chain1, chain2);
}
#[test]
fn test_signer_base_default_locale() {
let signer = MockSigner::new();
assert_eq!(signer.locale(), "en");
}
#[test]
fn test_signer_base_custom_locale() {
let signer = MockSigner::new().with_locale("fr");
assert_eq!(signer.locale(), "fr");
}
#[test]
fn test_signer_base_default_user_id() {
let signer = MockSigner::new();
assert_eq!(signer.user_id(), None);
}
#[test]
fn test_signer_base_custom_user_id() {
let signer = MockSigner::new().with_user_id("user123");
assert_eq!(signer.user_id(), Some("user123".to_string()));
}
#[test]
fn test_signer_base_as_any() {
let signer = MockSigner::new();
let any_ref = signer.as_any();
assert!(any_ref.is::<MockSigner>());
}
#[test]
fn test_unified_signer_supports_nothing() {
let signer = MockSigner::new();
assert!(!signer.supports_solana());
assert!(!signer.supports_evm());
assert!(signer.as_solana().is_none());
assert!(signer.as_evm().is_none());
assert!(signer.as_multi_chain().is_none());
}
#[test]
fn test_chain_debug() {
let solana_chain = Chain::Solana;
let evm_chain = Chain::Evm { chain_id: 1 };
let debug_str = format!("{:?}", solana_chain);
assert!(debug_str.contains("Solana"));
let debug_str = format!("{:?}", evm_chain);
assert!(debug_str.contains("Evm"));
assert!(debug_str.contains("1"));
}
#[test]
fn test_chain_clone() {
let chain = Chain::Evm { chain_id: 42 };
let cloned_chain = chain.clone();
assert_eq!(chain, cloned_chain);
}
}