diff --git a/Cargo.lock b/Cargo.lock index 7909926f..99297100 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,27 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "admin-cli" +version = "0.1.0" +dependencies = [ + "alloy", + "clap", + "dirs", + "dotenv", + "eyre", + "hex", + "rand 0.9.1", + "rpassword", + "serde", + "serde_json", + "shared", + "tokio", + "tokio-util", + "toml", + "url", +] + [[package]] name = "aead" version = "0.5.2" @@ -2311,21 +2332,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "dev-utils" -version = "0.1.0" -dependencies = [ - "alloy", - "clap", - "eyre", - "hex", - "rand 0.9.1", - "shared", - "tokio", - "tokio-util", - "url", -] - [[package]] name = "diatomic-waker" version = "0.2.3" @@ -2359,7 +2365,28 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "dirs-sys", + "dirs-sys 0.5.0", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.4.6", + "windows-sys 0.48.0", ] [[package]] @@ -2370,7 +2397,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.5.0", "windows-sys 0.59.0", ] @@ -2433,6 +2460,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dtoa" version = "1.0.10" @@ -5812,6 +5845,17 @@ dependencies = [ "bitflags 2.9.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "redox_users" version = "0.5.0" @@ -5981,6 +6025,17 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rpassword" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.59.0", +] + [[package]] name = "rtnetlink" version = "0.13.1" @@ -6017,6 +6072,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "rtoolbox" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "ruint" version = "1.14.0" diff --git a/Cargo.toml b/Cargo.toml index e5fa95bd..b4edc1b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = [ "crates/validator", "crates/shared", "crates/orchestrator", - "crates/dev-utils", + "crates/admin-cli", ] resolver = "2" diff --git a/Makefile b/Makefile index 1d171ae2..82d13239 100644 --- a/Makefile +++ b/Makefile @@ -4,43 +4,43 @@ ENV_FILE ?= .env mint-ai-tokens-to-provider: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example mint_ai_token -- --address $${PROVIDER_ADDRESS} --key $${PRIVATE_KEY_FEDERATOR} --rpc-url $${RPC_URL} + cargo run -p admin-cli -- token mint --address $${PROVIDER_ADDRESS} --key env:PRIVATE_KEY_FEDERATOR --rpc-url $${RPC_URL} test-concurrent-calls: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example test_concurrent_calls -- --address $${PROVIDER_ADDRESS} --key $${PRIVATE_KEY_FEDERATOR} --rpc-url $${RPC_URL} + cargo run -p admin-cli -- test concurrent-calls --address $${PROVIDER_ADDRESS} --key env:PRIVATE_KEY_FEDERATOR --rpc-url $${RPC_URL} mint-ai-tokens-to-federator: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example mint_ai_token -- --address $${FEDERATOR_ADDRESS} --key $${PRIVATE_KEY_FEDERATOR} --rpc-url $${RPC_URL} --amount 1000000000000000000 + cargo run -p admin-cli -- token mint --address $${FEDERATOR_ADDRESS} --key env:PRIVATE_KEY_FEDERATOR --rpc-url $${RPC_URL} --amount 1000000000000000000 transfer-eth-to-provider: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example transfer_eth -- --address $${PROVIDER_ADDRESS} --key $${PRIVATE_KEY_FEDERATOR} --rpc-url $${RPC_URL} --amount 100000000000000000 + cargo run -p admin-cli -- token transfer-eth --address $${PROVIDER_ADDRESS} --key env:PRIVATE_KEY_FEDERATOR --rpc-url $${RPC_URL} --amount 0.1 transfer-eth-to-pool-owner: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example transfer_eth -- --address $${POOL_OWNER_ADDRESS} --key $${PRIVATE_KEY_FEDERATOR} --rpc-url $${RPC_URL} --amount 1000000000000000000 + cargo run -p admin-cli -- token transfer-eth --address $${POOL_OWNER_ADDRESS} --key env:PRIVATE_KEY_FEDERATOR --rpc-url $${RPC_URL} --amount 1 create-domain: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example create_domain -- --domain-name "$${DOMAIN_NAME:-default_domain}" --domain-uri "$${DOMAIN_URI:-http://default.uri}" --key $${PRIVATE_KEY_FEDERATOR} --validation-logic $${WORK_VALIDATION_CONTRACT} --rpc-url $${RPC_URL} + cargo run -p admin-cli -- domain create --domain-name "$${DOMAIN_NAME:-default_domain}" --domain-uri "$${DOMAIN_URI:-http://default.uri}" --key env:PRIVATE_KEY_FEDERATOR --validation-logic $${WORK_VALIDATION_CONTRACT} --rpc-url $${RPC_URL} create-training-domain: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example create_domain -- --domain-name "$${DOMAIN_NAME:-training}" --domain-uri "$${DOMAIN_URI:-http://default.uri}" --key $${PRIVATE_KEY_FEDERATOR} --rpc-url $${RPC_URL} --validation-logic $${WORK_VALIDATION_CONTRACT} + cargo run -p admin-cli -- domain create-training --domain-name "$${DOMAIN_NAME:-training}" --domain-uri "$${DOMAIN_URI:-http://default.uri}" --key env:PRIVATE_KEY_FEDERATOR --rpc-url $${RPC_URL} create-synth-data-domain: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example create_domain -- --domain-name "$${DOMAIN_NAME:-synth_data}" --domain-uri "$${DOMAIN_URI:-http://default.uri}" --key $${PRIVATE_KEY_FEDERATOR} --rpc-url $${RPC_URL} --validation-logic $${WORK_VALIDATION_CONTRACT} + cargo run -p admin-cli -- domain create-synth-data --domain-name "$${DOMAIN_NAME:-synth_data}" --domain-uri "$${DOMAIN_URI:-http://default.uri}" --key env:PRIVATE_KEY_FEDERATOR --rpc-url $${RPC_URL} create-compute-pool: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example compute_pool -- --domain-id "$${DOMAIN_ID:-0}" --compute-manager-key "$${POOL_OWNER_ADDRESS}" --pool-name "$${POOL_NAME:-default_pool}" --pool-data-uri "$${POOL_DATA_URI:-http://default.pool.data}" --key $${PRIVATE_KEY_FEDERATOR} --rpc-url $${RPC_URL} + cargo run -p admin-cli -- pool create --domain-id "$${DOMAIN_ID:-0}" --compute-manager-key "$${POOL_OWNER_ADDRESS}" --pool-name "$${POOL_NAME:-default_pool}" --pool-data-uri "$${POOL_DATA_URI:-http://default.pool.data}" --key env:PRIVATE_KEY_FEDERATOR --rpc-url $${RPC_URL} start-compute-pool: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example start_compute_pool -- --key $${POOL_OWNER_PRIVATE_KEY} --rpc-url $${RPC_URL} --pool-id="$${POOL_ID:-0}" + cargo run -p admin-cli -- pool start --key env:POOL_OWNER_PRIVATE_KEY --rpc-url $${RPC_URL} --pool-id="$${POOL_ID:-0}" setup: make mint-ai-tokens-to-provider @@ -71,7 +71,7 @@ down: whitelist-provider: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example whitelist_provider -- --provider-address $${PROVIDER_ADDRESS} --key $${PRIVATE_KEY_VALIDATOR} --rpc-url $${RPC_URL} + cargo run -p admin-cli -- provider whitelist --provider-address $${PROVIDER_ADDRESS} --key env:PRIVATE_KEY_VALIDATOR --rpc-url $${RPC_URL} watch-discovery: set -a; source .env; set +a; \ @@ -200,7 +200,7 @@ remote-worker-two: # testing: eject-node: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example eject_node -- --pool-id $${WORKER_COMPUTE_POOL_ID} --node $${NODE_ADDRESS} --provider-address $${PROVIDER_ADDRESS} --key $${POOL_OWNER_PRIVATE_KEY} --rpc-url $${RPC_URL} + cargo run -p admin-cli -- node eject --pool-id $${WORKER_COMPUTE_POOL_ID} --node $${NODE_ADDRESS} --provider-address $${PROVIDER_ADDRESS} --key env:POOL_OWNER_PRIVATE_KEY --rpc-url $${RPC_URL} sign-message: set -a; source ${ENV_FILE}; set +a; \ @@ -212,15 +212,15 @@ balance: get-node-info: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example get_node_info -- --provider-address $${PROVIDER_ADDRESS} --node-address $${NODE_ADDRESS} --key $${PRIVATE_KEY_FEDERATOR} --rpc-url $${RPC_URL} + cargo run -p admin-cli -- node info --provider-address $${PROVIDER_ADDRESS} --node-address $${NODE_ADDRESS} --key env:PRIVATE_KEY_FEDERATOR --rpc-url $${RPC_URL} submit-work: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example submit_work -- --pool-id $${POOL_ID:-0} --node $${NODE_ADDRESS} --work-key $${WORK_KEY} --key $${PRIVATE_KEY_PROVIDER} --rpc-url $${RPC_URL} + cargo run -p admin-cli -- work submit --pool-id $${POOL_ID:-0} --node $${NODE_ADDRESS} --work-key $${WORK_KEY} --key env:PRIVATE_KEY_PROVIDER --rpc-url $${RPC_URL} invalidate-work: set -a; source ${ENV_FILE}; set +a; \ - cargo run -p dev-utils --example invalidate_work -- --pool-id $${POOL_ID:-0} --penalty $${PENALTY} --work-key $${WORK_KEY} --key $${PRIVATE_KEY_VALIDATOR} --rpc-url $${RPC_URL} + cargo run -p admin-cli -- work invalidate --pool-id $${POOL_ID:-0} --penalty $${PENALTY} --work-key $${WORK_KEY} --key env:PRIVATE_KEY_VALIDATOR --rpc-url $${RPC_URL} deregister-worker: set -a; source ${ENV_FILE}; set +a; \ diff --git a/crates/admin-cli/Cargo.toml b/crates/admin-cli/Cargo.toml new file mode 100644 index 00000000..368288bf --- /dev/null +++ b/crates/admin-cli/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "admin-cli" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "admin-cli" +path = "src/main.rs" + +[dependencies] +shared = { workspace = true } +tokio = { workspace = true } +eyre = "0.6" +clap = { workspace = true } +url = { workspace = true } +alloy = { workspace = true } +tokio-util = { workspace = true } +hex = "0.4.3" +rand = "0.9.1" +serde = { workspace = true } +serde_json = { workspace = true } +dirs = "5.0" +rpassword = "7.0" +dotenv = "0.15" +toml = { workspace = true } \ No newline at end of file diff --git a/crates/admin-cli/src/commands/domain.rs b/crates/admin-cli/src/commands/domain.rs new file mode 100644 index 00000000..990b001a --- /dev/null +++ b/crates/admin-cli/src/commands/domain.rs @@ -0,0 +1,136 @@ +use alloy::primitives::Address; +use clap::Subcommand; +use eyre::Result; +use shared::web3::contracts::core::builder::ContractBuilder; +use shared::web3::wallet::Wallet; +use std::str::FromStr; +use url::Url; + +use crate::config::Config; +use crate::secure_key::get_private_key; + +#[derive(Subcommand)] +pub enum DomainCommands { + /// Create a new domain + Create { + /// Domain name to create + #[arg(short = 'd', long)] + domain_name: String, + + /// Validation logic address + #[arg(short = 'v', long)] + validation_logic: Option, + + /// Domain URI + #[arg(short = 'u', long)] + domain_uri: String, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, + /// Create a training domain (preset) + CreateTraining { + /// Domain name (defaults to "training") + #[arg(short = 'd', long)] + domain_name: Option, + + /// Domain URI + #[arg(short = 'u', long, default_value = "http://default.uri")] + domain_uri: String, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, + /// Create a synthetic data domain (preset) + CreateSynthData { + /// Domain name (defaults to "synth_data") + #[arg(short = 'd', long)] + domain_name: Option, + + /// Domain URI + #[arg(short = 'u', long, default_value = "http://default.uri")] + domain_uri: String, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, +} + +pub async fn handle_command(command: DomainCommands, config: &Config) -> Result<()> { + match command { + DomainCommands::Create { + domain_name, + validation_logic, + domain_uri, + key, + } => create_domain(domain_name, validation_logic, domain_uri, key, config).await, + DomainCommands::CreateTraining { + domain_name, + domain_uri, + key, + } => { + let name = domain_name.unwrap_or_else(|| "training".to_string()); + let validation_logic = config.get_contract_address("work_validation").cloned(); + create_domain(name, validation_logic, domain_uri, key, config).await + } + DomainCommands::CreateSynthData { + domain_name, + domain_uri, + key, + } => { + let name = domain_name.unwrap_or_else(|| "synth_data".to_string()); + let validation_logic = config.get_contract_address("work_validation").cloned(); + create_domain(name, validation_logic, domain_uri, key, config).await + } + } +} + +async fn create_domain( + domain_name: String, + validation_logic: Option, + domain_uri: String, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("federator").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .with_domain_registry() + .build()?; + + let validation_logic = validation_logic + .as_deref() + .unwrap_or("0x0000000000000000000000000000000000000000"); + let validation_logic_addr = Address::from_str(validation_logic)?; + + println!("Creating domain: {}", domain_name); + println!("Validation logic: {}", validation_logic); + println!("Domain URI: {}", domain_uri); + + let tx = contracts + .prime_network + .create_domain( + domain_name.clone(), + validation_logic_addr, + domain_uri.clone(), + ) + .await; + + println!("Transaction: {:?}", tx); + + Ok(()) +} diff --git a/crates/admin-cli/src/commands/mod.rs b/crates/admin-cli/src/commands/mod.rs new file mode 100644 index 00000000..48aae52e --- /dev/null +++ b/crates/admin-cli/src/commands/mod.rs @@ -0,0 +1,52 @@ +use crate::config::Config; +use eyre::Result; + +pub mod domain; +pub mod node; +pub mod pool; +pub mod provider; +pub mod test; +pub mod token; +pub mod wallet; +pub mod work; + +pub use domain::*; +pub use node::*; +pub use pool::*; +pub use provider::*; +pub use test::*; +pub use token::*; +pub use wallet::*; +pub use work::*; + +pub async fn handle_token_command(command: TokenCommands, config: &Config) -> Result<()> { + token::handle_command(command, config).await +} + +pub async fn handle_domain_command(command: DomainCommands, config: &Config) -> Result<()> { + domain::handle_command(command, config).await +} + +pub async fn handle_pool_command(command: PoolCommands, config: &Config) -> Result<()> { + pool::handle_command(command, config).await +} + +pub async fn handle_provider_command(command: ProviderCommands, config: &Config) -> Result<()> { + provider::handle_command(command, config).await +} + +pub async fn handle_work_command(command: WorkCommands, config: &Config) -> Result<()> { + work::handle_command(command, config).await +} + +pub async fn handle_node_command(command: NodeCommands, config: &Config) -> Result<()> { + node::handle_command(command, config).await +} + +pub async fn handle_wallet_command(command: WalletCommands, config: &Config) -> Result<()> { + wallet::handle_command(command, config).await +} + +pub async fn handle_test_command(command: TestCommands, config: &Config) -> Result<()> { + test::handle_command(command, config).await +} diff --git a/crates/admin-cli/src/commands/node.rs b/crates/admin-cli/src/commands/node.rs new file mode 100644 index 00000000..26d2666b --- /dev/null +++ b/crates/admin-cli/src/commands/node.rs @@ -0,0 +1,140 @@ +use alloy::primitives::Address; +use clap::Subcommand; +use eyre::Result; +use shared::web3::contracts::core::builder::ContractBuilder; +use shared::web3::wallet::Wallet; +use std::str::FromStr; +use url::Url; + +use crate::config::Config; +use crate::secure_key::get_private_key; + +#[derive(Subcommand)] +pub enum NodeCommands { + /// Get information about a node + Info { + /// Provider address + #[arg(short = 'p', long)] + provider_address: String, + + /// Node address + #[arg(short = 'n', long)] + node_address: String, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, + /// Eject a node from a pool + Eject { + /// Pool ID to eject from + #[arg(long)] + pool_id: u64, + + /// Provider address + #[arg(short = 'p', long)] + provider_address: String, + + /// Node address to eject + #[arg(short = 'n', long)] + node: String, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, +} + +pub async fn handle_command(command: NodeCommands, config: &Config) -> Result<()> { + match command { + NodeCommands::Info { + provider_address, + node_address, + key, + } => get_node_info(provider_address, node_address, key, config).await, + NodeCommands::Eject { + pool_id, + provider_address, + node, + key, + } => eject_node(pool_id, provider_address, node, key, config).await, + } +} + +async fn get_node_info( + provider_address: String, + node_address: String, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("federator").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .build()?; + + let provider_addr = Address::from_str(&provider_address)?; + let node_addr = Address::from_str(&node_address)?; + + println!("Getting node information:"); + println!(" Provider: {}", provider_address); + println!(" Node: {}", node_address); + + let node_info = contracts + .compute_registry + .get_node(provider_addr, node_addr) + .await; + println!("Node info: {:?}", node_info); + + Ok(()) +} + +async fn eject_node( + pool_id: u64, + provider_address: String, + node: String, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("pool_owner").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .build()?; + + let _provider_addr = Address::from_str(&provider_address)?; + let node_addr = Address::from_str(&node)?; + + println!("Ejecting node from pool:"); + println!(" Pool ID: {}", pool_id); + println!(" Provider: {}", provider_address); + println!(" Node: {}", node); + + let tx = contracts + .compute_pool + .eject_node(pool_id as u32, node_addr) + .await; + println!("Transaction: {:?}", tx); + + Ok(()) +} diff --git a/crates/admin-cli/src/commands/pool.rs b/crates/admin-cli/src/commands/pool.rs new file mode 100644 index 00000000..3641ecbe --- /dev/null +++ b/crates/admin-cli/src/commands/pool.rs @@ -0,0 +1,153 @@ +use alloy::primitives::{Address, U256}; +use clap::Subcommand; +use eyre::Result; +use shared::web3::contracts::core::builder::ContractBuilder; +use shared::web3::wallet::Wallet; +use std::str::FromStr; +use url::Url; + +use crate::config::Config; +use crate::secure_key::get_private_key; + +#[derive(Subcommand)] +pub enum PoolCommands { + /// Create a new compute pool + Create { + /// Domain ID for the pool + #[arg(short = 'd', long, default_value = "0")] + domain_id: u64, + + /// Compute manager key/address + #[arg(long)] + compute_manager_key: String, + + /// Pool name + #[arg(short = 'n', long)] + pool_name: String, + + /// Pool data URI + #[arg(long)] + pool_data_uri: String, + + /// Compute limit for the pool + #[arg(long, default_value = "1000")] + compute_limit: u64, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, + /// Start a compute pool + Start { + /// Pool ID to start + #[arg(short = 'p', long, default_value = "0")] + pool_id: u64, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, +} + +pub async fn handle_command(command: PoolCommands, config: &Config) -> Result<()> { + match command { + PoolCommands::Create { + domain_id, + compute_manager_key, + pool_name, + pool_data_uri, + compute_limit, + key, + } => { + create_pool( + domain_id, + compute_manager_key, + pool_name, + pool_data_uri, + compute_limit, + key, + config, + ) + .await + } + PoolCommands::Start { pool_id, key } => start_pool(pool_id, key, config).await, + } +} + +async fn create_pool( + domain_id: u64, + compute_manager_key: String, + pool_name: String, + pool_data_uri: String, + compute_limit: u64, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("federator").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .build()?; + + let compute_manager_addr = Address::from_str(&compute_manager_key)?; + + println!("Creating compute pool:"); + println!(" Domain ID: {}", domain_id); + println!(" Pool name: {}", pool_name); + println!(" Compute manager: {}", compute_manager_key); + println!(" Data URI: {}", pool_data_uri); + println!(" Compute limit: {}", compute_limit); + + let tx = contracts + .compute_pool + .create_compute_pool( + U256::from(domain_id), + compute_manager_addr, + pool_name, + pool_data_uri, + U256::from(compute_limit), + ) + .await; + + println!("Transaction: {:?}", tx); + + Ok(()) +} + +async fn start_pool(pool_id: u64, key: Option, config: &Config) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("pool_owner").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .build()?; + + println!("Starting compute pool ID: {}", pool_id); + + let tx = contracts + .compute_pool + .start_compute_pool(U256::from(pool_id)) + .await; + println!("Transaction: {:?}", tx); + + Ok(()) +} diff --git a/crates/admin-cli/src/commands/provider.rs b/crates/admin-cli/src/commands/provider.rs new file mode 100644 index 00000000..b8a87399 --- /dev/null +++ b/crates/admin-cli/src/commands/provider.rs @@ -0,0 +1,67 @@ +use alloy::primitives::Address; +use clap::Subcommand; +use eyre::Result; +use shared::web3::contracts::core::builder::ContractBuilder; +use shared::web3::wallet::Wallet; +use std::str::FromStr; +use url::Url; + +use crate::config::Config; +use crate::secure_key::get_private_key; + +#[derive(Subcommand)] +pub enum ProviderCommands { + /// Whitelist a provider + Whitelist { + /// Provider address to whitelist + #[arg(short = 'p', long)] + provider_address: String, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, +} + +pub async fn handle_command(command: ProviderCommands, config: &Config) -> Result<()> { + match command { + ProviderCommands::Whitelist { + provider_address, + key, + } => whitelist_provider(provider_address, key, config).await, + } +} + +async fn whitelist_provider( + provider_address: String, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("validator").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .build()?; + + let provider_addr = Address::from_str(&provider_address)?; + + println!("Whitelisting provider: {}", provider_address); + + let tx = contracts + .prime_network + .whitelist_provider(provider_addr) + .await; + println!("Transaction: {:?}", tx); + + Ok(()) +} diff --git a/crates/admin-cli/src/commands/test.rs b/crates/admin-cli/src/commands/test.rs new file mode 100644 index 00000000..861a12c2 --- /dev/null +++ b/crates/admin-cli/src/commands/test.rs @@ -0,0 +1,144 @@ +use alloy::primitives::utils::Unit; +use alloy::primitives::{Address, U256}; +use clap::Subcommand; +use eyre::Result; +use shared::web3::contracts::core::builder::ContractBuilder; +use shared::web3::wallet::Wallet; +use std::str::FromStr; +use tokio::time::{sleep, Duration}; +use url::Url; + +use crate::config::Config; +use crate::secure_key::get_private_key; + +#[derive(Subcommand)] +pub enum TestCommands { + /// Test concurrent blockchain calls + ConcurrentCalls { + /// Address to test with + #[arg(short = 'a', long)] + address: String, + + /// Amount for test transactions + #[arg(short = 'm', long, default_value = "1")] + amount: u64, + + /// Number of concurrent calls + #[arg(short = 'c', long, default_value = "5")] + concurrency: usize, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, +} + +pub async fn handle_command(command: TestCommands, config: &Config) -> Result<()> { + match command { + TestCommands::ConcurrentCalls { + address, + amount, + concurrency, + key, + } => test_concurrent_calls(address, amount, concurrency, key, config).await, + } +} + +async fn test_concurrent_calls( + address: String, + amount: u64, + concurrency: usize, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("federator").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .build()?; + + let target_address = Address::from_str(&address)?; + let mint_amount = U256::from(amount) * Unit::ETHER.wei(); + + println!( + "Testing {} concurrent calls to mint {} tokens to {}", + concurrency, amount, address + ); + println!("Starting concurrent operations..."); + + let mut handles = vec![]; + + for i in 0..concurrency { + let contracts_clone = contracts.clone(); + let target_address_clone = target_address; + let mint_amount_clone = mint_amount; + + let handle = tokio::spawn(async move { + let start = std::time::Instant::now(); + println!(" Call {} starting...", i + 1); + + let result = contracts_clone + .ai_token + .mint(target_address_clone, mint_amount_clone) + .await; + let duration = start.elapsed(); + + match result { + Ok(tx) => { + println!(" Call {} completed in {:?}: {:?}", i + 1, duration, tx); + Ok(()) + } + Err(e) => { + println!(" Call {} failed after {:?}: {}", i + 1, duration, e); + Err(eyre::eyre!("Transaction failed: {}", e)) + } + } + }); + + handles.push(handle); + + sleep(Duration::from_millis(100)).await; + } + + let mut successful = 0; + let mut failed = 0; + + for (i, handle) in handles.into_iter().enumerate() { + match handle.await { + Ok(Ok(())) => { + successful += 1; + } + Ok(Err(_)) | Err(_) => { + failed += 1; + println!(" Handle {} failed to complete", i + 1); + } + } + } + + println!("\nConcurrent call test completed:"); + println!(" Successful: {}", successful); + println!(" Failed: {}", failed); + println!(" Total: {}", successful + failed); + + let final_balance = contracts.ai_token.balance_of(target_address).await; + match final_balance { + Ok(balance) => { + println!("Final balance: {} AI tokens", balance / Unit::ETHER.wei()); + } + Err(e) => { + println!("Failed to get final balance: {}", e); + } + } + + Ok(()) +} diff --git a/crates/admin-cli/src/commands/token.rs b/crates/admin-cli/src/commands/token.rs new file mode 100644 index 00000000..eac36425 --- /dev/null +++ b/crates/admin-cli/src/commands/token.rs @@ -0,0 +1,190 @@ +use alloy::primitives::utils::Unit; +use alloy::primitives::{Address, U256}; +use alloy::providers::Provider; +use clap::Subcommand; +use eyre::Result; +use shared::web3::contracts::core::builder::ContractBuilder; +use shared::web3::wallet::Wallet; +use std::str::FromStr; +use url::Url; + +use crate::config::Config; +use crate::secure_key::get_private_key; + +#[derive(Subcommand)] +pub enum TokenCommands { + /// Mint AI tokens to an address + Mint { + /// Address to mint tokens to + #[arg(short = 'a', long)] + address: String, + + /// Amount to mint (in ETH units) + #[arg(short = 'm', long, default_value = "30000")] + amount: u64, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, + /// Transfer ETH between accounts + TransferEth { + /// Address to transfer ETH to + #[arg(short = 'a', long)] + address: String, + + /// Amount to transfer (in ETH units, supports decimals) + #[arg(short = 'm', long)] + amount: String, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, + /// Set minimum stake amount + SetMinStake { + /// Minimum stake amount + #[arg(long)] + min_stake_amount: String, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, +} + +pub async fn handle_command(command: TokenCommands, config: &Config) -> Result<()> { + match command { + TokenCommands::Mint { + address, + amount, + key, + } => mint_tokens(address, amount, key, config).await, + TokenCommands::TransferEth { + address, + amount, + key, + } => transfer_eth(address, amount, key, config).await, + TokenCommands::SetMinStake { + min_stake_amount, + key, + } => set_min_stake(min_stake_amount, key, config).await, + } +} + +async fn mint_tokens( + address: String, + amount: u64, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("federator").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .build()?; + + let address = Address::from_str(&address)?; + let amount = U256::from(amount) * Unit::ETHER.wei(); + + println!( + "Minting {} AI tokens to address: {}", + amount / Unit::ETHER.wei(), + address + ); + + let tx = contracts.ai_token.mint(address, amount).await; + println!("Transaction: {:?}", tx); + + let balance = contracts + .ai_token + .balance_of(address) + .await + .map_err(|e| eyre::eyre!("Failed to get balance: {}", e))?; + println!("New balance: {} AI tokens", balance / Unit::ETHER.wei()); + + Ok(()) +} + +async fn transfer_eth( + address: String, + amount: String, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("federator").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + + let to_address = Address::from_str(&address)?; + let amount_f64: f64 = amount + .parse() + .map_err(|_| eyre::eyre!("Invalid amount format"))?; + let amount_wei = U256::from((amount_f64 * 1e18) as u128); + + println!("Transferring {} ETH to address: {}", amount_f64, to_address); + + let provider = wallet.provider(); + let tx_request = alloy::rpc::types::TransactionRequest::default() + .to(to_address) + .value(amount_wei) + .from(wallet.address()); + + let tx = provider.send_transaction(tx_request).await?; + println!("Transaction sent: {:?}", tx.tx_hash()); + + let receipt = tx.get_receipt().await?; + println!("Transaction confirmed in block: {:?}", receipt.block_number); + + Ok(()) +} + +async fn set_min_stake( + min_stake_amount: String, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("federator").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .build()?; + + let amount_f64: f64 = min_stake_amount + .parse() + .map_err(|_| eyre::eyre!("Invalid stake amount format"))?; + let amount_wei = U256::from((amount_f64 * 1e18) as u128); + + println!("Setting minimum stake amount to: {} tokens", amount_f64); + + let tx = contracts.prime_network.set_stake_minimum(amount_wei).await; + println!("Transaction: {:?}", tx); + + Ok(()) +} diff --git a/crates/admin-cli/src/commands/wallet.rs b/crates/admin-cli/src/commands/wallet.rs new file mode 100644 index 00000000..beb1a773 --- /dev/null +++ b/crates/admin-cli/src/commands/wallet.rs @@ -0,0 +1,138 @@ +use alloy::providers::Provider; +use clap::Subcommand; +use eyre::Result; +use rand::RngCore; +use shared::web3::wallet::Wallet; +use url::Url; + +use crate::config::Config; + +#[derive(Subcommand)] +pub enum WalletCommands { + /// Generate a new wallet + Generate { + /// Output format (json, env, pretty) + #[arg(short = 'f', long, default_value = "pretty")] + format: String, + + /// Save to file + #[arg(short = 'o', long)] + output: Option, + }, + /// Get wallet address from private key + Address { + /// Private key (32 bytes hex) + #[arg(short = 'k', long)] + private_key: String, + }, + /// Check wallet balance + Balance { + /// Private key or address + #[arg(short = 'k', long)] + key_or_address: String, + + /// RPC URL (optional, uses config default) + #[arg(short = 'r', long)] + rpc_url: Option, + }, +} + +pub async fn handle_command(command: WalletCommands, config: &Config) -> Result<()> { + match command { + WalletCommands::Generate { format, output } => generate_wallet(format, output).await, + WalletCommands::Address { private_key } => get_address(private_key).await, + WalletCommands::Balance { + key_or_address, + rpc_url, + } => check_balance(key_or_address, rpc_url, config).await, + } +} + +async fn generate_wallet(format: String, output: Option) -> Result<()> { + let mut rng = rand::rng(); + let mut private_key_bytes = [0u8; 32]; + rng.fill_bytes(&mut private_key_bytes); + + let private_key_hex = hex::encode(private_key_bytes); + + let dummy_url = + Url::parse("http://localhost:8545").map_err(|e| eyre::eyre!("URL parse error: {}", e))?; + let wallet = Wallet::new(&private_key_hex, dummy_url) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let address = wallet.address(); + + let output_content = match format.as_str() { + "json" => serde_json::json!({ + "private_key": private_key_hex, + "address": format!("{:?}", address) + }) + .to_string(), + "env" => { + format!("PRIVATE_KEY={}\nADDRESS={:?}", private_key_hex, address) + } + "pretty" => { + format!( + "Generated new wallet:\n Private Key: {}\n Address: {:?}", + private_key_hex, address + ) + } + _ => { + format!( + "Generated new wallet:\n Private Key: {}\n Address: {:?}", + private_key_hex, address + ) + } + }; + + if let Some(file_path) = output { + std::fs::write(&file_path, &output_content)?; + println!("Wallet saved to: {}", file_path); + } else { + println!("{}", output_content); + } + + Ok(()) +} + +async fn get_address(private_key: String) -> Result<()> { + let dummy_url = + Url::parse("http://localhost:8545").map_err(|e| eyre::eyre!("URL parse error: {}", e))?; + let wallet = Wallet::new(&private_key, dummy_url) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let address = wallet.address(); + + println!("Address: {:?}", address); + Ok(()) +} + +async fn check_balance( + key_or_address: String, + rpc_url: Option, + config: &Config, +) -> Result<()> { + let rpc_url = rpc_url + .or_else(|| config.get_rpc_url().ok()) + .unwrap_or_else(|| "http://localhost:8545".to_string()); + let url = Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?; + + let address = if key_or_address.len() == 64 + || key_or_address.starts_with("0x") && key_or_address.len() == 66 + { + let wallet = Wallet::new(&key_or_address, url.clone()) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + wallet.address() + } else { + key_or_address + .parse() + .map_err(|_| eyre::eyre!("Invalid address or private key format"))? + }; + + let provider = alloy::providers::ProviderBuilder::new().connect_http(url); + let balance = provider.get_balance(address).await?; + let balance_eth = balance.to::() as f64 / 1e18; + + println!("Address: {:?}", address); + println!("Balance: {} ETH", balance_eth); + + Ok(()) +} diff --git a/crates/admin-cli/src/commands/work.rs b/crates/admin-cli/src/commands/work.rs new file mode 100644 index 00000000..677c2dc9 --- /dev/null +++ b/crates/admin-cli/src/commands/work.rs @@ -0,0 +1,155 @@ +use alloy::primitives::{Address, U256}; +use clap::Subcommand; +use eyre::Result; +use shared::web3::contracts::core::builder::ContractBuilder; +use shared::web3::wallet::Wallet; +use std::str::FromStr; +use url::Url; + +use crate::config::Config; +use crate::secure_key::get_private_key; + +#[derive(Subcommand)] +pub enum WorkCommands { + /// Submit work to a compute pool + Submit { + /// Pool ID to submit work to + #[arg(short = 'p', long)] + pool_id: u64, + + /// Node address + #[arg(short = 'n', long)] + node: String, + + /// Work key identifier + #[arg(short = 'w', long)] + work_key: String, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, + /// Invalidate submitted work with penalty + Invalidate { + /// Pool ID containing the work + #[arg(short = 'p', long)] + pool_id: u64, + + /// Penalty amount + #[arg(long)] + penalty: String, + + /// Work key identifier + #[arg(short = 'w', long)] + work_key: String, + + /// Private key source (env:VAR_NAME, file:/path, or interactive) + #[arg(short = 'k', long)] + key: Option, + }, +} + +pub async fn handle_command(command: WorkCommands, config: &Config) -> Result<()> { + match command { + WorkCommands::Submit { + pool_id, + node, + work_key, + key, + } => submit_work(pool_id, node, work_key, key, config).await, + WorkCommands::Invalidate { + pool_id, + penalty, + work_key, + key, + } => invalidate_work(pool_id, penalty, work_key, key, config).await, + } +} + +async fn submit_work( + pool_id: u64, + node: String, + work_key: String, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = get_private_key(key.or_else(|| config.get_default_key("provider").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .build()?; + + let node_addr = Address::from_str(&node)?; + + println!("Submitting work:"); + println!(" Pool ID: {}", pool_id); + println!(" Node: {}", node); + println!(" Work key: {}", work_key); + + let work_data = work_key.as_bytes().to_vec(); + let work_units = U256::from(1u64); // Default to 1 work unit + + let call_builder = contracts + .compute_pool + .build_work_submission_call(U256::from(pool_id), node_addr, work_data, work_units) + .await + .map_err(|e| eyre::eyre!("Failed to build work submission call: {}", e))?; + + let tx = call_builder.send().await; + println!("Transaction: {:?}", tx); + + Ok(()) +} + +async fn invalidate_work( + pool_id: u64, + penalty: String, + work_key: String, + key: Option, + config: &Config, +) -> Result<()> { + let private_key = + get_private_key(key.or_else(|| config.get_default_key("validator").cloned()))?; + let rpc_url = config.get_rpc_url()?; + + let wallet = Wallet::new( + &private_key, + Url::parse(&rpc_url).map_err(|e| eyre::eyre!("URL parse error: {}", e))?, + ) + .map_err(|e| eyre::eyre!("Wallet creation error: {}", e))?; + let contracts = ContractBuilder::new(wallet.provider()) + .with_compute_registry() + .with_ai_token() + .with_prime_network() + .with_compute_pool() + .build()?; + + let penalty_f64: f64 = penalty + .parse() + .map_err(|_| eyre::eyre!("Invalid penalty amount format"))?; + let penalty_wei = U256::from((penalty_f64 * 1e18) as u128); + + println!("Invalidating work:"); + println!(" Pool ID: {}", pool_id); + println!(" Work key: {}", work_key); + println!(" Penalty: {} tokens", penalty_f64); + + let work_data = work_key.as_bytes().to_vec(); + + let tx = contracts + .prime_network + .invalidate_work(U256::from(pool_id), penalty_wei, work_data) + .await; + println!("Transaction: {:?}", tx); + + Ok(()) +} diff --git a/crates/admin-cli/src/config.rs b/crates/admin-cli/src/config.rs new file mode 100644 index 00000000..6c50d94e --- /dev/null +++ b/crates/admin-cli/src/config.rs @@ -0,0 +1,98 @@ +use eyre::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::path::Path; + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct Config { + pub rpc_url: Option, + pub default_keys: HashMap, + pub contract_addresses: HashMap, +} + +impl Config { + pub fn load(config_path: &Option, env_file: &str) -> Result { + dotenv::from_filename(env_file).ok(); + + let mut config = if let Some(path) = config_path { + Self::load_from_file(path)? + } else { + Self::default() + }; + + config.load_from_env()?; + Ok(config) + } + + pub fn load_from_file(path: &str) -> Result { + if !Path::new(path).exists() { + return Ok(Self::default()); + } + + let content = std::fs::read_to_string(path) + .with_context(|| format!("Failed to read config file: {}", path))?; + + let config: Config = toml::from_str(&content) + .with_context(|| format!("Failed to parse config file: {}", path))?; + + Ok(config) + } + + pub fn load_from_env(&mut self) -> Result<()> { + if let Ok(rpc_url) = std::env::var("RPC_URL") { + self.rpc_url = Some(rpc_url); + } + + let env_vars = [ + ("PRIVATE_KEY_FEDERATOR", "federator"), + ("PRIVATE_KEY_VALIDATOR", "validator"), + ("PRIVATE_KEY_PROVIDER", "provider"), + ("POOL_OWNER_PRIVATE_KEY", "pool_owner"), + ("PRIVATE_KEY_NODE", "node"), + ("PRIVATE_KEY_NODE_2", "node_2"), + ]; + + for (env_var, key_name) in env_vars { + if let Ok(key) = std::env::var(env_var) { + self.default_keys.insert(key_name.to_string(), key); + } + } + + let contract_vars = [ + ("WORK_VALIDATION_CONTRACT", "work_validation"), + ("FEDERATOR_ADDRESS", "federator_address"), + ("VALIDATOR_ADDRESS", "validator_address"), + ("PROVIDER_ADDRESS", "provider_address"), + ("POOL_OWNER_ADDRESS", "pool_owner_address"), + ("NODE_ADDRESS", "node_address"), + ]; + + for (env_var, contract_name) in contract_vars { + if let Ok(address) = std::env::var(env_var) { + self.contract_addresses + .insert(contract_name.to_string(), address); + } + } + + Ok(()) + } + + pub fn with_rpc_url(mut self, rpc_url: String) -> Self { + self.rpc_url = Some(rpc_url); + self + } + + pub fn get_rpc_url(&self) -> Result { + self.rpc_url.clone().ok_or_else(|| { + eyre::eyre!("RPC URL not configured. Set RPC_URL environment variable or use --rpc-url") + }) + } + + pub fn get_default_key(&self, role: &str) -> Option<&String> { + self.default_keys.get(role) + } + + pub fn get_contract_address(&self, name: &str) -> Option<&String> { + self.contract_addresses.get(name) + } +} diff --git a/crates/admin-cli/src/main.rs b/crates/admin-cli/src/main.rs new file mode 100644 index 00000000..9b24ac3e --- /dev/null +++ b/crates/admin-cli/src/main.rs @@ -0,0 +1,97 @@ +use clap::{Parser, Subcommand}; +use eyre::Result; + +mod commands; +mod config; +mod secure_key; + +use commands::*; +use config::Config; + +#[derive(Parser)] +#[command(name = "admin-cli")] +#[command(about = "Prime Intellect Network Administrative CLI")] +#[command(version)] +struct Cli { + #[command(subcommand)] + command: Commands, + + /// Configuration file path + #[arg(long, global = true)] + config: Option, + + /// RPC URL (overrides config) + #[arg(long, global = true)] + rpc_url: Option, + + /// Environment file path + #[arg(long, global = true, default_value = ".env")] + env_file: String, +} + +#[derive(Subcommand)] +enum Commands { + /// Token management operations + Token { + #[command(subcommand)] + command: TokenCommands, + }, + /// Domain management operations + Domain { + #[command(subcommand)] + command: DomainCommands, + }, + /// Pool management operations + Pool { + #[command(subcommand)] + command: PoolCommands, + }, + /// Provider management operations + Provider { + #[command(subcommand)] + command: ProviderCommands, + }, + /// Work management operations + Work { + #[command(subcommand)] + command: WorkCommands, + }, + /// Node management operations + Node { + #[command(subcommand)] + command: NodeCommands, + }, + /// Wallet operations + Wallet { + #[command(subcommand)] + command: WalletCommands, + }, + /// Testing utilities + Test { + #[command(subcommand)] + command: TestCommands, + }, +} + +#[tokio::main] +async fn main() -> Result<()> { + let cli = Cli::parse(); + + let config = Config::load(&cli.config, &cli.env_file)?; + let config = if let Some(rpc_url) = cli.rpc_url { + config.with_rpc_url(rpc_url) + } else { + config + }; + + match cli.command { + Commands::Token { command } => handle_token_command(command, &config).await, + Commands::Domain { command } => handle_domain_command(command, &config).await, + Commands::Pool { command } => handle_pool_command(command, &config).await, + Commands::Provider { command } => handle_provider_command(command, &config).await, + Commands::Work { command } => handle_work_command(command, &config).await, + Commands::Node { command } => handle_node_command(command, &config).await, + Commands::Wallet { command } => handle_wallet_command(command, &config).await, + Commands::Test { command } => handle_test_command(command, &config).await, + } +} diff --git a/crates/admin-cli/src/secure_key.rs b/crates/admin-cli/src/secure_key.rs new file mode 100644 index 00000000..d71fc93f --- /dev/null +++ b/crates/admin-cli/src/secure_key.rs @@ -0,0 +1,50 @@ +use eyre::{Context, Result}; +use std::env; +use std::io::{self, Write}; + +pub enum KeySource { + Environment(String), + File(String), + Interactive, +} + +impl KeySource { + pub fn from_arg(key_arg: Option) -> Self { + match key_arg { + Some(key) if key.starts_with("env:") => { + Self::Environment(key.strip_prefix("env:").unwrap().to_string()) + } + Some(key) if key.starts_with("file:") => { + Self::File(key.strip_prefix("file:").unwrap().to_string()) + } + Some(_) => { + eprintln!( + "Warning: Direct private key arguments are deprecated for security reasons." + ); + eprintln!("Use 'env:VAR_NAME' or 'file:/path/to/key' instead."); + Self::Interactive + } + None => Self::Interactive, + } + } + + pub fn resolve(&self) -> Result { + match self { + Self::Environment(var_name) => env::var(var_name) + .with_context(|| format!("Environment variable {} not found", var_name)), + Self::File(path) => std::fs::read_to_string(path) + .with_context(|| format!("Failed to read private key from file: {}", path)) + .map(|s| s.trim().to_string()), + Self::Interactive => { + print!("Enter private key (hidden): "); + io::stdout().flush().unwrap(); + rpassword::read_password().context("Failed to read private key") + } + } + } +} + +pub fn get_private_key(key_arg: Option) -> Result { + let key_source = KeySource::from_arg(key_arg); + key_source.resolve() +} diff --git a/crates/dev-utils/Cargo.toml b/crates/dev-utils/Cargo.toml deleted file mode 100644 index e6cd5552..00000000 --- a/crates/dev-utils/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "dev-utils" -version = "0.1.0" -edition = "2021" - -[dependencies] -shared = { workspace = true } -tokio = { workspace = true } -eyre = "0.6" -clap = { workspace = true } -url = { workspace = true } -alloy = { workspace = true } -tokio-util = { workspace = true } -hex = "0.4.3" -rand = "0.9.1" diff --git a/crates/dev-utils/examples/compute_pool.rs b/crates/dev-utils/examples/compute_pool.rs deleted file mode 100644 index 2569980c..00000000 --- a/crates/dev-utils/examples/compute_pool.rs +++ /dev/null @@ -1,98 +0,0 @@ -use alloy::primitives::Address; -use alloy::primitives::U256; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use shared::web3::contracts::implementations::rewards_distributor_contract::RewardsDistributor; -use shared::web3::wallet::Wallet; -use std::str::FromStr; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Domain ID to create the compute pool for - #[arg(short = 'd', long)] - domain_id: U256, - - /// Compute manager key address - #[arg(short = 'm', long)] - compute_manager_key: String, - - /// Pool name - #[arg(short = 'n', long)] - pool_name: String, - - /// Pool data URI - #[arg(short = 'u', long)] - pool_data_uri: String, - - /// Private key for transaction signing - /// The address of this key will be the creator of the compute pool - /// They are the only one who can actually set the pool to active - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let wallet = Wallet::new(&args.key, Url::parse(&args.rpc_url)?).unwrap(); - - // Build all contracts - let contracts = ContractBuilder::new(wallet.provider()) - .with_compute_registry() - .with_ai_token() - .with_prime_network() - .with_compute_pool() - .build() - .unwrap(); - - let domain_id = args.domain_id; - let compute_manager_key = Address::from_str(&args.compute_manager_key).unwrap(); - let pool_name = args.pool_name.clone(); - let pool_data_uri = args.pool_data_uri.clone(); - - let compute_limit = U256::from(0); - - let tx = contracts - .compute_pool - .create_compute_pool( - domain_id, - compute_manager_key, - pool_name, - pool_data_uri, - compute_limit, - ) - .await; - println!("Transaction: {:?}", tx); - let rewards_distributor_address = contracts - .compute_pool - .get_reward_distributor_address(U256::from(0)) - .await - .unwrap(); - - println!( - "Rewards distributor address: {:?}", - rewards_distributor_address - ); - let rewards_distributor = RewardsDistributor::new( - rewards_distributor_address, - wallet.provider(), - "rewards_distributor.json", - ); - let rate = U256::from(10000000000000000u64); - let tx = rewards_distributor.set_reward_rate(rate).await; - println!("Setting reward rate: {:?}", tx); - - let reward_rate = rewards_distributor.get_reward_rate().await.unwrap(); - println!( - "Reward rate: {}", - reward_rate.to_string().parse::().unwrap_or(0.0) / 10f64.powf(18.0) - ); - - Ok(()) -} diff --git a/crates/dev-utils/examples/create_domain.rs b/crates/dev-utils/examples/create_domain.rs deleted file mode 100644 index 4365c764..00000000 --- a/crates/dev-utils/examples/create_domain.rs +++ /dev/null @@ -1,64 +0,0 @@ -use alloy::primitives::Address; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use shared::web3::wallet::Wallet; -use std::str::FromStr; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Domain name to create - #[arg(short = 'd', long)] - domain_name: String, - - /// Validation logic address - #[arg( - short = 'v', - long, - default_value = "0x0000000000000000000000000000000000000000" - )] - validation_logic: String, - - /// Domain URI - #[arg(short = 'u', long)] - domain_uri: String, - - /// Private key for transaction signing - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let wallet = Wallet::new(&args.key, Url::parse(&args.rpc_url)?).unwrap(); - - // Build all contracts - let contracts = ContractBuilder::new(wallet.provider()) - .with_compute_registry() - .with_ai_token() - .with_prime_network() - .with_compute_pool() - .with_domain_registry() - .build() - .unwrap(); - - let domain_name = args.domain_name.clone(); - - let validation_logic = Address::from_str(&args.validation_logic).unwrap(); - let domain_uri = args.domain_uri.clone(); - - let tx = contracts - .prime_network - .create_domain(domain_name, validation_logic, domain_uri) - .await; - println!("Creating domain: {}", args.domain_name); - println!("Validation logic: {}", args.validation_logic); - println!("Transaction: {:?}", tx); - Ok(()) -} diff --git a/crates/dev-utils/examples/eject_node.rs b/crates/dev-utils/examples/eject_node.rs deleted file mode 100644 index e2ed03a3..00000000 --- a/crates/dev-utils/examples/eject_node.rs +++ /dev/null @@ -1,71 +0,0 @@ -use alloy::primitives::Address; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use shared::web3::wallet::Wallet; -use std::str::FromStr; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Private key for transaction signing - /// The address of this key must be the pool creator or manager - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, - - /// Pool ID - #[arg(short = 'p', long)] - pool_id: u32, - - /// Provider address - #[arg(short = 'a', long)] - provider_address: String, - - /// Node address to eject - #[arg(short = 'n', long)] - node: String, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let wallet = Wallet::new(&args.key, Url::parse(&args.rpc_url)?).unwrap(); - - // Build all contracts - let contracts = ContractBuilder::new(wallet.provider()) - .with_compute_registry() - .with_ai_token() - .with_prime_network() - .with_compute_pool() - .build() - .unwrap(); - - let node_address = Address::from_str(&args.node).expect("Invalid node address"); - let provider_address = - Address::from_str(&args.provider_address).expect("Invalid provider address"); - - let node_info = contracts - .compute_registry - .get_node(provider_address, node_address) - .await; - println!("Node info: {:?}", node_info); - - let tx = contracts - .compute_pool - .eject_node(args.pool_id, node_address) - .await; - println!("Ejected node {} from pool {}", args.node, args.pool_id); - println!("Transaction: {:?}", tx); - - let node_info = contracts - .compute_registry - .get_node(provider_address, node_address) - .await; - println!("Post ejection node info: {:?}", node_info); - - Ok(()) -} diff --git a/crates/dev-utils/examples/get_node_info.rs b/crates/dev-utils/examples/get_node_info.rs deleted file mode 100644 index fec5f526..00000000 --- a/crates/dev-utils/examples/get_node_info.rs +++ /dev/null @@ -1,63 +0,0 @@ -use alloy::primitives::Address; -use alloy::providers::RootProvider; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use std::str::FromStr; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Provider address - #[arg(short = 'p', long)] - provider_address: String, - - /// Node address - #[arg(short = 'n', long)] - node_address: String, - - /// Private key for transaction signing - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let provider = RootProvider::new_http(Url::parse(&args.rpc_url).unwrap()); - - // Build the contract - let contracts = ContractBuilder::new(provider) - .with_compute_registry() - .with_ai_token() // Initialize AI Token - .with_prime_network() // Initialize Prime Network - .with_compute_pool() - .build() - .unwrap(); - - let provider_address = Address::from_str(&args.provider_address).unwrap(); - let node_address = Address::from_str(&args.node_address).unwrap(); - - // Get node info - let (active, validated) = contracts - .compute_registry - .get_node(provider_address, node_address) - .await - .unwrap(); - - let is_node_in_pool = contracts - .compute_pool - .is_node_in_pool(0, node_address) - .await - .unwrap(); - - println!( - "Node Active: {}, Validated: {}, In Pool: {}", - active, validated, is_node_in_pool - ); - Ok(()) -} diff --git a/crates/dev-utils/examples/invalidate_work.rs b/crates/dev-utils/examples/invalidate_work.rs deleted file mode 100644 index 78154b07..00000000 --- a/crates/dev-utils/examples/invalidate_work.rs +++ /dev/null @@ -1,71 +0,0 @@ -use alloy::primitives::U256; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use shared::web3::wallet::Wallet; -use std::str::FromStr; -use url::Url; - -#[derive(Parser)] -struct Args { - #[arg(long)] - pool_id: u64, - #[arg(long)] - penalty: String, - #[arg(long)] - work_key: String, - #[arg(long)] - key: String, - #[arg(long)] - rpc_url: String, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - - // Parse RPC URL with proper error handling - let rpc_url = Url::parse(&args.rpc_url).map_err(|e| eyre::eyre!("Invalid RPC URL: {}", e))?; - - // Create wallet with error conversion - let wallet = Wallet::new(&args.key, rpc_url) - .map_err(|e| eyre::eyre!("Failed to create wallet: {}", e))?; - - // Build the PrimeNetwork contract - let contracts = ContractBuilder::new(wallet.provider()) - .with_compute_registry() - .with_ai_token() - .with_prime_network() - .with_compute_pool() - .build() - .map_err(|e| eyre::eyre!("Failed to build contracts: {}", e))?; - - // Convert arguments to appropriate types - let pool_id = U256::from(args.pool_id); - let penalty = - U256::from_str(&args.penalty).map_err(|e| eyre::eyre!("Invalid penalty value: {}", e))?; - let work_key = hex::decode(args.work_key.trim_start_matches("0x")) - .map_err(|e| eyre::eyre!("Invalid work key hex: {}", e))?; - - // Validate work_key length - if work_key.len() != 32 { - return Err(eyre::eyre!("Work key must be 32 bytes")); - } - - let data = work_key; // Use the decoded work_key as data - - // Call invalidate_work on the PrimeNetwork contract - let tx = contracts - .prime_network - .invalidate_work(pool_id, penalty, data) - .await - .map_err(|e| eyre::eyre!("Failed to invalidate work: {}", e))?; - - println!( - "Invalidated work in pool {} with penalty {}", - args.pool_id, args.penalty - ); - println!("Transaction hash: {:?}", tx); - - Ok(()) -} diff --git a/crates/dev-utils/examples/mint_ai_token.rs b/crates/dev-utils/examples/mint_ai_token.rs deleted file mode 100644 index 5e572b40..00000000 --- a/crates/dev-utils/examples/mint_ai_token.rs +++ /dev/null @@ -1,53 +0,0 @@ -use alloy::primitives::utils::Unit; -use alloy::primitives::Address; -use alloy::primitives::U256; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use shared::web3::wallet::Wallet; -use std::str::FromStr; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Address to mint tokens to - #[arg(short = 'a', long)] - address: String, - - /// Private key for transaction signing - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, - - /// Amount to mint - #[arg(short = 'm', long, default_value = "30000")] - amount: u64, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let wallet = Wallet::new(&args.key, Url::parse(&args.rpc_url)?).unwrap(); - - // Unfortunately have to build all contracts atm - let contracts = ContractBuilder::new(wallet.provider()) - .with_compute_registry() - .with_ai_token() - .with_prime_network() - .with_compute_pool() - .build() - .unwrap(); - - let address = Address::from_str(&args.address).unwrap(); - let amount = U256::from(args.amount) * Unit::ETHER.wei(); - let tx = contracts.ai_token.mint(address, amount).await; - println!("Minting to address: {}", args.address); - println!("Transaction: {:?}", tx); - - let balance = contracts.ai_token.balance_of(address).await; - println!("Balance: {:?}", balance); - Ok(()) -} diff --git a/crates/dev-utils/examples/set_min_stake_amount.rs b/crates/dev-utils/examples/set_min_stake_amount.rs deleted file mode 100644 index 82644e61..00000000 --- a/crates/dev-utils/examples/set_min_stake_amount.rs +++ /dev/null @@ -1,48 +0,0 @@ -use alloy::primitives::utils::Unit; -use alloy::primitives::U256; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use shared::web3::wallet::Wallet; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Minimum stake amount to set - #[arg(short = 'm', long)] - min_stake_amount: f64, - - /// Private key for transaction signing - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let wallet = Wallet::new(&args.key, Url::parse(&args.rpc_url)?).unwrap(); - - // Build all contracts - let contracts = ContractBuilder::new(wallet.provider()) - .with_compute_registry() - .with_ai_token() - .with_prime_network() - .with_compute_pool() - .build() - .unwrap(); - - let min_stake_amount = U256::from(args.min_stake_amount) * Unit::ETHER.wei(); - println!("Min stake amount: {}", min_stake_amount); - - let tx = contracts - .prime_network - .set_stake_minimum(min_stake_amount) - .await; - println!("Transaction: {:?}", tx); - - Ok(()) -} diff --git a/crates/dev-utils/examples/start_compute_pool.rs b/crates/dev-utils/examples/start_compute_pool.rs deleted file mode 100644 index b11e2b2c..00000000 --- a/crates/dev-utils/examples/start_compute_pool.rs +++ /dev/null @@ -1,46 +0,0 @@ -use alloy::primitives::U256; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use shared::web3::wallet::Wallet; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Private key for transaction signing - /// The address of this key will be the creator of the compute pool - /// They are the only one who can actually set the pool to active - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, - - /// Pool ID - #[arg(short = 'p', long)] - pool_id: U256, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let wallet = Wallet::new(&args.key, Url::parse(&args.rpc_url)?).unwrap(); - - // Build all contracts - let contracts = ContractBuilder::new(wallet.provider()) - .with_compute_registry() - .with_ai_token() - .with_prime_network() - .with_compute_pool() - .build() - .unwrap(); - - let tx = contracts - .compute_pool - .start_compute_pool(U256::from(args.pool_id)) - .await; - println!("Started compute pool with id: {}", args.pool_id); - println!("Transaction: {:?}", tx); - Ok(()) -} diff --git a/crates/dev-utils/examples/submit_work.rs b/crates/dev-utils/examples/submit_work.rs deleted file mode 100644 index 0d00cd52..00000000 --- a/crates/dev-utils/examples/submit_work.rs +++ /dev/null @@ -1,71 +0,0 @@ -use alloy::primitives::{Address, U256}; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use shared::web3::wallet::Wallet; -use std::str::FromStr; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Pool ID - #[arg(short = 'p', long)] - pool_id: u32, - - /// Node address - #[arg(short = 'n', long)] - node: String, - - /// Work key (32-byte hex string) - #[arg(short = 'w', long)] - work_key: String, - - /// Private key for transaction signing (provider's private key) - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let wallet = Wallet::new(&args.key, Url::parse(&args.rpc_url)?).unwrap(); - - // Build all contracts - let contracts = ContractBuilder::new(wallet.provider()) - .with_compute_registry() - .with_ai_token() - .with_prime_network() - .with_compute_pool() - .build() - .unwrap(); - - let pool_id = U256::from(args.pool_id); - let node = Address::from_str(&args.node).expect("Invalid node address"); - let work_key = hex::decode(&args.work_key).expect("Invalid work key hex"); - - if work_key.len() != 32 { - panic!("Work key must be 32 bytes"); - } - - let call = contracts - .compute_pool - .build_work_submission_call(pool_id, node, work_key, U256::from(179949060096000.0)) - .await - .map_err(|e| eyre::eyre!("Failed to build work submission call: {}", e))?; - - let tx = call - .send() - .await - .map_err(|e| eyre::eyre!("Failed to submit work: {}", e))?; - println!( - "Submitted work for node {} in pool {}", - args.node, args.pool_id - ); - println!("Transaction hash: {:?}", tx); - - Ok(()) -} diff --git a/crates/dev-utils/examples/test_concurrent_calls.rs b/crates/dev-utils/examples/test_concurrent_calls.rs deleted file mode 100644 index 47f7bbea..00000000 --- a/crates/dev-utils/examples/test_concurrent_calls.rs +++ /dev/null @@ -1,103 +0,0 @@ -use alloy::eips::BlockId; -use alloy::eips::BlockNumberOrTag; -use alloy::primitives::utils::Unit; -use alloy::primitives::Address; -use alloy::primitives::U256; -use alloy::providers::Provider; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use shared::web3::contracts::helpers::utils::retry_call; -use shared::web3::wallet::Wallet; -use std::str::FromStr; -use std::sync::Arc; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Address to mint tokens to - #[arg(short = 'a', long)] - address: String, - - /// Private key for transaction signing - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, - - /// Amount to mint - #[arg(short = 'm', long, default_value = "30000")] - amount: u64, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let wallet = Arc::new(Wallet::new(&args.key, Url::parse(&args.rpc_url)?).unwrap()); - - let price = wallet.provider.get_gas_price().await?; - println!("Gas price: {:?}", price); - - let current_nonce = wallet - .provider - .get_transaction_count(wallet.address()) - .await?; - let pending_nonce = wallet - .provider - .get_transaction_count(wallet.address()) - .block_id(BlockId::Number(BlockNumberOrTag::Pending)) - .await?; - - println!("Pending nonce: {:?}", pending_nonce); - println!("Current nonce: {:?}", current_nonce); - - // Unfortunately have to build all contracts atm - let contracts = Arc::new( - ContractBuilder::new(wallet.provider()) - .with_compute_registry() - .with_ai_token() - .with_prime_network() - .with_compute_pool() - .build() - .unwrap(), - ); - - let address = Address::from_str(&args.address).unwrap(); - let amount = U256::from(args.amount) * Unit::ETHER.wei(); - let random = (rand::random::() % 10) + 1; - println!("Random: {:?}", random); - - let contracts_one = contracts.clone(); - let wallet_one = wallet.clone(); - tokio::spawn(async move { - let mint_call = contracts_one - .ai_token - .build_mint_call(address, amount) - .unwrap(); - - let tx = retry_call(mint_call, 5, wallet_one.provider(), None) - .await - .unwrap(); - println!("Transaction hash I: {:?}", tx); - }); - - let contracts_two = contracts.clone(); - let wallet_two = wallet.clone(); - tokio::spawn(async move { - let mint_call_two = contracts_two - .ai_token - .build_mint_call(address, amount) - .unwrap(); - let tx = retry_call(mint_call_two, 5, wallet_two.provider(), None) - .await - .unwrap(); - println!("Transaction hash II: {:?}", tx); - }); - - let balance = contracts.ai_token.balance_of(address).await.unwrap(); - println!("Balance: {:?}", balance); - tokio::time::sleep(tokio::time::Duration::from_secs(40)).await; - Ok(()) -} diff --git a/crates/dev-utils/examples/transfer_eth.rs b/crates/dev-utils/examples/transfer_eth.rs deleted file mode 100644 index 1dd68f60..00000000 --- a/crates/dev-utils/examples/transfer_eth.rs +++ /dev/null @@ -1,62 +0,0 @@ -use alloy::{ - network::TransactionBuilder, primitives::Address, primitives::U256, providers::Provider, - rpc::types::TransactionRequest, -}; -use clap::Parser; -use eyre::Result; -use shared::web3::wallet::Wallet; -use std::str::FromStr; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Address to send ETH to - #[arg(short = 'a', long)] - address: String, - - /// Private key for transaction signing - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, - - /// Amount to send - #[arg(short = 'm', long, default_value = "1000")] - amount: u64, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let wallet = Wallet::new(&args.key, Url::parse(&args.rpc_url)?).unwrap(); - - let balance_before = wallet.provider.get_balance(wallet.signer.address()).await?; - // Start of Selection - let from = wallet.signer.address(); - let to = Address::from_str(&args.address).unwrap(); - let amount = U256::from(args.amount); - let tx = TransactionRequest::default() - .with_from(from) - .with_to(to) - .with_value(amount); - - // Send the transaction and listen for the transaction to be included. - let tx_hash = wallet.provider.send_transaction(tx).await?.watch().await?; - - println!("Sent transaction: {tx_hash}"); - - println!( - "Sender's ETH balance before transaction: {} ETH", - balance_before / U256::from(10u64.pow(18)) - ); - - let balance_after = wallet.provider.get_balance(to).await?; - println!( - "Receiver's ETH balance after transaction: {} ETH", - alloy::primitives::utils::format_ether(balance_after) - ); - - Ok(()) -} diff --git a/crates/dev-utils/examples/whitelist_provider.rs b/crates/dev-utils/examples/whitelist_provider.rs deleted file mode 100644 index 620d6d38..00000000 --- a/crates/dev-utils/examples/whitelist_provider.rs +++ /dev/null @@ -1,46 +0,0 @@ -use alloy::primitives::Address; -use clap::Parser; -use eyre::Result; -use shared::web3::contracts::core::builder::ContractBuilder; -use shared::web3::wallet::Wallet; -use url::Url; - -#[derive(Parser)] -struct Args { - /// Provider address to whitelist - #[arg(short = 'a', long)] - provider_address: String, - - /// Private key for transaction signing - #[arg(short = 'k', long)] - key: String, - - /// RPC URL - #[arg(short = 'r', long)] - rpc_url: String, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Args::parse(); - let wallet = Wallet::new(&args.key, Url::parse(&args.rpc_url)?).unwrap(); - - // Build all contracts - let contracts = ContractBuilder::new(wallet.provider()) - .with_compute_registry() - .with_ai_token() - .with_prime_network() - .with_compute_pool() - .build() - .unwrap(); - - let provider_address: Address = args.provider_address.parse()?; - - let _ = contracts - .prime_network - .whitelist_provider(provider_address) - .await; - println!("Whitelisting provider: {}", args.provider_address); - - Ok(()) -}