Skip to main content

ironclad_wallet/
lib.rs

1//! # ironclad-wallet
2//!
3//! Ethereum wallet management for the Ironclad agent runtime. Provides HD
4//! wallet generation, on-chain balance tracking, x402 payment protocol
5//! (EIP-3009), treasury policy enforcement, and DeFi yield optimization.
6//!
7//! ## Key Types
8//!
9//! - [`WalletService`] -- Top-level facade composing wallet, treasury, and yield engine
10//! - [`Wallet`] -- HD wallet with key loading/generation
11//! - [`TreasuryPolicy`] -- Spending limits and survival-tier-aware caps
12//! - [`YieldEngine`] -- DeFi yield optimization (Aave/Compound on Base)
13//! - [`X402Handler`] -- x402 payment protocol handler
14//! - [`Money`] -- USDC amount type with formatting
15//!
16//! ## Modules
17//!
18//! - `wallet` -- Wallet loading, generation, address, balance
19//! - `treasury` -- Treasury policy engine with per-payment caps and reserves
20//! - `yield_engine` -- DeFi protocol integration for idle capital
21//! - `x402` -- EIP-3009 `transferWithAuthorization` payment flow
22//! - `money` -- USDC amount type and arithmetic
23
24pub mod evm_submit;
25pub mod money;
26pub mod treasury;
27pub mod wallet;
28pub mod x402;
29pub mod yield_engine;
30
31pub use evm_submit::{
32    EvmContractCall, get_evm_transaction_receipt_status, submit_evm_contract_call,
33};
34pub use money::Money;
35pub use treasury::TreasuryPolicy;
36pub use wallet::{TokenBalance, Wallet};
37pub use x402::{PaymentRequirements, WalletPaymentHandler, X402Handler};
38pub use yield_engine::YieldEngine;
39
40use ironclad_core::Result;
41use ironclad_core::config::IroncladConfig;
42
43pub struct WalletService {
44    pub wallet: Wallet,
45    pub treasury: TreasuryPolicy,
46    pub yield_engine: YieldEngine,
47}
48
49impl WalletService {
50    pub async fn new(config: &IroncladConfig) -> Result<Self> {
51        let wallet = Wallet::load_or_generate(&config.wallet).await?;
52        let treasury = TreasuryPolicy::new(&config.treasury);
53        let yield_engine = YieldEngine::new(&config.r#yield);
54
55        Ok(Self {
56            wallet,
57            treasury,
58            yield_engine,
59        })
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    const MINIMAL_TOML: &str = r#"
68[agent]
69name = "TestBot"
70id = "test"
71
72[server]
73port = 9999
74
75[database]
76path = ":memory:"
77
78[models]
79primary = "ollama/qwen3:8b"
80"#;
81
82    #[tokio::test]
83    async fn wallet_service_new_with_temp_wallet_path() {
84        let dir = tempfile::tempdir().expect("temp dir");
85        let wallet_path = dir.path().join("wallet.json");
86
87        let mut config = IroncladConfig::from_str(MINIMAL_TOML).expect("parse config");
88        config.wallet.path = wallet_path;
89
90        let service = WalletService::new(&config)
91            .await
92            .expect("WalletService::new");
93        assert!(!service.wallet.address().is_empty());
94        assert_eq!(service.wallet.chain_id(), 8453);
95    }
96
97    #[tokio::test]
98    async fn wallet_service_new_uses_treasury_and_yield_from_config() {
99        let dir = tempfile::tempdir().expect("temp dir");
100        let mut config = IroncladConfig::from_str(MINIMAL_TOML).expect("parse config");
101        config.wallet.path = dir.path().join("wallet.json");
102
103        let service = WalletService::new(&config)
104            .await
105            .expect("WalletService::new");
106        let _ = &service.treasury;
107        let _ = &service.yield_engine;
108    }
109}