From 23c26dcdcb3640b26dfa8f5c149e0797c478b55b Mon Sep 17 00:00:00 2001 From: livelybug Date: Wed, 27 Aug 2025 16:51:23 +0800 Subject: [PATCH 1/2] Add intergration test of Anda BNB. Chores --- tools/anda_bnb/Cargo.toml | 3 +- tools/anda_bnb/examples/balance_test.rs | 122 +++++++++++++++++++++++ tools/anda_bnb/examples/common.rs | 5 + tools/anda_bnb/examples/transfer_test.rs | 113 +++++++++++++++++++++ tools/anda_bnb/src/ledger.rs | 10 +- 5 files changed, 244 insertions(+), 9 deletions(-) create mode 100644 tools/anda_bnb/examples/balance_test.rs create mode 100644 tools/anda_bnb/examples/common.rs create mode 100644 tools/anda_bnb/examples/transfer_test.rs diff --git a/tools/anda_bnb/Cargo.toml b/tools/anda_bnb/Cargo.toml index d4af966..7be9ac4 100644 --- a/tools/anda_bnb/Cargo.toml +++ b/tools/anda_bnb/Cargo.toml @@ -23,7 +23,8 @@ reqwest = { workspace = true } thiserror = { workspace = true } k256 = { workspace = true } async-trait = { workspace = true } -alloy = { version = "0.15", features = [ +structured-logger = { workspace = true } +alloy = { version = "1.0.25", features = [ "providers", "network", "sol-types", diff --git a/tools/anda_bnb/examples/balance_test.rs b/tools/anda_bnb/examples/balance_test.rs new file mode 100644 index 0000000..47d4ef9 --- /dev/null +++ b/tools/anda_bnb/examples/balance_test.rs @@ -0,0 +1,122 @@ +/// Tests the balance retrieval functionality for BNB ledgers. +/// +/// This async function demonstrates the process of: +/// - Loading cryptographic identity and secrets +/// - Initializing a Web3 client for BNB Chain +/// - Creating BNB ledgers and a balance query tool +/// - Deriving a user's EVM address +/// - Querying balances for different token symbols +/// +/// # Examples +/// +/// test_bnb_ledger_balance().await; +/// +use std::sync::Arc; + +use alloy::hex; +use anda_bnb::ledger::{BNBLedgers, BalanceOfArgs, BalanceOfTool, DRVT_PATH, bnb_rpc}; +use anda_bnb::signer::derive_address_from_pubkey; +use anda_core::{KeysFeatures, Tool}; +use anda_engine::{context::Web3SDK, engine::EngineBuilder, extension::extractor::Extractor}; +use anda_web3_client::client::{Client as Web3Client, load_identity}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeSet; +use structured_logger::Builder; + +use crate::common::{CHAIN_ID, TOKEN_ADDR}; +mod common; + +pub async fn test_bnb_ledger_balance() { + Builder::with_level("debug").init(); + + // Read identity and root secret + let id_secret = dotenv::var("ID_SECRET").unwrap(); + let root_secret_org = dotenv::var("ROOT_SECRET").unwrap(); + + // Parse and validate cryptographic secrets + let identity = load_identity(&id_secret).unwrap(); + let root_secret = hex::decode(&root_secret_org).unwrap(); + let root_secret: [u8; 48] = root_secret + .try_into() + .map_err(|_| format!("invalid root_secret: {:?}", &root_secret_org)) + .unwrap(); + + // Initialize Web3 client for BNB Chain network interaction + let web3 = Web3Client::builder() + .with_identity(Arc::new(identity)) + .with_root_secret(root_secret) + .build() + .await + .unwrap(); + + let mut token_addrs = BTreeSet::new(); + token_addrs.insert(TOKEN_ADDR.to_string()); + let drvt_path: Vec> = DRVT_PATH.iter().map(|&s| s.to_vec()).collect(); + // Create ledgers instance + let ledgers = BNBLedgers::load(bnb_rpc(), CHAIN_ID, drvt_path.clone(), token_addrs) + .await + .unwrap(); + let ledgers = Arc::new(ledgers); + let tool = BalanceOfTool::new(ledgers.clone()); + let definition = tool.definition(); + assert_eq!(definition.name, "bnb_ledger_balance_of"); + + // Create an agent for testing + #[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] + struct TestStruct { + name: String, + age: Option, + } + let agent = Extractor::::default(); + + // Create a context for testing + let engine_ctx = EngineBuilder::new() + // .with_name("BNB_TEST".to_string()).unwrap() + .with_web3_client(Arc::new(Web3SDK::from_web3(Arc::new(web3)))) + .register_agent(agent) + .unwrap() + .mock_ctx(); + let base_ctx = engine_ctx.base.clone(); + + // Derive EVM address from derivation path + let pubkey_bytes = base_ctx + .secp256k1_public_key(drvt_path) + .await + .map_err(|e| { + format!( + "Failed to get public key from derivation path: {:?}. Error: {:?}", + DRVT_PATH, + e.to_string() + ) + }) + .unwrap(); + let user_address = derive_address_from_pubkey(&pubkey_bytes).unwrap(); + log::debug!( + "User pubkey: {:?}, User EVM address: {:?}", + hex::encode(pubkey_bytes), + user_address + ); + + // Iterate through the ledgers and perform balance queries + for (symbol, _) in ledgers.ledgers.clone() { + // Create arguments for balance query + let args = BalanceOfArgs { + account: user_address.to_string(), + symbol: symbol.into(), + }; + + // Call the tool to query balance + let res = tool.call(base_ctx.clone(), args, vec![]).await; + assert!(res.is_ok(), "Balance query failed: {:?}", res); + println!("Balance query result: {:#?}", res.unwrap()); + } +} + +// cargo run --example balance_test +// cargo run -p anda_bnb --example balance_test +#[tokio::main] +async fn main() { + println!("Anda BNB query test!"); + test_bnb_ledger_balance().await; +} diff --git a/tools/anda_bnb/examples/common.rs b/tools/anda_bnb/examples/common.rs new file mode 100644 index 0000000..0b6b895 --- /dev/null +++ b/tools/anda_bnb/examples/common.rs @@ -0,0 +1,5 @@ +// public static url of BNB BEP20 contract address +pub static TOKEN_ADDR: &str = "0xDE3a190D9D26A8271Ae9C27573c03094A8A2c449"; // BNB testnet + +// public static chain id of BNB +pub static CHAIN_ID: u64 = 97; // BNB testnet diff --git a/tools/anda_bnb/examples/transfer_test.rs b/tools/anda_bnb/examples/transfer_test.rs new file mode 100644 index 0000000..64b2210 --- /dev/null +++ b/tools/anda_bnb/examples/transfer_test.rs @@ -0,0 +1,113 @@ +/// Tests the BNB ledger transfer functionality by performing token transfers across different ledgers. +/// +/// This test function demonstrates the process of: +/// - Loading cryptographic identity and secrets +/// - Creating a BNB ledger instance +/// - Initializing a Web3 client +/// - Creating an engine context +/// - Iterating through ledgers and performing token transfers +/// +/// # Panics +/// Panics if identity loading, ledger loading, or token transfers fail +/// +/// # Environment Variables +/// - `ID_SECRET`: Cryptographic identity secret +/// - `ROOT_SECRET`: Root secret for Web3 client +/// +use std::{collections::BTreeSet, sync::Arc}; + +use alloy::hex; +use alloy::primitives::address; +use anda_bnb::ledger::{BNBLedgers, DRVT_PATH, TransferToArgs, TransferTool, bnb_rpc}; +use anda_core::Tool; +use anda_engine::{context::Web3SDK, engine::EngineBuilder, extension::extractor::Extractor}; +use anda_web3_client::client::{Client as Web3Client, load_identity}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use structured_logger::Builder; + +use crate::common::{CHAIN_ID, TOKEN_ADDR}; +mod common; + +pub async fn test_bnb_ledger_transfer() { + Builder::with_level("debug").init(); + + // Generate random bytes for identity and root secret + let id_secret = dotenv::var("ID_SECRET").unwrap(); + let root_secret_org = dotenv::var("ROOT_SECRET").unwrap(); + + // Parse cryptographic secrets + let identity = load_identity(&id_secret).unwrap(); + let root_secret = hex::decode(&root_secret_org).unwrap(); + let root_secret: [u8; 48] = root_secret + .try_into() + .map_err(|_| format!("invalid root_secret: {:?}", &root_secret_org)) + .unwrap(); + + let mut token_addrs = BTreeSet::new(); + token_addrs.insert(TOKEN_ADDR.to_string()); + let drvt_path: Vec> = DRVT_PATH.iter().map(|&s| s.to_vec()).collect(); + + // Create a BNB ledger instance + let ledgers = BNBLedgers::load(bnb_rpc(), CHAIN_ID, drvt_path.clone(), token_addrs) + .await + .unwrap(); + let ledgers = Arc::new(ledgers); + let tool = TransferTool::new(ledgers.clone()); + let definition = tool.definition(); + assert_eq!(definition.name, "bnb_ledger_transfer"); + assert_eq!( + tool.description() + .contains(ledgers.ledgers.clone().first_key_value().unwrap().0), + true + ); + + // Create an agent for testing + #[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] + struct TestStruct { + name: String, + age: Option, + } + let agent = Extractor::::default(); + + // Initialize Web3 client for BNB network + let web3 = Web3Client::builder() + .with_identity(Arc::new(identity)) + .with_root_secret(root_secret) + .build() + .await + .unwrap(); + + // Create a context for testing + let engine_ctx = EngineBuilder::new() + .with_web3_client(Arc::new(Web3SDK::from_web3(Arc::new(web3)))) + .register_agent(agent) + .unwrap() + .mock_ctx(); + let base_ctx = engine_ctx.base; + + // Iterate through the ledgers and perform transfers + for (symbol, _) in ledgers.ledgers.clone() { + // Init transfer arguments + let to_addr = address!("0xA8c4AAE4ce759072D933bD4a51172257622eF128"); // Receiver addr + let transfer_amount = 0.00012; + let transfer_to_args = TransferToArgs { + account: to_addr.to_string(), + symbol: symbol.clone(), + amount: transfer_amount, + }; + + // Call tool to transfer tokens + let res = tool.call(base_ctx.clone(), transfer_to_args, vec![]).await; + assert!(res.is_ok(), "Transfer failed: {:?}", res); + println!("Transfer result: {:#?}", res.unwrap()); + } +} + +// cargo run --example transfer_test +// cargo run -p anda_bnb --example transfer_test +#[tokio::main] +async fn main() { + println!("Anda BNB transfer test!"); + test_bnb_ledger_transfer().await; +} diff --git a/tools/anda_bnb/src/ledger.rs b/tools/anda_bnb/src/ledger.rs index a3aed0d..9cad408 100644 --- a/tools/anda_bnb/src/ledger.rs +++ b/tools/anda_bnb/src/ledger.rs @@ -52,12 +52,6 @@ pub fn bnb_rpc() -> String { dotenv::var("BNB_RPC").unwrap_or_else(|_| "https://bsc-testnet.bnbchain.org".to_string()) } -// public static url of BNB BEP20 contract address -pub static TOKEN_ADDR: &str = "0xDE3a190D9D26A8271Ae9C27573c03094A8A2c449"; // BNB testnet - -// public static chain id of BNB -pub static CHAIN_ID: u64 = 97; // BNB testnet - // public static derivation path pub static DRVT_PATH: &[&[u8]] = &[b"44'", b"60'", b"10'", b"20", b"30"]; @@ -68,7 +62,7 @@ pub struct BNBLedgers { chain_id: u64, derivation_path: Vec>, /// Map of token symbols to their corresponding canister ID and decimals places - ledgers: BTreeMap, + pub ledgers: BTreeMap, } impl BNBLedgers { @@ -136,7 +130,7 @@ impl BNBLedgers { // Get sender EVM address let sender_address = NetworkWallet::::default_signer_address(&wallet); log::debug!("Sender EVM address: {:?}", sender_address); - + // Create a provider with the wallet. let provider = ProviderBuilder::new() .with_simple_nonce_management() From d4377e2af21aec22b71859250a51ae81fe2a6258 Mon Sep 17 00:00:00 2001 From: livelybug Date: Wed, 27 Aug 2025 20:47:08 +0800 Subject: [PATCH 2/2] Chores --- tools/anda_bnb/examples/balance_test.rs | 2 +- tools/anda_bnb/examples/{common.rs => common/mod.rs} | 0 tools/anda_bnb/examples/transfer_test.rs | 5 ++--- tools/anda_bnb/src/signer/anda_signer.rs | 5 ++++- 4 files changed, 7 insertions(+), 5 deletions(-) rename tools/anda_bnb/examples/{common.rs => common/mod.rs} (100%) diff --git a/tools/anda_bnb/examples/balance_test.rs b/tools/anda_bnb/examples/balance_test.rs index 47d4ef9..89bccd0 100644 --- a/tools/anda_bnb/examples/balance_test.rs +++ b/tools/anda_bnb/examples/balance_test.rs @@ -103,7 +103,7 @@ pub async fn test_bnb_ledger_balance() { // Create arguments for balance query let args = BalanceOfArgs { account: user_address.to_string(), - symbol: symbol.into(), + symbol: symbol, }; // Call the tool to query balance diff --git a/tools/anda_bnb/examples/common.rs b/tools/anda_bnb/examples/common/mod.rs similarity index 100% rename from tools/anda_bnb/examples/common.rs rename to tools/anda_bnb/examples/common/mod.rs diff --git a/tools/anda_bnb/examples/transfer_test.rs b/tools/anda_bnb/examples/transfer_test.rs index 64b2210..dd8f4ba 100644 --- a/tools/anda_bnb/examples/transfer_test.rs +++ b/tools/anda_bnb/examples/transfer_test.rs @@ -56,10 +56,9 @@ pub async fn test_bnb_ledger_transfer() { let tool = TransferTool::new(ledgers.clone()); let definition = tool.definition(); assert_eq!(definition.name, "bnb_ledger_transfer"); - assert_eq!( + assert!( tool.description() - .contains(ledgers.ledgers.clone().first_key_value().unwrap().0), - true + .contains(ledgers.ledgers.clone().first_key_value().unwrap().0) ); // Create an agent for testing diff --git a/tools/anda_bnb/src/signer/anda_signer.rs b/tools/anda_bnb/src/signer/anda_signer.rs index b43eb30..7d7a2c7 100644 --- a/tools/anda_bnb/src/signer/anda_signer.rs +++ b/tools/anda_bnb/src/signer/anda_signer.rs @@ -213,7 +213,7 @@ mod tests { sync::Arc, }; - use crate::ledger::{CHAIN_ID, DRVT_PATH}; + use crate::ledger::DRVT_PATH; use anda_engine::{ context::Web3SDK, engine::{AgentInfo, EngineBuilder}, @@ -226,6 +226,9 @@ mod tests { use super::*; + // public static chain id of BNB + pub static CHAIN_ID: u64 = 97; // BNB testnet + #[tokio::test] async fn test_sign_message() { // Create an agent for testing