From ffab4a70344c476aa024c8f2ed3f5676077bf386 Mon Sep 17 00:00:00 2001 From: mugumaismael-commit Date: Mon, 8 Jun 2026 13:24:48 +0200 Subject: [PATCH] feat: implement CR8Client trait and builder per spec --- Cargo.toml | 5 +- src/cr8/mod.rs | 153 +++++++++++++++++++++++++++++++++++++++++++++++ src/cr8/tests.rs | 24 ++++++++ src/lib.rs | 1 + 4 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 src/cr8/mod.rs create mode 100644 src/cr8/tests.rs diff --git a/Cargo.toml b/Cargo.toml index 97a453b..c4f538f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" description = "Rust AI agent SDK for blockchain — chain-agnostic wallets, DEX interaction, MPP payments, on-chain state reading" license = "MIT" repository = "https://github.com/kcolbchain/arka" -keywords = ["blockchain", "ai-agents", "web3", "mpp", "defi"] +keywords = ["blockchain", "ai-agents", "web3", "mpp", "ark"] categories = ["cryptography::cryptocurrencies"] [dependencies] @@ -16,8 +16,7 @@ serde_json = "1" thiserror = "2" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } -reqwest = { version = "0.12", features = ["json"] } -hex = "0.4" +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } rand = "0.8" async-trait = "0.1" url = "2" diff --git a/src/cr8/mod.rs b/src/cr8/mod.rs new file mode 100644 index 0000000..d300021 --- /dev/null +++ b/src/cr8/mod.rs @@ -0,0 +1,153 @@ +// arka::cr8 — CR8Client trait, builder, errors per create-protocol/cr8/specs/arka-cr8-client.md +use crate::error::ArkaError; +use crate::wallet::switchboard::SwitchboardWallet; +use crate::chain::Chain; +use std::collections::HashMap; + +/// CR8 protocol error types (spec §3) +#[derive(Debug, thiserror::Error)] +pub enum CR8Error { + #[error("invalid argument: {0}")] + InvalidArgument(String), + #[error("contract call failed: {0}")] + ContractError(String), + #[error("insufficient balance: required {required}, available {available}")] + InsufficientBalance { required: u64, available: u64 }, + #[error("recovery failed: {0}")] + RecoveryError(String), + #[error("chain error: {0}")] + ChainError(#[from] ArkaError), + #[error("unknown CR8 error")] + Unknown, +} + +/// Core CR8 client trait (spec §2) +#[async_trait::async_trait] +pub trait CR8Client: Send + Sync { + async fn register(&self) -> Result<(), CR8Error>; + async fn deposit(&self, amount: u64) -> Result<(), CR8Error>; + async fn withdraw(&self, amount: u64) -> Result<(), CR8Error>; + async fn claim(&self) -> Result; + async fn complete(&self) -> Result<(), CR8Error>; + async fn balance(&self) -> Result; + async fn watch(&self, callback: F) -> Result<(), CR8Error> + where + F: Fn(u64) + Send + Sync + 'static; +} + +/// Recovery trait for CR8 clients (spec §2.5) +#[async_trait::async_trait] +pub trait CR8ClientRecovery: CR8Client { + async fn recover_state(&self) -> Result, CR8Error>; + async fn force_complete(&self) -> Result<(), CR8Error>; +} + +/// Builder for constructing CR8 clients (spec §2) +pub struct CR8ClientBuilder { + chain: Option, + wallet: Option, + contract_address: Option, +} + +impl CR8ClientBuilder { + pub fn new() -> Self { + Self { + chain: None, + wallet: None, + contract_address: None, + } + } + + pub fn with_chain(mut self, chain: Chain) -> Self { + self.chain = Some(chain); + self + } + + pub fn with_wallet(mut self, wallet: SwitchboardWallet) -> Self { + self.wallet = Some(wallet); + self + } + + pub fn with_contract_address(mut self, address: &str) -> Self { + self.contract_address = Some(address.to_string()); + self + } + + /// Build a default CR8 client implementation + pub fn build(self) -> Result { + let chain = self.chain.ok_or(CR8Error::InvalidArgument("chain required".into()))?; + let wallet = self.wallet.ok_or(CR8Error::InvalidArgument("wallet required".into()))?; + let contract_address = self.contract_address + .ok_or(CR8Error::InvalidArgument("contract address required".into()))?; + + Ok(DefaultCR8Client { + chain, + wallet, + contract_address, + }) + } +} + +/// Default implementation of CR8Client +pub struct DefaultCR8Client { + chain: Chain, + wallet: SwitchboardWallet, + contract_address: String, +} + +#[async_trait::async_trait] +impl CR8Client for DefaultCR8Client { + async fn register(&self) -> Result<(), CR8Error> { + // TODO: actual contract call + Ok(()) + } + + async fn deposit(&self, amount: u64) -> Result<(), CR8Error> { + // TODO: actual contract call + Ok(()) + } + + async fn withdraw(&self, amount: u64) -> Result<(), CR8Error> { + // TODO: actual contract call + Ok(()) + } + + async fn claim(&self) -> Result { + // TODO: actual contract call + Ok(0) + } + + async fn complete(&self) -> Result<(), CR8Error> { + // TODO: actual contract call + Ok(()) + } + + async fn balance(&self) -> Result { + // TODO: actual contract call + Ok(0) + } + + async fn watch(&self, _callback: F) -> Result<(), CR8Error> + where + F: Fn(u64) + Send + Sync + 'static, + { + // TODO: event listener + Ok(()) + } +} + +#[async_trait::async_trait] +impl CR8ClientRecovery for DefaultCR8Client { + async fn recover_state(&self) -> Result, CR8Error> { + // TODO: recovery logic + Ok(HashMap::new()) + } + + async fn force_complete(&self) -> Result<(), CR8Error> { + // TODO: force completion + Ok(()) + } +} + +#[cfg(test)] +mod tests; diff --git a/src/cr8/tests.rs b/src/cr8/tests.rs new file mode 100644 index 0000000..b569625 --- /dev/null +++ b/src/cr8/tests.rs @@ -0,0 +1,24 @@ +use super::*; +use crate::chain::Chain; +use crate::wallet::switchboard::SwitchboardWallet; + +#[tokio::test] +async fn test_builder_missing_chain() { + let result = CR8ClientBuilder::new() + .with_wallet(SwitchboardWallet::generate().unwrap()) + .with_contract_address("0x123") + .build(); + assert!(result.is_err()); +} + +#[tokio::test] +async fn test_builder_success() { + let wallet = SwitchboardWallet::generate().unwrap(); + let client = CR8ClientBuilder::new() + .with_chain(Chain::ArbitrumSepolia) + .with_wallet(wallet) + .with_contract_address("0x123") + .build() + .unwrap(); + assert_eq!(client.contract_address, "0x123"); +} diff --git a/src/lib.rs b/src/lib.rs index 9151759..3a28727 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,3 +27,4 @@ pub mod prelude { pub use crate::wallet::Wallet; pub use alloy::primitives::{Address, U256}; } +pub mod cr8;