├── src ├── lib.rs ├── fvn_rewards.rs ├── fvn_round_contract.rs ├── fvn_contract.rs ├── fvn_cli.rs ├── fvn_error.rs ├── fvn_config.rs ├── fvn.rs ├── fvn_help.rs └── main.rs ├── .gitignore ├── LICENSE ├── Cargo.toml └── README.md /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod fvn; 2 | pub use fvn::*; 3 | pub mod fvn_cli; 4 | pub mod fvn_config; 5 | pub mod fvn_contract; 6 | pub mod fvn_error; 7 | pub mod fvn_help; 8 | pub mod fvn_rewards; 9 | pub mod fvn_round_contract; 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # RustRover 17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 19 | # and can be added to the global gitignore or merged into this file. For a more nuclear 20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 21 | #.idea/ -------------------------------------------------------------------------------- /src/fvn_rewards.rs: -------------------------------------------------------------------------------- 1 | impl crate::Fvn { 2 | // for randgen 3 | pub async fn check_rewards(&self) -> Result, mind_sdk_util::MindError> { 4 | let registered_voter_wallet: Option = self.get_voter_wallet().await?; 5 | match registered_voter_wallet { 6 | Some(registered_voter_wallet) => { 7 | let rewards = self 8 | .onchain 9 | .check_mind_rewards(self.basic_config.subnet_id as i64, registered_voter_wallet) 10 | .await?; 11 | return Ok(Some((registered_voter_wallet, rewards))); 12 | } 13 | None => { 14 | log::error!("hot wallet is not registered and has no rewards"); 15 | return Ok(None); 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Mind Network 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/fvn_round_contract.rs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////// 2 | pub use alloy::sol; 3 | 4 | sol!( 5 | #[allow(missing_docs)] 6 | #[derive(Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)] 7 | #[sol(rpc=true, abi=true, alloy_sol_types=alloy::sol_types, docs=true)] 8 | FvnRoundContractAbi, 9 | "../resources/RandgenSubnetRound.json" 10 | ); 11 | 12 | //////////////////////////////////////////////////////////////////////////////////////////////// 13 | pub(crate) type FvnRoundContractInstance = 14 | crate::fvn_round_contract::FvnRoundContractAbi::FvnRoundContractAbiInstance< 15 | alloy::transports::http::Http, 16 | mind_sdk_chain::network::MindChainFillProvider, 17 | >; 18 | 19 | //////////////////////////////////////////////////////////////////////////////////////////////// 20 | pub fn get_fvn_round_contract_instance( 21 | fhekeyregistry_contact_address: &alloy::primitives::Address, 22 | network_provider: &mind_sdk_chain::network::MindChainFillProvider, 23 | ) -> Result { 24 | let fvn_round_contract: crate::fvn_round_contract::FvnRoundContractInstance = 25 | crate::fvn_round_contract::FvnRoundContractAbi::new( 26 | fhekeyregistry_contact_address.clone(), 27 | network_provider.clone(), 28 | ); // 29 | return Ok(fvn_round_contract); 30 | } 31 | -------------------------------------------------------------------------------- /src/fvn_contract.rs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////// 2 | pub use alloy::sol; 3 | sol!( 4 | #[allow(missing_docs)] 5 | #[derive(Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)] 6 | #[sol(rpc=true, abi=true, alloy_sol_types=alloy::sol_types, docs=true)] 7 | FvnContractAbi, 8 | "../resources/RandgenSubnet.json" 9 | ); 10 | 11 | //////////////////////////////////////////////////////////////////////////////////////////////// 12 | pub type FvnContractInstance = crate::fvn_contract::FvnContractAbi::FvnContractAbiInstance< 13 | alloy::transports::http::Http, 14 | mind_sdk_chain::network::MindChainFillProvider, 15 | >; 16 | 17 | pub type FvnContractInstanceSubmitRandomCtCall<'a> = alloy::contract::CallBuilder< 18 | alloy::transports::http::Http, 19 | &'a mind_sdk_chain::network::MindChainFillProvider, 20 | std::marker::PhantomData, 21 | >; 22 | 23 | //////////////////////////////////////////////////////////////////////////////////////////////// 24 | pub fn get_fvn_contract_instance( 25 | subnet_contact_address: &alloy::primitives::Address, 26 | network_provider: &mind_sdk_chain::network::MindChainFillProvider, 27 | ) -> Result { 28 | let randgen_contract: crate::fvn_contract::FvnContractInstance = 29 | crate::fvn_contract::FvnContractAbi::new( 30 | subnet_contact_address.clone(), 31 | network_provider.clone(), 32 | ); 33 | return Ok(randgen_contract); 34 | } 35 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mind_sdk_deepseek" 3 | version = "0.1.2" 4 | edition = "2021" 5 | authors = ["Mind Network"] 6 | description = "Mind Network Rust SDK" 7 | license = "MIT" 8 | homepage = "https://github.com/mind-network" 9 | repository = "https://github.com/mind-network/mind-sdk-deepseek-rust" 10 | readme = "README.md" 11 | keywords = ["fhe", "async", "web3", "ai", "deepseek"] 12 | categories = ["cryptography"] 13 | 14 | 15 | [dependencies] 16 | mind_sdk_fhe = "0.1.3" 17 | mind_sdk_util = "0.1.2" 18 | mind_sdk_chain = "0.1.3" 19 | mind_sdk_config = "0.1.2" 20 | mind_sdk_io = "0.1.3" 21 | mind_sdk_cli = "0.1.2" 22 | 23 | serde = { version="1.0.217", features = ["derive"]} 24 | serde_json = "1.0.138" 25 | log = "0.4.25" 26 | chrono = { version = "0.4.39", features = ["serde"] } 27 | 28 | rand = { version = "0.9.0"} 29 | reqwest = { version = "0.12.12", features = ["blocking", "json", "multipart"] } 30 | hex = "0.4.3" 31 | bytes = "1.10.0" 32 | toml = "0.8.20" 33 | base64 = "0.22.1" 34 | bincode = "1.3.3" 35 | k256 = "0.13.4" 36 | sha3 = "0.10.8" 37 | alloy = { version = "0.5.2", features = ["full"] } 38 | tokio = { version = "1.43.0", features = ["full"] } 39 | clap = { version = "4.5.30", features = ["default", "derive"] } 40 | flexi_logger = "0.29.8" 41 | sqlx = { version = "0.8.3", features = [ 42 | "sqlite", 43 | "mysql", 44 | "runtime-tokio", 45 | "derive", 46 | "json", 47 | "tls-rustls", 48 | "bigdecimal", 49 | ] } 50 | openssl = "0.10.71" 51 | dotenv = "0.15.0" 52 | gethostname = "1.0.0" 53 | futures = "0.3.31" 54 | deepseek_rs = "0.1.4" 55 | 56 | tfhe = { version = "~1.0.0", features = ["boolean", "shortint", "integer"] } 57 | -------------------------------------------------------------------------------- /src/fvn_cli.rs: -------------------------------------------------------------------------------- 1 | #[derive(clap::Subcommand, Clone, Debug)] 2 | pub enum Commands { 3 | /// check hot wallet address, by default will use ./config/config_fvn.toml 4 | CheckHotWalletAddress(CheckHotWalletAddress), 5 | /// check hot wallet gas balance, need gas fee to vote 6 | CheckGasBalance(CheckGasBalance), 7 | /// check if hot wallet has registered with a particular voter wallet 8 | CheckRegistration(CheckRegistration), 9 | /// register voter address 10 | Register(Register), 11 | /// think a number and vote once 12 | DeepseekFheVote(DeepSeekApiKey), 13 | /// check voting rewards 14 | CheckVoteRewards(CheckVoteRewards), 15 | /// check voting tx history on the explore 16 | CheckVote(CheckVote), 17 | /// check voting tx history on the explore 18 | CheckRound(CheckRound), 19 | } 20 | 21 | #[derive(clap::Args, Clone, Debug)] 22 | pub struct CheckGasBalance { 23 | pub hot_wallet_address: Option, 24 | } 25 | 26 | #[derive(clap::Args, Clone, Debug)] 27 | pub struct CheckRegistration { 28 | pub hot_wallet_address: Option, 29 | pub voter_wallet_address: Option, 30 | } 31 | 32 | #[derive(clap::Args, Clone, Debug)] 33 | pub struct Register { 34 | pub voter_wallet_address: Option, 35 | } 36 | 37 | #[derive(clap::Args, Clone, Debug)] 38 | pub struct DeepSeekApiKey { 39 | pub deepseek_api_key: String, 40 | pub fhe_public_key_fp: String 41 | } 42 | 43 | #[derive(clap::Args, Clone, Debug)] 44 | pub struct VoteOnceRandom {} 45 | 46 | #[derive(clap::Args, Clone, Debug)] 47 | pub struct VoteLoopRandom {} 48 | 49 | #[derive(clap::Args, Clone, Debug)] 50 | pub struct CheckVoteRewards { 51 | pub voter_wallet_address: Option, 52 | } 53 | 54 | #[derive(clap::Args, Clone, Debug)] 55 | pub struct CheckVote {} 56 | 57 | #[derive(clap::Args, Clone, Debug)] 58 | pub struct CheckRound {} 59 | 60 | #[derive(clap::Args, Clone, Debug)] 61 | pub struct CheckHotWalletAddress {} 62 | 63 | #[derive(clap::Parser, Clone, Debug)] 64 | #[command(author = "Mind Network", version = "0.1.0", about = "FHE Randgen Voter Node Cli", long_about = None)] 65 | #[command(propagate_version = true)] 66 | pub struct Cli { 67 | #[clap(default_value = "./config/config_randgen.toml")] 68 | #[arg(long)] 69 | /// fvn config file, contains all the config to run fvn 70 | pub node_config_file: String, 71 | 72 | #[arg(long, value_enum, default_value_t = mind_sdk_cli::CliLogLevel::Info)] //, rename_all = "UPPER")] 73 | /// control level of print, useful for debug, default is info 74 | pub log_level: mind_sdk_cli::CliLogLevel, 75 | 76 | #[arg(long)] 77 | /// fvn wallet private key is needed if to load a different wallet from config_fvn.toml to sign the message onchain, by default load from ./config/config_fvn.toml 78 | pub hot_wallet_private_key: Option, 79 | 80 | //#[arg(long)] 81 | /// fvn will randomly generate a number and do FHE encryption to vote, you can specific a given number instead of random, useful to integrate with other use case 82 | //pub vote: Option, 83 | 84 | #[command(subcommand)] 85 | pub command: Commands, 86 | } 87 | -------------------------------------------------------------------------------- /src/fvn_error.rs: -------------------------------------------------------------------------------- 1 | impl crate::Fvn { 2 | pub async fn process_contract_call_error( 3 | &self, 4 | e: alloy::contract::Error, 5 | ) -> mind_sdk_util::MindError { 6 | match e { 7 | alloy::contract::Error::TransportError(rpc_error) => { 8 | return self.process_transport_error(rpc_error).await; 9 | } 10 | alloy::contract::Error::PendingTransactionError(pending_transaction_error) => { 11 | match pending_transaction_error { 12 | alloy::providers::PendingTransactionError::TransportError(rpc_error) => { 13 | return self.process_transport_error(rpc_error).await; 14 | } 15 | _e => { 16 | return mind_sdk_util::MindError::MindContractOtherError(format!( 17 | "Randgen: Unexpected contract error: {:#?}", 18 | _e 19 | )); 20 | } 21 | } 22 | } 23 | _e => { 24 | return mind_sdk_util::MindError::MindContractOtherError(format!( 25 | "Randgen: Unexpected contract error: {:#?}", 26 | _e 27 | )); 28 | } 29 | } 30 | } 31 | 32 | pub async fn process_transport_error( 33 | &self, 34 | rpc_error: alloy::transports::RpcError, 35 | ) -> mind_sdk_util::MindError { 36 | let rpc_error_response = rpc_error.as_error_resp(); 37 | match rpc_error_response { 38 | Some(rpc_error) => { 39 | // Attempt to decode the transport error into contract ABI errors 40 | if let Some(abi_error) = rpc_error.as_decoded_error(true) { 41 | match abi_error { 42 | crate::fvn_contract::FvnContractAbi::FvnContractAbiErrors::GeneralError( 43 | general_error, 44 | ) => { 45 | let contract_id = general_error.contractID; 46 | let contract_id = mind_sdk_chain::error::parse_contract_id(contract_id); 47 | let error_code = general_error.errorCode; 48 | // 429: Duplicate submission detected for RandgenSubnet. 49 | // 507: Insufficient gas funds: 50 | // 304: Hot wallet already registered with voter wallet. 51 | return mind_sdk_util::MindError::MindContractGeneralError( 52 | mind_sdk_util::MindContractError { 53 | error_code, 54 | contract_name: contract_id, 55 | note: "from rsuban_randgen.fvn_error.process_transport_error function".to_string(), 56 | can_continue: false, 57 | from_wallet: self.onchain.account.address.to_string(), 58 | } 59 | ); 60 | } 61 | _ => { 62 | return mind_sdk_util::MindError::MindContractOtherError( 63 | "Randgen: Undetected ABI contract".to_string(), 64 | ); 65 | } 66 | } 67 | } else { 68 | return mind_sdk_util::MindError::MindContractOtherError(format!( 69 | "Randgen: Unable to decode RPC error into ABI error: {:#?}", 70 | rpc_error 71 | )); 72 | } 73 | } 74 | None => { 75 | return mind_sdk_util::MindError::MindContractOtherError(format!( 76 | "Randgen: RPC error without a response: {:#?}", 77 | rpc_error 78 | )); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/fvn_config.rs: -------------------------------------------------------------------------------- 1 | impl crate::Fvn { 2 | pub async fn new(node_config_file: String) -> Result { 3 | let config: crate::fvn_config::FvnConfig = 4 | crate::fvn_config::FvnConfig::new_from_file(node_config_file.clone())?; 5 | let basic_config: mind_sdk_config::BasicConfig = 6 | mind_sdk_config::BasicConfig::new_from_file(config.basic_config_file.clone())?; 7 | let rewards_contract_address = basic_config.rewards_contract_address; 8 | let fhekeyregistry_contract_address = basic_config.fhekeyregistry_contract_address; 9 | let subnet_controller_contract_address = basic_config.subnet_controller_contract_address; 10 | let onchain = mind_sdk_chain::function::OnChainFunction::new( 11 | config.fvn_wallet_private_key.clone(), 12 | basic_config.rpc_url_read.clone(), 13 | rewards_contract_address.clone(), 14 | fhekeyregistry_contract_address.clone(), 15 | subnet_controller_contract_address.clone(), 16 | ) 17 | .await?; 18 | let given_random_int_pt = None; 19 | let fhe_public_key_local_fp = "".to_string(); 20 | let fhe: Option = None; 21 | let fvn_contract = crate::fvn_contract::get_fvn_contract_instance( 22 | &basic_config.randgen_contract_address, 23 | &onchain.rpc.provider, 24 | )?; 25 | let fvn_round_contract = crate::fvn_round_contract::get_fvn_round_contract_instance( 26 | &basic_config.randgen_round_contract_address, 27 | &onchain.rpc.provider, 28 | )?; 29 | 30 | Ok(Self { 31 | node_config_file: node_config_file.clone(), 32 | config: config.clone(), 33 | basic_config: basic_config.clone(), 34 | onchain: onchain.clone(), 35 | fhe_public_key_local_fp: fhe_public_key_local_fp, 36 | given_random_int_pt: given_random_int_pt, 37 | fhe: fhe, 38 | fvn_contract: fvn_contract, 39 | fvn_round_contract: fvn_round_contract, 40 | }) 41 | } 42 | 43 | pub async fn update_config(&mut self) -> Result<(), mind_sdk_util::MindError> { 44 | self.onchain.wallet_private_key = self.config.fvn_wallet_private_key.clone(); 45 | self.onchain.rpc_url = self.basic_config.rpc_url_read.clone(); 46 | self.onchain.update_config().await?; 47 | 48 | // must reset here to avoid "Missing signing credential for" error 49 | self.fvn_contract = crate::fvn_contract::get_fvn_contract_instance( 50 | &self.basic_config.randgen_contract_address, 51 | &self.onchain.rpc.provider, 52 | )?; 53 | self.fvn_round_contract = crate::fvn_round_contract::get_fvn_round_contract_instance( 54 | &self.basic_config.randgen_round_contract_address, 55 | &self.onchain.rpc.provider, 56 | )?; 57 | Ok(()) 58 | } 59 | 60 | pub fn set_hot_wallet_private_key(&mut self, hot_wallet_private_key: String) { 61 | self.config.fvn_wallet_private_key = hot_wallet_private_key; 62 | } 63 | } 64 | 65 | /////////////////////////////////////////////////////////////////////////// 66 | #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] 67 | pub struct FvnConfig { 68 | pub basic_config_file: String, 69 | pub fvn_wallet_private_key: String, // hot_wallet_key: String, 70 | pub fhe_key_type: String, 71 | pub dir_keys: String, 72 | pub dir_data_fvn: String, 73 | pub poll_interval: u64, 74 | } 75 | 76 | impl FvnConfig { 77 | pub fn new_from_file(fp_config: String) -> Result { 78 | log::debug!("read node config file: {:?}", &fp_config); 79 | let content = std::fs::read_to_string(fp_config.clone())?; 80 | return FvnConfig::new_from_string(content); 81 | } 82 | 83 | pub fn new_from_string(content: String) -> Result { 84 | let config = toml::from_str(&content).map_err(|e| { 85 | log::error!("TOML parse error: {:#?}", e); 86 | mind_sdk_util::MindError::TomlParseError(e.to_string()) 87 | })?; 88 | Ok(config) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mind-sdk-deepseek-rust 2 | `mind_sdk_deepseek` is a **Native Rust SDK** by [Mind Network](https://www.mindnetwork.xyz/). DeepSeek will assist user as an AI Agent to think and predict, and then encrypted by FHE and submit to Mind Network for model consensus. 3 | 4 | [![mind_sdk_deepseek on crates.io](https://img.shields.io/crates/v/mind_sdk_deepseek)](https://crates.io/crates/mind_sdk_deepseek) 5 | [![Documentation on docs.rs](https://img.shields.io/badge/docs-docs.rs-blue)](https://docs.rs/mind_sdk_deepseek) 6 | [![Licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) 7 | [![Github](https://img.shields.io/badge/source-github.com-blue.svg)](https://github.com/mind-network/mind-sdk-deepseek-rust) 8 | [![Github](https://img.shields.io/badge/build-pass-green.svg)](https://github.com/mind-network/mind-sdk-deepseek-rust) 9 | 10 | 11 | ## Usage 12 | ```rust 13 | // call deepseek to predict 14 | let prompt = "Please predict BTC price in next 7 days, return must be a positive integer".to_string() 15 | let client = deepseek_rs::DeepSeekClient::default().unwrap(); 16 | let request = deepseek_rs::client::chat_completions::request::RequestBody::new_messages(vec![ 17 | deepseek_rs::client::chat_completions::request::Message::new_user_message(prompt) 18 | ]).with_model(deepseek_rs::client::chat_completions::request::Model::DeepSeekReasoner); 19 | let response = client.chat_completions(request).await.unwrap(); 20 | //println!("Reasoning: {}", response.choices[0].message.reasoning_content.unwrap()); 21 | //println!("Answer: {}", response.choices[0].message.content.unwrap()); 22 | 23 | // convert deepseek prediction to int type 24 | let deepseek_prediction = match response.choices[0].clone().message.content.unwrap().parse::() { 25 | Ok(prediction) => prediction, 26 | Err(_) => 0, 27 | }; 28 | 29 | // fhe encrypt 30 | let fhe: mind_sdk_fhe::FheInt = mind_sdk_fhe::FheInt::new_from_public_key_local(&fhe_public_key_fp); 31 | let ciphertext = mind_sdk_fhe::fhe_client::encrypt(&fhe, "u8", deepseek_prediction.clone()); 32 | let ciphertext_str: String = mind_sdk_fhe::io::serialize_base64(ciphertext)?; 33 | 34 | // submit ciphertext onchain 35 | let result: alloy::rpc::types::TransactionReceipt = self.submit_fhe_encrypted(ciphertext_str).await?; 36 | ``` 37 | 38 | 39 | 40 | ## Quick Start 41 | **TFHE-rs-v1.0.0** marks the first official stable release, see more details [here](https://github.com/zama-ai/tfhe-rs/releases/tag/tfhe-rs-1.0.0). 42 | **Rustc-v1.84.0** is required to compile TFHE-rs v1.0.0. You can upgrade rustc like `rustup update stable`. 43 | 44 | ### Install 45 | ```toml 46 | [dependencies] 47 | mind_sdk_deepseek = "* 48 | ``` 49 | 50 | ### build 51 | ```bash 52 | cargo build --debug 53 | cargo build --release 54 | ``` 55 | 56 | ### run 57 | ``` bash 58 | cd msn 59 | cargo build && ./target/debug/mind_sdk_deepseek --log-level=info check-hot-wallet-address 60 | 61 | cargo build && ./target/debug/mind_sdk_deepseek --log-level=debug --node-config-file=./config/config_fvn.toml register 0x06eF5C5ba427434bf36469B877e4ea9044D1b735 62 | cargo build && ./target/debug/mind_sdk_deepseek --log-level=debug --node-config-file=./config/config_fvn_1.toml register 0x2F8aCe76a34e50943573826A326a8Eb8DC854f84 63 | cargo build && ./target/debug/mind_sdk_deepseek --log-level=debug --node-config-file=./config/config_fvn_2.toml register 0x3df4b66E1895E68aB000f1086e9393ca1937Cd8b 64 | 65 | cargo build && ./target/debug/mind_sdk_deepseek --log-level=debug --node-config-file=./config/config_fvn.toml deepseek-fhe-vote 66 | cargo build && ./target/debug/mind_sdk_deepseek --log-level=debug --node-config-file=./config/config_fvn_1.toml deepseek-fhe-vote 67 | cargo build && ./target/debug/mind_sdk_deepseek --log-level=debug --node-config-file=./config/config_fvn_2.toml deepseek-fhe-vote 68 | 69 | cargo build && ./target/debug/mind_sdk_deepseek --log-level=debug --node-config-file=./config/config_fvn.toml check-registration 70 | cargo build && ./target/debug/mind_sdk_deepseek --log-level=debug --node-config-file=./config/config_fvn_1.toml check-registration 71 | cargo build && ./target/debug/mind_sdk_deepseek --log-level=debug --node-config-file=./config/config_fvn_2.toml check-registration 72 | ``` 73 | 74 | 75 | 76 | ## CLI Help 77 | ``` bash 78 | # ./bin/deepseek --help 79 | 80 | FHE Randen Voter Node Cli 81 | 82 | Usage: fvn [OPTIONS] 83 | 84 | Commands: 85 | deepseek-fhe-vote let deepseek think and predict, and then encrypted by FHE and submit to Mind Network for model consensus 86 | check-hot-wallet-address check hot wallet address, by default will use ./config/config_fvn.toml 87 | check-gas-balance check hot wallet gas balance, need gas fee to vote 88 | check-registration check if hot wallet has registered with a particular voter wallet 89 | register register voter address 90 | check-vote-rewards check voting rewards 91 | check-vote check voting tx history on the explore 92 | help Print this message or the help of the given subcommand(s) 93 | 94 | Options: 95 | --node-config-file 96 | fvn config file, contains all the config to run fvn [default: ./config/config_fvn.toml] 97 | --log-level 98 | control level of print, useful for debug, default is info [default: info] [possible values: debug, info, warn, error] 99 | --hot-wallet-private-key 100 | fvn wallet private key is needed if to load a different wallet from config_fvn.toml to sign the message onchain, by default load from ./config/config_fvn.toml 101 | -h, --help 102 | Print help 103 | -V, --version 104 | Print version 105 | ``` 106 | 107 | ## CLI Example 108 | ``` bash 109 | ## command 110 | ./bin/deepseek --log-level=info deepseek-fhe-vote 111 | { 112 | "app": "deepseek", 113 | "command": "deepseek-fhe-vote", 114 | "arg": "deekseek predicted BTC price: 95833, hot_wallet: 0x6224F72f1439E76803e063262a7e1c03e86c6Dbd", 115 | "status": true, 116 | "result": "0x42d78185e4779dd3105598ac4f2786998c5059f8381a55daec12e4ffcc952a56", 117 | "note": "deekseek predicted BTC price: 95833, gas_sued: 304749, block_number: 26373, tx_hash: 0x42d78185e4779dd3105598ac4f2786998c5059f8381a55daec12e4ffcc952a56" 118 | } 119 | 120 | ## command 121 | ./bin/deepseek --log-level=info check-hot-wallet-address 122 | { 123 | "app": "deepseek", 124 | "command": "check-hot-wallet-address", 125 | "arg": "", 126 | "status": true, 127 | "result": "0x64FF17078669A507D0c831D9E844AF1C967604Dd", 128 | "note": "" 129 | } 130 | 131 | ## command 132 | ./bin/deepseek --log-level=info check-gas-balance 133 | { 134 | "app": "deepseek", 135 | "command": "check-gas-balance", 136 | "arg": "hot_wallet: 0x6224F72f1439E76803e063262a7e1c03e86c6Dbd", 137 | "status": true, 138 | "result": "197015375000000", 139 | "note": "" 140 | } 141 | 142 | ## command 143 | ./bin/deepseek --log-level=info check-registration 144 | { 145 | "app": "deepseek", 146 | "command": "check-registration", 147 | "arg": "0x6224F72f1439E76803e063262a7e1c03e86c6Dbd", 148 | "status": false, 149 | "result": "", 150 | "note": "hot wallet is not registered with any voter wallet" 151 | } 152 | 153 | ## command 154 | ./bin/deepseek --log-level=info check-vote-rewards 155 | { 156 | "app": "deepseek", 157 | "command": "check-vote-rewards", 158 | "arg": "hot_wallet: 0x6224F72f1439E76803e063262a7e1c03e86c6Dbd", 159 | "status": false, 160 | "result": "0", 161 | "note": "hot_wallet: 0x6224F72f1439E76803e063262a7e1c03e86c6Dbd, voter_wallet: , vote_rewards: 0" 162 | } 163 | 164 | ## command 165 | ./bin/deepseek --log-level=info register 0x06eF5C5ba427434bf36469B877e4ea9044D1b735 166 | { 167 | "app": "deepseek", 168 | "command": "register", 169 | "arg": "hot_wallet: 0x6224F72f1439E76803e063262a7e1c03e86c6Dbd, voter_wallet: 0x06eF5C5ba427434bf36469B877e4ea9044D1b735", 170 | "status": true, 171 | "result": "registration successful !", 172 | "note": "is_registered: true, hot_wallet: 0x6224F72f1439E76803e063262a7e1c03e86c6Dbd, voter_wallet: 0x06eF5C5ba427434bf36469B877e4ea9044D1b735" 173 | } 174 | 175 | 176 | ## command 177 | ./bin/deepseek --log-level=info check-vote-rewards 178 | { 179 | "app": "deepseek", 180 | "command": "check-vote-rewards", 181 | "arg": "hot_wallet: 0x6224F72f1439E76803e063262a7e1c03e86c6Dbd", 182 | "status": true, 183 | "result": "206095238095238095", 184 | "note": "hot_wallet: 0x6224F72f1439E76803e063262a7e1c03e86c6Dbd, voter_wallet: 0x06eF5C5ba427434bf36469B877e4ea9044D1b735, vote_rewards: 206095238095238095" 185 | } 186 | 187 | ## command 188 | ./bin/deepseek --log-level=info check-vote 189 | { 190 | "app": "deepseek", 191 | "command": "check-vote", 192 | "arg": "0x6224F72f1439E76803e063262a7e1c03e86c6Dbd", 193 | "status": true, 194 | "result": "check on the explore: testnet: https://explorer-testnet.mindnetwork.xyz/address/0x6224F72f1439E76803e063262a7e1c03e86c6Dbd, mainnet: https://explorer.mindnetwork.xyz/address/0x6224F72f1439E76803e063262a7e1c03e86c6Dbd", 195 | "note": "" 196 | } 197 | 198 | ``` 199 | 200 | ## **License** 201 | 202 | This project is licensed under the **MIT License**. 203 | 204 | ## **Contact** 205 | 206 | For questions or support, please contact [Mind Network Official Channels](https://mindnetwork.xyz/). 207 | 208 | 209 | -------------------------------------------------------------------------------- /src/fvn.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol_types::{SolValue}; 2 | use mind_sdk_chain::fhe::FvnNodeInterface; 3 | 4 | //////////////////////////////////////////////////////////////////////////////////////////////// 5 | #[derive(Debug, Clone)] 6 | pub struct Fvn { 7 | pub config: crate::fvn_config::FvnConfig, 8 | pub basic_config: mind_sdk_config::BasicConfig, 9 | pub node_config_file: String, 10 | pub onchain: mind_sdk_chain::function::OnChainFunction, 11 | pub given_random_int_pt: Option, 12 | pub fhe_public_key_local_fp: String, 13 | pub fhe: Option, 14 | pub fvn_contract: crate::fvn_contract::FvnContractInstance, 15 | pub fvn_round_contract: crate::fvn_round_contract::FvnRoundContractInstance, 16 | } 17 | 18 | impl mind_sdk_chain::fhe::FvnNodeInterface for Fvn { 19 | async fn download_fhekey_if_not_exist(&mut self) -> Result<(), mind_sdk_util::MindError> { 20 | let fhe_public_key_data = self.fvn_contract.fheKeySetID().call().await?; 21 | let fhekeyset_id = fhe_public_key_data._0; 22 | let fp_dir = self.config.dir_data_fvn.clone(); 23 | let fhe_key_type = self.config.fhe_key_type.clone(); 24 | let _pk_fp = self 25 | .onchain 26 | .get_fhe_publickey( 27 | fhekeyset_id, 28 | fp_dir, 29 | self.basic_config.clone(), 30 | fhe_key_type, 31 | ) 32 | .await?; 33 | self.fhe_public_key_local_fp = _pk_fp; 34 | Ok(()) 35 | } 36 | 37 | async fn set_fhe(&mut self) -> Result<(), mind_sdk_util::MindError> { 38 | self.download_fhekey_if_not_exist().await?; 39 | let fhe: mind_sdk_fhe::FheInt = 40 | mind_sdk_fhe::FheInt::new_from_public_key_local(&self.fhe_public_key_local_fp); 41 | self.fhe = Some(fhe); 42 | log::debug!("set_fhe: {:?} {:?}", self.fhe.is_some(), self.fhe); 43 | Ok(()) 44 | } 45 | 46 | fn fhe_encrypt(&self, random_u128: u128) -> Result { 47 | match &self.fhe { 48 | Some(fhe) => { 49 | let random_ct: tfhe::integer::RadixCiphertext = 50 | mind_sdk_fhe::fhe_client::encrypt(fhe, "u8", random_u128); 51 | let fhe_content: String = mind_sdk_fhe::io::serialize_base64(random_ct)?; 52 | log::debug!( 53 | "encrypt ({:?}) and get ciphertext with size: {}", 54 | random_u128, 55 | fhe_content.len() 56 | ); 57 | return Ok(fhe_content); 58 | } 59 | None => { 60 | let error = mind_sdk_util::MindError::FheNotInit("".to_string()); 61 | return Err(error); 62 | } 63 | } 64 | } 65 | 66 | async fn submit_fhe_encrypted( 67 | &self, 68 | fhe_content: String, 69 | ) -> Result { 70 | log::debug!("submit ciphertext: {}", &fhe_content.len()); 71 | let response = mind_sdk_io::upload_ciphertext( 72 | &fhe_content, 73 | &self.config.fvn_wallet_private_key, 74 | self.basic_config.subnet_id as usize, 75 | &self.basic_config.upload_url, 76 | ) 77 | .await?; 78 | log::debug!("{:#?}", response); 79 | if response.status().is_success() { 80 | /* let digest = sha3::Keccak256::new_with_prefix(fhe_content.clone()); 81 | let file_digest = digest.clone().finalize(); 82 | let filename = hex::encode(file_digest); 83 | log::debug!("filename: {:#?}, len: {}", filename, filename.len()); 84 | log::debug!("upload_url: {:#?}", &self.basic_config.upload_url); 85 | */ 86 | let url_response: mind_sdk_io::Reply = response.json().await?; 87 | log::debug!("url: {:#?}", url_response); 88 | let url = url_response.url; 89 | log::debug!("url: {:#?}", url); 90 | 91 | let gas_amount = self.basic_config.gas_amount; 92 | let tx_receipt = self.submit_random_ct(url, gas_amount).await?; 93 | return Ok(tx_receipt); 94 | } else { 95 | log::error!("{:#?}", &response); 96 | let es = format!("cipher upload error: {:#?}", &response); 97 | let error = mind_sdk_util::MindError::CloudStroageCipherUploadError(es.clone()); 98 | return Err(error); 99 | } 100 | } 101 | } 102 | 103 | //////////////////////////////////////////////////////////////////////////////////////////////// 104 | impl crate::Fvn { 105 | pub async fn submit_random_ct( 106 | &self, 107 | ct_url: String, 108 | new_gas_amount: u64, 109 | ) -> Result { 110 | // Step 0: Set up call builder 111 | let contract_call_builder = self.fvn_contract.submitRandomCt(ct_url.abi_encode().into()); 112 | 113 | // Step 1: Set up the transaction with the gas amount and wallet address 114 | let tx = contract_call_builder 115 | .clone() 116 | .gas(new_gas_amount) 117 | .from(self.onchain.account.address); 118 | 119 | // Step 2: Run the simulation 120 | match tx.call().await { 121 | Ok(simulation_result) => { 122 | log::debug!("Simulation successful: {:?}", simulation_result); 123 | 124 | // Only proceed with sending the transaction if the simulation is successful 125 | let send_result = tx.send().await?; 126 | 127 | // Step 3: Wait for the transaction receipt 128 | let tx_receipt = send_result.get_receipt().await?; 129 | //log::info!("Confirmed TX: {:?}", tx_receipt); 130 | log::info!("Confirmed TX: status: {:?}, hash: {:?}, gas: {:?}, block: {:?}", &tx_receipt.status(), &tx_receipt.transaction_hash, &tx_receipt.gas_used, &tx_receipt.block_number.unwrap_or(0)); 131 | Ok(tx_receipt) 132 | } 133 | Err(e) => { 134 | return Err(self.process_contract_call_error(e).await); 135 | }, 136 | } 137 | } 138 | } 139 | 140 | 141 | 142 | /////////////////////////////////////////////////////////////////////////// 143 | impl crate::Fvn { 144 | // for randgen 145 | pub async fn set( 146 | node_wallet_private_key: String, 147 | fp_config_template: String, 148 | int: u128, 149 | ) -> Result { 150 | let mut fvn = crate::Fvn::new(fp_config_template).await?; 151 | fvn.config.fvn_wallet_private_key = node_wallet_private_key.clone(); 152 | fvn.update_config().await?; 153 | fvn.given_random_int_pt = Some(int); 154 | return Ok(fvn); 155 | } 156 | 157 | pub async fn run_once( 158 | &mut self, prediction: u128 159 | ) -> Result { 160 | let isvoterready = self.check_if_voter_ready().await?; 161 | match isvoterready { 162 | true => { 163 | let pt = prediction; 164 | 165 | if self.fhe.is_none() { 166 | let _ = self.set_fhe().await; 167 | } 168 | 169 | log::debug!( 170 | "hot: {:?}, to_encrypt: {:?}", 171 | self.onchain.account.address, 172 | pt 173 | ); 174 | let ct = self.fhe_encrypt(pt)?; 175 | let result: alloy::rpc::types::TransactionReceipt = 176 | self.submit_fhe_encrypted(ct).await?; 177 | return Ok(result); 178 | } 179 | false => { 180 | let es = format!("voter is not ready: {:#?}", &self.onchain.account.address); 181 | log::error!("{}", &es); 182 | let error = mind_sdk_util::MindError::VoterNotReady(es); 183 | return Err(error); 184 | } 185 | } 186 | } 187 | 188 | 189 | pub async fn run_once_with_deepseek_prediction(&mut self, fhe_public_key_fp: String) -> Result { 190 | let client = deepseek_rs::DeepSeekClient::default().unwrap(); 191 | let request = deepseek_rs::client::chat_completions::request::RequestBody::new_messages(vec![ 192 | deepseek_rs::client::chat_completions::request::Message::new_user_message("Please predict BTC price in next 7 days, return must be a positive integer".to_string()) 193 | ]).with_model(deepseek_rs::client::chat_completions::request::Model::DeepSeekReasoner); 194 | let response = client.chat_completions(request).await.unwrap(); 195 | //println!("Reasoning: {}", response.choices[0].message.reasoning_content.unwrap()); 196 | //println!("Answer: {}", response.choices[0].message.content.unwrap()); 197 | let deepseek_prediction = match response.choices[0].clone().message.content.unwrap().parse::() { 198 | Ok(prediction) => prediction, 199 | Err(_) => 0, 200 | }; 201 | let fhe: mind_sdk_fhe::FheInt = mind_sdk_fhe::FheInt::new_from_public_key_local(&fhe_public_key_fp); 202 | let ciphertext = mind_sdk_fhe::fhe_client::encrypt(&fhe, "u8", deepseek_prediction.clone()); 203 | let ciphertext_str: String = mind_sdk_fhe::io::serialize_base64(ciphertext)?; 204 | let result: alloy::rpc::types::TransactionReceipt = self.submit_fhe_encrypted(ciphertext_str).await?; 205 | return Ok(result); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/fvn_help.rs: -------------------------------------------------------------------------------- 1 | impl crate::Fvn { 2 | // for randgen 3 | 4 | pub async fn check_round(&self) -> Result<(), mind_sdk_util::MindError> { 5 | let current_round = self.fvn_round_contract.currentRoundNumber().call().await?; 6 | let current_round_number = current_round._0.to_string().parse::()?; 7 | 8 | let used_round = self.fvn_round_contract.usedRoundNumber().call().await?; 9 | let used_round_number = used_round._0.to_string().parse::()?; 10 | 11 | println!("current round: {:#?}", current_round_number); 12 | println!("used round: {:#?}", used_round_number); 13 | 14 | let current_round_details = self 15 | .fvn_round_contract 16 | .rounds(alloy::primitives::Uint::from(current_round_number)) 17 | .call() 18 | .await?; 19 | println!("current round details: {:#?}", current_round_details); 20 | 21 | let current_round_details = self 22 | .fvn_round_contract 23 | .rounds(alloy::primitives::Uint::from(current_round_number - 1)) 24 | .call() 25 | .await 26 | .unwrap(); 27 | println!("current-1 round details: {:#?}", current_round_details); 28 | 29 | let current_round_details = self 30 | .fvn_round_contract 31 | .rounds(alloy::primitives::Uint::from(current_round_number - 2)) 32 | .call() 33 | .await?; 34 | println!("current-2 round details: {:#?}", current_round_details); 35 | 36 | let used_round_details = self 37 | .fvn_round_contract 38 | .rounds(alloy::primitives::Uint::from(used_round_number)) 39 | .call() 40 | .await?; 41 | println!("used round details: {:#?}", used_round_details); 42 | Ok(()) 43 | } 44 | 45 | pub async fn check_if_voter_ready(&self) -> Result { 46 | let hot_wallet_address = self.onchain.account.address; 47 | let voter = self 48 | .fvn_contract 49 | .isVoterReady(hot_wallet_address.clone()) 50 | .call() 51 | .await?; 52 | log::debug!("{:#?} {:#?}", &voter, &self.onchain.account.address); 53 | if voter._0.to_string() == "404".to_string() { 54 | log::error!( 55 | "hot_wallet: {:?} is not registered, please register before voting ...", 56 | hot_wallet_address 57 | ); 58 | return Ok(false); 59 | } else { 60 | if voter._0 != alloy::primitives::U256::ZERO { 61 | // not registered on chain 62 | return Ok(false); 63 | } else { 64 | let hot_wallet_address_eth_balance = self.onchain.get_eth_balance().await?; 65 | 66 | if hot_wallet_address_eth_balance 67 | >= alloy::primitives::U256::from(self.basic_config.min_gas) 68 | { 69 | return Ok(true); 70 | } else { 71 | // hot_wallet has no enought gas 72 | log::error!( 73 | "hot_wallet has no enough gas ! hot wallet: {:?}, has: {:?}, require:{:?}", 74 | &self.onchain.account.address, 75 | hot_wallet_address_eth_balance, 76 | self.basic_config.min_gas 77 | ); 78 | return Ok(false); // TODO to change back 79 | } 80 | } 81 | } 82 | } 83 | 84 | pub async fn get_voter_wallet( 85 | &self, 86 | ) -> Result, mind_sdk_util::MindError> { 87 | let registered_voter_address = self 88 | .fvn_contract 89 | .hotWalletToVoter(self.onchain.account.address) 90 | .call() 91 | .await?; 92 | let registered_voter_address = registered_voter_address.voter; 93 | log::debug!("{:#?}", ®istered_voter_address); 94 | if registered_voter_address.clone().to_string() 95 | == "0x0000000000000000000000000000000000000000" 96 | { 97 | return Ok(None); 98 | } else { 99 | return Ok(Some(registered_voter_address)); 100 | } 101 | } 102 | 103 | pub async fn check_hot_voter_registration( 104 | &self, 105 | hot_wallet_address: &alloy::primitives::Address, 106 | voter_wallet_address: &alloy::primitives::Address, 107 | ) -> Result { 108 | let registered_voter_address = self 109 | .fvn_contract 110 | .hotWalletToVoter(hot_wallet_address.clone()) 111 | .call() 112 | .await?; 113 | let registered_voter_address = registered_voter_address.voter; 114 | if registered_voter_address == voter_wallet_address.clone() { 115 | return Ok(true); 116 | } else { 117 | if registered_voter_address.clone().to_string() 118 | == "0x0000000000000000000000000000000000000000" 119 | { 120 | log::debug!( 121 | "hot_wallet_is_new_and_has_no_hot_wallet_registered !: {:?}", 122 | hot_wallet_address 123 | ); 124 | return Ok(false); 125 | } else { 126 | log::error!( 127 | "hot_wallet_is_registered_with_other_hot_wallet_error !: hot: {:?}, other voter_wallet: {:?}, and not match to expected voter wallet: {:?}", 128 | hot_wallet_address, registered_voter_address, voter_wallet_address 129 | ); 130 | return Ok(false); 131 | } 132 | } 133 | } 134 | 135 | pub async fn hot_wallet_send_voter_registration( 136 | &self, 137 | voter_wallet_address: alloy::primitives::Address, 138 | ) -> Result { 139 | let registration_status = self 140 | .check_hot_voter_registration(&self.onchain.account.address, &voter_wallet_address) 141 | .await?; 142 | match registration_status { 143 | true => { 144 | log::debug!("voter and hot is already registered, will register again"); 145 | return Err(mind_sdk_util::MindError::VoterAlreadyRegistered(format!( 146 | "duplicated voter registration: hot: {:#?}, voter: {:#?}", 147 | &self.onchain.account.address, &voter_wallet_address 148 | ))); 149 | } 150 | false => { 151 | log::debug!( 152 | "voter and hot is not registered or registered with others, will register here" 153 | ); 154 | } 155 | } 156 | 157 | // Step 1: Set up the transaction with the gas amount and wallet address 158 | let contract_call_builder = self 159 | .fvn_contract 160 | .registerVoter(voter_wallet_address.clone()); 161 | 162 | // Step 2: Run the simulation 163 | let estimated_gas = contract_call_builder.estimate_gas().await?; 164 | 165 | let new_gas_amount = estimated_gas; 166 | let tx = contract_call_builder 167 | .clone() 168 | .gas(new_gas_amount) 169 | .from(self.onchain.account.address); 170 | 171 | match tx.call().await { 172 | Ok(simulation_result) => { 173 | log::debug!("Simulation successful: {:?}", simulation_result); 174 | // Only proceed with sending the transaction if the simulation is successful 175 | let send_result = tx.send().await?; 176 | 177 | // Step 3: Wait for the transaction receipt 178 | let tx_receipt: alloy::rpc::types::TransactionReceipt = 179 | send_result.get_receipt().await?; 180 | 181 | log::info!( 182 | "Confirmed TX: status: {:?}, hash: {:?}, gas: {:?}, block: {:?}", 183 | &tx_receipt.status(), 184 | &tx_receipt.transaction_hash, 185 | &tx_receipt.gas_used, 186 | &tx_receipt.block_number.unwrap_or(0) 187 | ); 188 | Ok(tx_receipt) 189 | } 190 | Err(e) => { 191 | let _r = mind_sdk_util::jsonlog( 192 | "error", 193 | 480, 194 | "call simulation error", 195 | serde_json::json!({"caller": self.onchain.account.address, "error": format!("{:#?}", &e)}), 196 | )?; 197 | log::error!("Simulation failed: {:?}", e); 198 | Err(mind_sdk_util::MindError::ContractCallSimulationError(e)) 199 | } 200 | } 201 | } 202 | 203 | pub async fn register_voter_wallet( 204 | &self, 205 | voter_wallet_address: alloy::primitives::Address, 206 | ) -> Result<(), mind_sdk_util::MindError> { 207 | let tx_receipt = self 208 | .hot_wallet_send_voter_registration(voter_wallet_address) 209 | .await?; 210 | log::debug!("registration is done: {:#?}", tx_receipt); 211 | let voter_ready = self.check_if_voter_ready().await?; 212 | if voter_ready { 213 | log::info!( 214 | "Voter is correctly set: hot: {:?}, voter: {:?}", 215 | self.onchain.account.address, 216 | voter_wallet_address 217 | ); 218 | } else { 219 | log::error!( 220 | "voter is not correctly set, hot: {:?}, expected voter: {:?}", 221 | self.onchain.account.address, 222 | voter_wallet_address 223 | ); 224 | } 225 | Ok(()) 226 | } 227 | 228 | pub fn generate_random_int_pt(&self) -> u128 { 229 | let random_u128: u128 = rand::random(); 230 | log::debug!("generated random_int: {}", random_u128); 231 | return random_u128; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | 3 | #[tokio::main] 4 | pub async fn main() -> Result<(), mind_sdk_util::MindError> { 5 | match main_fn().await { 6 | Ok(_) => Ok(()), 7 | Err(e) => Err(e), 8 | } 9 | } 10 | 11 | pub async fn main_fn() -> Result<(), mind_sdk_util::MindError> { 12 | mind_sdk_util::init_log(); 13 | let cli = mind_sdk_deepseek::fvn_cli::Cli::parse(); 14 | println!("cli: {:#?}", cli); 15 | let log_level = mind_sdk_cli::get_cli_log_level(cli.log_level); 16 | log::set_max_level(log_level); 17 | 18 | if !std::path::Path::new(&cli.node_config_file.clone()).exists() { 19 | let isexist = std::path::Path::new(&cli.node_config_file).exists(); 20 | if isexist == false { 21 | mind_sdk_util::list_files_in_directory_if_file_not_exist(&cli.node_config_file); 22 | } 23 | log::error!("your config file is not exist, created or specific with --node-config-file OTHER_CONFIG_FILE_PATH!") 24 | } 25 | 26 | let app = "randgen".to_string(); 27 | let mut fvn = mind_sdk_deepseek::Fvn::new(cli.node_config_file.clone()).await?; 28 | 29 | match cli.hot_wallet_private_key { 30 | Some(hot_wallet_private_key) => { 31 | fvn.config.fvn_wallet_private_key = hot_wallet_private_key.clone(); 32 | fvn.update_config().await?; 33 | } 34 | None => {} 35 | }; 36 | 37 | match &cli.command { 38 | mind_sdk_deepseek::fvn_cli::Commands::CheckHotWalletAddress(_arg) => { 39 | let mut r = mind_sdk_cli::cli_default_json_result(app); 40 | r.command = "check-hot-wallet-address".to_string(); 41 | r.result = fvn.onchain.account.address.to_string(); 42 | println!("{}", serde_json::to_string_pretty(&r).unwrap()); 43 | Ok(()) 44 | } 45 | mind_sdk_deepseek::fvn_cli::Commands::CheckGasBalance(arg) => { 46 | let mut r = mind_sdk_cli::cli_default_json_result(app); 47 | r.command = "check-gas-balance".to_string(); 48 | match arg.hot_wallet_address.clone() { 49 | Some(hot_wallet_address) => { 50 | r.arg = format!("hot_wallet: {}", hot_wallet_address); 51 | r.result = fvn 52 | .onchain 53 | .get_eth_balance_by_wallet(&hot_wallet_address) 54 | .await? 55 | .to_string(); 56 | } 57 | None => { 58 | r.arg = format!("hot_wallet: {}", fvn.onchain.account.address.to_string()); 59 | r.result = fvn.onchain.get_eth_balance().await?.to_string(); 60 | } 61 | }; 62 | println!("{}", serde_json::to_string_pretty(&r).unwrap()); 63 | Ok(()) 64 | } 65 | mind_sdk_deepseek::fvn_cli::Commands::CheckRegistration(arg) => { 66 | let mut r = mind_sdk_cli::cli_default_json_result(app); 67 | r.command = "check-registration".to_string(); 68 | let hot_wallet_address = match arg.hot_wallet_address.clone() { 69 | Some(hot_wallet_address_str) => { 70 | mind_sdk_chain::account::get_address_by_str(&hot_wallet_address_str)? 71 | } 72 | None => fvn.onchain.account.address, 73 | }; 74 | r.arg = hot_wallet_address.to_string(); 75 | let mut voter_wallet_address = alloy::primitives::Address::ZERO; 76 | match arg.voter_wallet_address.clone() { 77 | Some(voter_wallet_address_str) => { 78 | voter_wallet_address = 79 | mind_sdk_chain::account::get_address_by_str(&voter_wallet_address_str)?; 80 | } 81 | None => {} 82 | }; 83 | if voter_wallet_address == alloy::primitives::Address::ZERO { 84 | let a = fvn.get_voter_wallet().await?; 85 | match a { 86 | Some(a) => { 87 | r.status = true; 88 | r.result = a.to_string(); 89 | r.note = format!( 90 | "is_registered: true, hot_wallet: {}, voter_wallet: {}", 91 | fvn.onchain.account.address, 92 | a.to_string() 93 | ); 94 | } 95 | None => { 96 | r.status = false; 97 | r.note = format!("hot wallet is not registered with any voter wallet"); 98 | } 99 | } 100 | } else { 101 | let registration_status = fvn 102 | .check_hot_voter_registration(&hot_wallet_address, &voter_wallet_address) 103 | .await?; 104 | r.status = registration_status; 105 | r.note = format!( 106 | "is_registered: {}, hot_wallet: {}, voter_wallet: {}", 107 | registration_status, hot_wallet_address, voter_wallet_address 108 | ); 109 | } 110 | println!("{}", serde_json::to_string_pretty(&r).unwrap()); 111 | Ok(()) 112 | } 113 | mind_sdk_deepseek::fvn_cli::Commands::Register(arg) => { 114 | let mut r = mind_sdk_cli::cli_default_json_result(app); 115 | r.command = "register".to_string(); 116 | match arg.voter_wallet_address.clone() { 117 | Some(voter_wallet_address_str) => { 118 | let voter_wallet_address = 119 | mind_sdk_chain::account::get_address_by_str(&voter_wallet_address_str)?; 120 | let _result = fvn.register_voter_wallet(voter_wallet_address).await?; 121 | r.arg = format!( 122 | "hot_wallet: {}, voter_wallet: {}", 123 | fvn.onchain.account.address.to_string(), 124 | voter_wallet_address_str 125 | ); 126 | r.status = true; 127 | r.result = "registration successful !".to_string(); 128 | r.note = format!( 129 | "is_registered: true, hot_wallet: {}, voter_wallet: {}", 130 | fvn.onchain.account.address, voter_wallet_address_str 131 | ); 132 | } 133 | None => { 134 | r.arg = format!( 135 | "hot_wallet: {}, voter_wallet: none", 136 | fvn.onchain.account.address.to_string() 137 | ); 138 | r.status = false; 139 | r.result = "voter_wallet is undefined".to_string(); 140 | r.note = format!("voter_wallet must provide for registration"); 141 | } 142 | }; 143 | println!("{}", serde_json::to_string_pretty(&r).unwrap()); 144 | Ok(()) 145 | } 146 | mind_sdk_deepseek::fvn_cli::Commands::DeepseekFheVote(arg) => { 147 | let tx_receipt = fvn.run_once_with_deepseek_prediction(arg.fhe_public_key_fp.clone()).await?; 148 | let _gas_used = tx_receipt.gas_used; 149 | let _status = tx_receipt.status(); 150 | let _block_number = tx_receipt.block_number; 151 | let _tx_hash: String = tx_receipt.transaction_hash.to_string(); 152 | 153 | let mut r = mind_sdk_cli::cli_default_json_result(app); 154 | r.command = "deepseek-fhe-vote".to_string(); 155 | r.arg = format!( 156 | "number: {}, hot_wallet: {}", 157 | arg.deepseek_api_key, 158 | fvn.onchain.account.address.to_string() 159 | ); 160 | r.status = _status; 161 | r.result = _tx_hash.to_string(); 162 | r.note = format!( 163 | "deepseek predicted btc price:, gas_sued: {}, block_number: {:?}, tx_hash: {}", 164 | _gas_used, _block_number, _tx_hash 165 | ); 166 | println!("{}", serde_json::to_string_pretty(&r).unwrap()); 167 | Ok(()) 168 | } 169 | mind_sdk_deepseek::fvn_cli::Commands::CheckVoteRewards(_arg) => { 170 | let rewards = fvn.check_rewards().await?; 171 | let mut r = mind_sdk_cli::cli_default_json_result(app); 172 | r.command = "check-vote-rewards".to_string(); 173 | r.arg = format!("hot_wallet: {}", fvn.onchain.account.address.to_string()); 174 | match rewards { 175 | Some((voter_wallet_address, rewards)) => { 176 | r.status = true; 177 | r.result = rewards.to_string(); 178 | r.note = format!( 179 | "hot_wallet: {}, voter_wallet: {}, vote_rewards: {}", 180 | fvn.onchain.account.address, voter_wallet_address, rewards 181 | ); 182 | println!("{}", serde_json::to_string_pretty(&r).unwrap()); 183 | Ok(()) 184 | } 185 | None => { 186 | r.status = false; 187 | r.result = "".to_string(); 188 | r.note = format!("hot_wallet is not registered yet",); 189 | println!("{}", serde_json::to_string_pretty(&r).unwrap()); 190 | Ok(()) 191 | } 192 | } 193 | } 194 | mind_sdk_deepseek::fvn_cli::Commands::CheckVote(_arg) => { 195 | let mut r = mind_sdk_cli::cli_default_json_result(app); 196 | r.command = "check-vote".to_string(); 197 | r.arg = fvn.onchain.account.address.to_string(); 198 | r.status = true; 199 | let hot_wallet_address_str = fvn.onchain.account.address.to_string(); 200 | r.result = format!("check on the explore: testnet: https://explorer-testnet.mindnetwork.xyz/address/{}, mainnet: https://explorer.mindnetwork.xyz/address/{}", &hot_wallet_address_str, &hot_wallet_address_str); 201 | println!("{}", serde_json::to_string_pretty(&r).unwrap()); 202 | Ok(()) 203 | } 204 | mind_sdk_deepseek::fvn_cli::Commands::CheckRound(_arg) => { 205 | let mut r = mind_sdk_cli::cli_default_json_result(app); 206 | r.command = "check-round".to_string(); 207 | r.arg = fvn.onchain.account.address.to_string(); 208 | r.status = true; 209 | let _hot_wallet_address_str = fvn.check_round().await; 210 | r.result = format!("see stdout"); 211 | println!("{}", serde_json::to_string_pretty(&r).unwrap()); 212 | Ok(()) 213 | } 214 | } 215 | } 216 | --------------------------------------------------------------------------------