From 3927be0878fa043ab29710451fe7375aad840a09 Mon Sep 17 00:00:00 2001 From: n0x <54751288+N0xMare@users.noreply.github.com> Date: Thu, 22 Feb 2024 11:46:23 -0700 Subject: [PATCH 1/3] add token admin behavior --- .gitignore | 1 + src/behaviors/mod.rs | 1 + src/behaviors/token_admin.rs | 143 +++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 src/behaviors/token_admin.rs diff --git a/.gitignore b/.gitignore index 0b17328..502a371 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,6 @@ docs/ # Dotenv file .env *.lock +.idea/ analysis/counter/* \ No newline at end of file diff --git a/src/behaviors/mod.rs b/src/behaviors/mod.rs index 38b8105..23fb27d 100644 --- a/src/behaviors/mod.rs +++ b/src/behaviors/mod.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; pub mod deployer; +mod token_admin; pub use deployer::Deployer; diff --git a/src/behaviors/token_admin.rs b/src/behaviors/token_admin.rs new file mode 100644 index 0000000..ad7b776 --- /dev/null +++ b/src/behaviors/token_admin.rs @@ -0,0 +1,143 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use anyhow::{Result}; +use arbiter_core::{middleware::ArbiterMiddleware}; +use arbiter_engine::{ + machine::{Behavior, EventStream}, + messager::Messager +}; +use arbiter_engine::machine::ControlFlow; +use arbiter_engine::messager::{Message, To}; +use ethers::types::H160; + +use super::*; +use crate::bindings::token::ArbiterToken; + +#[derive(Debug, Serialize, Deserialize)] +pub struct TokenAdmin{ + #[serde(skip)] + pub tokens: Option>>, + pub token_data: HashMap, + #[serde(skip)] + pub messager: Option +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct TokenData { + pub name: String, + pub symbol: String, + pub decimals: u8, + pub address: Option +} + +/// Used as an action to ask what tokens are available. +#[derive(Debug, Deserialize, Serialize)] +pub enum TokenAdminQuery { + /// Get the address of the token. + AddressOf(String), + /// Mint tokens. + MintRequest(MintRequest) +} + +/// Used as an action to mint tokens. +#[derive(Debug, Deserialize, Serialize)] +pub struct MintRequest { + /// The token to mint. + pub token: String, + /// The address to mint to. + pub mint_to: H160, + /// The amount to mint. + pub mint_amount: u64 +} + +#[async_trait::async_trait] +impl Behavior for TokenAdmin { + async fn startup( + &mut self, + client: Arc, + messager: Messager, + ) -> Result>> { + let mut deployed_tokens = HashMap::new(); + let mut token_addresses = HashMap::new(); // For messaging purposes + + for (token_id, data) in self.token_data.iter_mut() { + // Handle the deployment result + let deploy_result = ArbiterToken::deploy( + client.clone(), + ( + data.name.clone(), + data.symbol.clone(), + data.decimals, + ), + )?.send().await?; + + // Send the deployment transaction and await its confirmation + let token = deploy_result; + + // Assuming `address` is an async method to get the deployed contract's address + let token_address = token.address(); + data.address = Some(token_address); + + deployed_tokens.insert(token_id.clone(), token); + token_addresses.insert(token_id.clone(), token_address); // Collect addresses for messaging + } + + self.tokens = Some(deployed_tokens); + + // Serialize and send token information to all agents + let message_content = serde_json::to_string(&token_addresses)?; + + messager + .send(To::All, serde_json::to_string(&message_content)?) + .await; + + // Return None as no event stream is required for startup + Ok(None) + } + + async fn process( + &mut self, + event: Message + ) -> Result { + // First, deserialize the incoming message to a TokenAdminQuery + let query: TokenAdminQuery = match serde_json::from_str(&event.data) { + Ok(query) => query, + Err(_) => { + // Log or handle the error as appropriate for your application + eprintln!("Failed to deserialize the event data into a TokenAdminQuery"); + return Ok(ControlFlow::Continue); + } + }; + + // Process the query + match query { + TokenAdminQuery::AddressOf(token_name) => { + if let Some(token_data) = self.token_data.get(&token_name) { + let response = serde_json::to_string(&token_data.address) + .map_err(|_| anyhow::anyhow!("Failed to serialize token address"))?; + if let Some(messager) = &self.messager { + messager.send(To::Agent(event.from.clone()), response).await?; + } else { + eprintln!("Messager is not available."); + return Err(anyhow::anyhow!("Messager is not available.")); + } + } else { + eprintln!("Token not found: {}", token_name); + } + Ok(ControlFlow::Continue) + }, + TokenAdminQuery::MintRequest(mint_request) => { + if let Some(token) = self.tokens.as_ref().and_then(|tokens| tokens.get(&mint_request.token)) { + match token.mint(mint_request.mint_to, mint_request.mint_amount.into()).send().await { + Ok(_) => println!("Minting successful for token: {}", mint_request.token), + Err(e) => eprintln!("Failed to mint token: {}. Error: {:?}", mint_request.token, e), + } + } else { + eprintln!("Token not found for minting: {}", mint_request.token); + } + Ok(ControlFlow::Continue) + } + } + } +} \ No newline at end of file From 0db3c22fe3538c93f4104ef485f41b9b1839af61 Mon Sep 17 00:00:00 2001 From: n0x <54751288+N0xMare@users.noreply.github.com> Date: Fri, 23 Feb 2024 09:28:37 -0700 Subject: [PATCH 2/3] address changes and add test --- Cargo.toml | 4 +- src/behaviors/token_admin.rs | 182 ++++++++++++++++++++++++++--------- 2 files changed, 142 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 897087b..b0f7a08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] tracing = "0.1.40" -tracing-subscriber = "0.3.18" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } arbiter-core = { version = "0.10.1" } arbiter-macros = { version = "0.1.1" } arbiter-engine = { version = "0.3.0" } @@ -16,3 +16,5 @@ anyhow = { version = "1.0.79" } async-trait = "0.1.74" clap = { version = "4.4.8", features = ["derive"] } serde_json = "1.0.113" +log = "0.4.20" +futures-util = "0.3.30" diff --git a/src/behaviors/token_admin.rs b/src/behaviors/token_admin.rs index ad7b776..0f6f7fd 100644 --- a/src/behaviors/token_admin.rs +++ b/src/behaviors/token_admin.rs @@ -1,26 +1,25 @@ -use std::collections::HashMap; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; -use anyhow::{Result}; -use arbiter_core::{middleware::ArbiterMiddleware}; +use anyhow::Result; +use arbiter_core::middleware::ArbiterMiddleware; use arbiter_engine::{ - machine::{Behavior, EventStream}, - messager::Messager + machine::{Behavior, ControlFlow, EventStream}, + messager::{Message, Messager, To}, }; -use arbiter_engine::machine::ControlFlow; -use arbiter_engine::messager::{Message, To}; + use ethers::types::H160; +use futures_util::StreamExt; use super::*; use crate::bindings::token::ArbiterToken; #[derive(Debug, Serialize, Deserialize)] -pub struct TokenAdmin{ +pub struct TokenAdmin { #[serde(skip)] pub tokens: Option>>, pub token_data: HashMap, #[serde(skip)] - pub messager: Option + pub messager: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -28,7 +27,7 @@ pub struct TokenData { pub name: String, pub symbol: String, pub decimals: u8, - pub address: Option + pub address: Option, } /// Used as an action to ask what tokens are available. @@ -37,7 +36,7 @@ pub enum TokenAdminQuery { /// Get the address of the token. AddressOf(String), /// Mint tokens. - MintRequest(MintRequest) + MintRequest(MintRequest), } /// Used as an action to mint tokens. @@ -48,7 +47,7 @@ pub struct MintRequest { /// The address to mint to. pub mint_to: H160, /// The amount to mint. - pub mint_amount: u64 + pub mint_amount: u64, } #[async_trait::async_trait] @@ -59,65 +58,52 @@ impl Behavior for TokenAdmin { messager: Messager, ) -> Result>> { let mut deployed_tokens = HashMap::new(); - let mut token_addresses = HashMap::new(); // For messaging purposes + let mut token_addresses = HashMap::new(); for (token_id, data) in self.token_data.iter_mut() { - // Handle the deployment result let deploy_result = ArbiterToken::deploy( client.clone(), - ( - data.name.clone(), - data.symbol.clone(), - data.decimals, - ), - )?.send().await?; - - // Send the deployment transaction and await its confirmation + (data.name.clone(), data.symbol.clone(), data.decimals), + )? + .send() + .await?; + let token = deploy_result; - // Assuming `address` is an async method to get the deployed contract's address let token_address = token.address(); data.address = Some(token_address); deployed_tokens.insert(token_id.clone(), token); - token_addresses.insert(token_id.clone(), token_address); // Collect addresses for messaging + token_addresses.insert(token_id.clone(), token_address); } self.tokens = Some(deployed_tokens); - // Serialize and send token information to all agents let message_content = serde_json::to_string(&token_addresses)?; - messager - .send(To::All, serde_json::to_string(&message_content)?) - .await; + let _ = messager.send(To::All, &message_content).await; - // Return None as no event stream is required for startup Ok(None) } - async fn process( - &mut self, - event: Message - ) -> Result { - // First, deserialize the incoming message to a TokenAdminQuery + async fn process(&mut self, event: Message) -> Result { let query: TokenAdminQuery = match serde_json::from_str(&event.data) { Ok(query) => query, Err(_) => { - // Log or handle the error as appropriate for your application eprintln!("Failed to deserialize the event data into a TokenAdminQuery"); return Ok(ControlFlow::Continue); } }; - // Process the query match query { TokenAdminQuery::AddressOf(token_name) => { if let Some(token_data) = self.token_data.get(&token_name) { let response = serde_json::to_string(&token_data.address) .map_err(|_| anyhow::anyhow!("Failed to serialize token address"))?; if let Some(messager) = &self.messager { - messager.send(To::Agent(event.from.clone()), response).await?; + messager + .send(To::Agent(event.from.clone()), response) + .await?; } else { eprintln!("Messager is not available."); return Err(anyhow::anyhow!("Messager is not available.")); @@ -126,12 +112,23 @@ impl Behavior for TokenAdmin { eprintln!("Token not found: {}", token_name); } Ok(ControlFlow::Continue) - }, + } TokenAdminQuery::MintRequest(mint_request) => { - if let Some(token) = self.tokens.as_ref().and_then(|tokens| tokens.get(&mint_request.token)) { - match token.mint(mint_request.mint_to, mint_request.mint_amount.into()).send().await { + if let Some(token) = self + .tokens + .as_ref() + .and_then(|tokens| tokens.get(&mint_request.token)) + { + match token + .mint(mint_request.mint_to, mint_request.mint_amount.into()) + .send() + .await + { Ok(_) => println!("Minting successful for token: {}", mint_request.token), - Err(e) => eprintln!("Failed to mint token: {}. Error: {:?}", mint_request.token, e), + Err(e) => eprintln!( + "Failed to mint token: {}. Error: {:?}", + mint_request.token, e + ), } } else { eprintln!("Token not found for minting: {}", mint_request.token); @@ -140,4 +137,103 @@ impl Behavior for TokenAdmin { } } } -} \ No newline at end of file +} + + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use arbiter_engine::{agent::Agent, world::World}; + use futures_util::{FutureExt, StreamExt}; + use crate::behaviors::token_admin::{TokenAdmin, TokenData}; + use tracing::{info, subscriber::set_global_default}; + use tracing_subscriber::FmtSubscriber; + + /*#[tokio::test] + async fn token_admin_behavior_test() { + // Initialize the tracing subscriber to capture logs + let subscriber = FmtSubscriber::new(); + set_global_default(subscriber).expect("setting default subscriber failed"); + + let mut world = World::new("univ3"); + let messager = world.messager.clone(); + + let token_admin_behavior = TokenAdmin { + tokens: None, + token_data: { + let mut h = HashMap::new(); + h.insert("MockToken".to_string(), TokenData { + name: "MockToken".to_string(), + symbol: "MTK".to_string(), + decimals: 18, + address: None, + }); + h + }, + messager: Some(messager.clone()), + }; + + let agent = Agent::builder("token_admin_agent"); + world.add_agent(agent.with_behavior(token_admin_behavior)); + + world.run().await.expect("World failed to run"); + + let mut stream = messager.stream().unwrap(); + let res = stream.next().await.unwrap(); + let token_res_data = res.data; + println!("{}", token_res_data); + assert_eq!("\"{\\\"MockToken\\\":\\\"0xb00efcb70090a21d46660adf95a16ec69623f694\\\"}\"", token_res_data); + + }*/ + + #[tokio::test] + async fn token_admin_behavior_test() { + // Initialize the tracing subscriber to capture logs + let subscriber = FmtSubscriber::new(); + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + + let mut world = World::new("univ3"); + let messager = world.messager.clone(); + + let token_admin_behavior = TokenAdmin { + tokens: None, + token_data: { + let mut h = HashMap::new(); + h.insert("MockToken".to_string(), TokenData { + name: "MockToken".to_string(), + symbol: "MTK".to_string(), + decimals: 18, + address: None, + }); + h + }, + messager: Some(messager.clone()), + }; + + let agent = Agent::builder("token_admin_agent"); + world.add_agent(agent.with_behavior(token_admin_behavior)); + + world.run().await.expect("World failed to run"); + + let mut stream = messager.stream().expect("Failed to get messager stream"); + if let Some(res) = stream.next().await { + let token_res_data = &res.data; + println!("{}", token_res_data); + + // Deserialize the JSON string into a HashMap + let data: String = serde_json::from_str(token_res_data).expect("Failed to deserialize message data"); + + println!("{}", data); + + // First, deserialize the JSON string into a HashMap + let parsed_data: HashMap = serde_json::from_str(&data) + .expect("Failed to deserialize token data"); + + if let Some(address) = parsed_data.get("MockToken") { + assert_eq!("0xb00efcb70090a21d46660adf95a16ec69623f694", address); + } else { + panic!("MockToken not found in the parsed data"); + } + } + } +} From 52be6cb2a93ff26bde8c481dcb9ab0b71885004a Mon Sep 17 00:00:00 2001 From: n0x <54751288+N0xMare@users.noreply.github.com> Date: Fri, 23 Feb 2024 11:06:03 -0700 Subject: [PATCH 3/3] clippy and fmt + cleanup --- src/behaviors/token_admin.rs | 80 +++++++++--------------------------- 1 file changed, 19 insertions(+), 61 deletions(-) diff --git a/src/behaviors/token_admin.rs b/src/behaviors/token_admin.rs index 0f6f7fd..5e9cb9f 100644 --- a/src/behaviors/token_admin.rs +++ b/src/behaviors/token_admin.rs @@ -2,13 +2,8 @@ use std::{collections::HashMap, sync::Arc}; use anyhow::Result; use arbiter_core::middleware::ArbiterMiddleware; -use arbiter_engine::{ - machine::{Behavior, ControlFlow, EventStream}, - messager::{Message, Messager, To}, -}; - +use arbiter_engine::messager::{Message, Messager, To}; use ethers::types::H160; -use futures_util::StreamExt; use super::*; use crate::bindings::token::ArbiterToken; @@ -139,58 +134,21 @@ impl Behavior for TokenAdmin { } } - #[cfg(test)] mod tests { use std::collections::HashMap; + use arbiter_engine::{agent::Agent, world::World}; - use futures_util::{FutureExt, StreamExt}; - use crate::behaviors::token_admin::{TokenAdmin, TokenData}; - use tracing::{info, subscriber::set_global_default}; + use futures_util::StreamExt; use tracing_subscriber::FmtSubscriber; - /*#[tokio::test] - async fn token_admin_behavior_test() { - // Initialize the tracing subscriber to capture logs - let subscriber = FmtSubscriber::new(); - set_global_default(subscriber).expect("setting default subscriber failed"); - - let mut world = World::new("univ3"); - let messager = world.messager.clone(); - - let token_admin_behavior = TokenAdmin { - tokens: None, - token_data: { - let mut h = HashMap::new(); - h.insert("MockToken".to_string(), TokenData { - name: "MockToken".to_string(), - symbol: "MTK".to_string(), - decimals: 18, - address: None, - }); - h - }, - messager: Some(messager.clone()), - }; - - let agent = Agent::builder("token_admin_agent"); - world.add_agent(agent.with_behavior(token_admin_behavior)); - - world.run().await.expect("World failed to run"); - - let mut stream = messager.stream().unwrap(); - let res = stream.next().await.unwrap(); - let token_res_data = res.data; - println!("{}", token_res_data); - assert_eq!("\"{\\\"MockToken\\\":\\\"0xb00efcb70090a21d46660adf95a16ec69623f694\\\"}\"", token_res_data); - - }*/ + use crate::behaviors::token_admin::{TokenAdmin, TokenData}; #[tokio::test] async fn token_admin_behavior_test() { - // Initialize the tracing subscriber to capture logs let subscriber = FmtSubscriber::new(); - tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + tracing::subscriber::set_global_default(subscriber) + .expect("setting default subscriber failed"); let mut world = World::new("univ3"); let messager = world.messager.clone(); @@ -199,12 +157,15 @@ mod tests { tokens: None, token_data: { let mut h = HashMap::new(); - h.insert("MockToken".to_string(), TokenData { - name: "MockToken".to_string(), - symbol: "MTK".to_string(), - decimals: 18, - address: None, - }); + h.insert( + "MockToken".to_string(), + TokenData { + name: "MockToken".to_string(), + symbol: "MTK".to_string(), + decimals: 18, + address: None, + }, + ); h }, messager: Some(messager.clone()), @@ -220,14 +181,11 @@ mod tests { let token_res_data = &res.data; println!("{}", token_res_data); - // Deserialize the JSON string into a HashMap - let data: String = serde_json::from_str(token_res_data).expect("Failed to deserialize message data"); - - println!("{}", data); + let data: String = + serde_json::from_str(token_res_data).expect("Failed to deserialize message data"); - // First, deserialize the JSON string into a HashMap - let parsed_data: HashMap = serde_json::from_str(&data) - .expect("Failed to deserialize token data"); + let parsed_data: HashMap = + serde_json::from_str(&data).expect("Failed to deserialize token data"); if let Some(address) = parsed_data.get("MockToken") { assert_eq!("0xb00efcb70090a21d46660adf95a16ec69623f694", address);