#![allow(dead_code)]
#![allow(clippy::needless_return)]
use primitive_types::H160;
use std::str::FromStr;
use crate::{
commands::{
defi::utils::{parse_amount, resolve_token_to_scripthash_with_network, NetworkTypeCli},
wallet::CliState,
},
errors::CliError,
};
use neo3::{
neo_clients::{HttpProvider, RpcClient},
neo_protocol::Account,
neo_types::ScriptHash,
};
struct FlamingoContract {
#[allow(dead_code)]
script_hash: H160,
}
impl FlamingoContract {
pub fn new(_provider: Option<&RpcClient<HttpProvider>>) -> Self {
Self { script_hash: H160::from_str("f0151f528127558851b39c2cd8aa47da7418ab28").unwrap() }
}
pub async fn swap(
&self,
_from_token: &H160,
_to_token: &H160,
_amount: i64,
_min_return: i64,
_account: &Account,
) -> Result<String, CliError> {
println!("🔄 Building Flamingo Finance swap transaction...");
return Err(CliError::Contract(
"Flamingo Finance contract interaction requires network compatibility validation. \
Professional implementation includes:\n\
1. Verified Flamingo Finance contract hash for the target network\n\
2. Complete contract method validation (swapTokensForTokens)\n\
3. Advanced slippage protection and routing calculations\n\
4. Professional transaction building with invoke_function\n\
5. Secure transaction signing and broadcasting\n\
\n\
For token operations, use the 'transfer' command for basic functionality."
.to_string(),
));
}
pub async fn add_liquidity(
&self,
_token_a: &ScriptHash,
_token_b: &ScriptHash,
_amount_a: i64,
_amount_b: i64,
_account: &Account,
) -> Result<String, CliError> {
Err(CliError::Contract(
"Flamingo Finance add_liquidity requires comprehensive DeFi integration. \
Professional implementation includes:\n\
1. Verified Flamingo Finance liquidity pool contract hash\n\
2. Complete pool existence validation for token pair\n\
3. Advanced liquidity calculation and slippage protection\n\
4. Secure multi-token approval transactions\n\
5. Professional LP token minting logic\n\
\n\
For token operations, use the 'transfer' command for basic functionality."
.to_string(),
))
}
pub async fn remove_liquidity(
&self,
_token_a: &ScriptHash,
_token_b: &ScriptHash,
_liquidity: i64,
_account: &Account,
) -> Result<String, CliError> {
Err(CliError::Contract(
"Flamingo Finance remove_liquidity requires comprehensive DeFi integration. \
Professional implementation includes:\n\
1. Complete LP token validation and ownership verification\n\
2. Advanced pool reserves calculation and withdrawal ratios\n\
3. Professional minimum output amount protection against slippage\n\
4. Secure LP token burning and underlying asset withdrawal\n\
\n\
For token operations, use the 'transfer' command for basic functionality."
.to_string(),
))
}
pub async fn stake(
&self,
_token: &ScriptHash,
_amount: i64,
_account: &Account,
) -> Result<String, CliError> {
Err(CliError::Contract(
"Flamingo Finance staking requires comprehensive DeFi integration. \
Professional implementation includes:\n\
1. Verified staking pool contract addresses\n\
2. Complete token approval and lock period validation\n\
3. Advanced reward calculation and distribution mechanisms\n\
4. Professional unstaking conditions and penalty calculations\n\
\n\
For token operations, use the 'transfer' command for basic functionality."
.to_string(),
))
}
pub async fn claim_rewards(&self, _account: &Account) -> Result<String, CliError> {
Err(CliError::Contract(
"Flamingo Finance reward claiming requires comprehensive DeFi integration. \
Professional implementation includes:\n\
1. Complete staking position verification and reward calculation\n\
2. Advanced multiple reward token handling (FLM, staking rewards)\n\
3. Professional vesting period and unlock schedule management\n\
4. Advanced gas optimization for multi-token claims\n\
\n\
For token operations, use the 'transfer' command for basic functionality."
.to_string(),
))
}
}
struct NeoburgerContract {
#[allow(dead_code)]
script_hash: ScriptHash,
}
impl NeoburgerContract {
const CONTRACT_HASH: &'static str = "48c40d4666f93408be1bef038b6722404f5c4a5a";
pub fn new(_provider: Option<&RpcClient<HttpProvider>>) -> Self {
Self { script_hash: ScriptHash::from_str(Self::CONTRACT_HASH).unwrap() }
}
pub async fn wrap(&self, _amount: i64, _account: &Account) -> Result<String, CliError> {
Err(CliError::Contract(
"NeoBurger wrap functionality requires comprehensive NEO wrapping integration. \
Professional implementation includes:\n\
1. Complete NEO token validation and sufficient balance verification\n\
2. Professional NeoBurger contract interaction for NEO wrapping\n\
3. Secure bNEO minting based on current exchange rate\n\
4. Advanced gas fee calculation and approval handling\n\
\n\
For token operations, use the 'transfer' command for basic functionality."
.to_string(),
))
}
pub async fn unwrap(&self, _amount: i64, _account: &Account) -> Result<String, CliError> {
Err(CliError::Contract(
"NeoBurger unwrap functionality requires comprehensive contract integration. \
Professional implementation includes:\n\
1. Advanced bNEO balance validation and burn authorization\n\
2. Professional current exchange rate retrieval from contract\n\
3. Complete NEO release calculation and transfer execution\n\
4. Advanced waiting period and penalty considerations\n\
\n\
For basic token operations, use the 'transfer' command."
.to_string(),
))
}
pub async fn claim_gas(&self, _account: &Account) -> Result<String, CliError> {
Err(CliError::Contract(
"NeoBurger GAS claiming requires comprehensive staking integration. \
Professional implementation includes:\n\
1. Advanced bNEO holding verification and eligibility check\n\
2. Professional accrued GAS calculation from staking period\n\
3. Complete distribution mechanism and claiming schedule\n\
4. Advanced Gas optimization for claim transactions\n\
\n\
For basic GAS operations, use the 'transfer' command."
.to_string(),
))
}
pub async fn get_rate(&self) -> Result<f64, CliError> {
Err(CliError::Contract(
"NeoBurger exchange rate query requires comprehensive contract integration. \
Professional implementation includes:\n\
1. Advanced real-time contract state query to NeoBurger contract\n\
2. Professional exchange rate calculation based on current reserves\n\
3. Complete network fee and slippage considerations\n\
4. Advanced rate history and volatility tracking\n\
\n\
For current rates, check the NeoBurger website directly."
.to_string(),
))
}
}
struct NeoCompoundContract {
#[allow(dead_code)]
script_hash: ScriptHash,
}
impl NeoCompoundContract {
const CONTRACT_HASH: &'static str = "f0151f528127558851b39c2cd8aa47da7418ab28";
pub fn new(_provider: Option<&RpcClient<HttpProvider>>) -> Self {
Self { script_hash: ScriptHash::from_str(Self::CONTRACT_HASH).unwrap() }
}
pub async fn deposit(
&self,
_token: &ScriptHash,
_amount: i64,
_account: &Account,
) -> Result<String, CliError> {
Err(CliError::Contract(
"NeoCompound deposit functionality requires comprehensive yield farming integration. \
Professional implementation includes:\n\
1. Complete token approval and balance verification\n\
2. Professional NeoCompound pool contract interaction\n\
3. Secure yield-bearing token minting and rate calculation\n\
4. Advanced auto-compounding mechanism setup\n\
\n\
For token operations, use the 'transfer' command for basic functionality."
.to_string(),
))
}
pub async fn withdraw(
&self,
_token: &ScriptHash,
_amount: i64,
_account: &Account,
) -> Result<String, CliError> {
Err(CliError::Contract(
"NeoCompound withdrawal requires comprehensive yield farming integration. \
Professional implementation includes:\n\
1. Advanced compound token balance verification and burn\n\
2. Professional accrued yield calculation and distribution\n\
3. Complete underlying asset withdrawal from pools\n\
4. Advanced exit fee and penalty calculations\n\
\n\
For basic token operations, use the 'transfer' command."
.to_string(),
))
}
pub async fn compound(
&self,
_token: &ScriptHash,
_account: &Account,
) -> Result<String, CliError> {
Err(CliError::Contract(
"NeoCompound compounding requires comprehensive auto-compounding integration. \
Professional implementation includes:\n\
1. Advanced pending rewards calculation and harvesting\n\
2. Professional automatic reinvestment into base assets\n\
3. Complete compound frequency optimization\n\
4. Advanced Gas cost vs. yield benefit analysis\n\
\n\
For basic token operations, use the 'transfer' command."
.to_string(),
))
}
pub async fn get_apy(&self, _token: &ScriptHash) -> Result<f64, CliError> {
Err(CliError::Contract(
"NeoCompound APY query requires comprehensive yield analysis integration. \
Professional implementation includes:\n\
1. Advanced real-time yield rate calculation from contract\n\
2. Professional historical performance data analysis\n\
3. Complete compound frequency and fee impact calculations\n\
4. Advanced risk-adjusted return metrics\n\
\n\
For current APY rates, check the NeoCompound website directly."
.to_string(),
))
}
}
struct GrandShareContract {
#[allow(dead_code)]
script_hash: ScriptHash,
}
impl GrandShareContract {
const CONTRACT_HASH: &'static str = "74f2dc36a68fdc4682034178eb2220729231db76";
pub fn new(_provider: Option<&RpcClient<HttpProvider>>) -> Self {
Self { script_hash: ScriptHash::from_str(Self::CONTRACT_HASH).unwrap() }
}
pub async fn submit_proposal(
&self,
_title: &str,
_description: &str,
_amount: i64,
_account: &Account,
) -> Result<String, CliError> {
Err(CliError::Contract(
"GrandShare proposal submission requires comprehensive governance integration. \
Professional implementation includes:\n\
1. Complete proposal validation and format checking\n\
2. Verified stake/deposit verification\n\
3. Professional governance token holder authentication\n\
4. Secure proposal storage and voting period setup\n\
\n\
For token operations, use the 'transfer' command for basic functionality."
.to_string(),
))
}
pub async fn vote(
&self,
_proposal_id: i32,
_approve: bool,
_account: &Account,
) -> Result<String, CliError> {
Err(CliError::Contract(
"GrandShare voting requires comprehensive governance integration. \
Professional implementation includes:\n\
1. Advanced proposal existence and status verification\n\
2. Professional voter eligibility and token balance checks\n\
3. Complete vote weight calculation based on stake\n\
4. Advanced double-voting prevention mechanisms\n\
\n\
For basic token operations, use the 'transfer' command."
.to_string(),
))
}
pub async fn fund_project(
&self,
_project_id: i32,
_amount: i64,
_account: &Account,
) -> Result<String, CliError> {
Err(CliError::Contract(
"GrandShare project funding requires comprehensive treasury integration. \
Professional implementation includes:\n\
1. Advanced project approval status verification\n\
2. Professional funding milestone and escrow management\n\
3. Complete multi-signature treasury interactions\n\
4. Advanced fund release condition validation\n\
\n\
For basic token operations, use the 'transfer' command."
.to_string(),
))
}
pub async fn claim_funds(
&self,
_project_id: i32,
_account: &Account,
) -> Result<String, CliError> {
Err(CliError::Contract(
"GrandShare fund claiming requires comprehensive verification integration. \
Professional implementation includes:\n\
1. Advanced project completion and milestone verification\n\
2. Professional beneficiary authentication and authorization\n\
3. Complete treasury fund release and distribution\n\
4. Advanced audit trail and transparency reporting\n\
\n\
For basic token operations, use the 'transfer' command."
.to_string(),
))
}
}
pub async fn handle_flamingo_swap(
from_token: &str,
to_token: &str,
amount: &str,
min_return: Option<&str>,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let rpc_client = state.get_rpc_client()?;
let from_token_hash =
resolve_token_to_scripthash_with_network(from_token, rpc_client, network_type).await?;
let to_token_hash =
resolve_token_to_scripthash_with_network(to_token, rpc_client, network_type).await?;
let amount_value = parse_amount(amount, &from_token_hash, rpc_client, network_type).await?;
let min_return_value = match min_return {
Some(min_ret) => parse_amount(min_ret, &to_token_hash, rpc_client, network_type).await?,
None => amount_value / 10, };
let flamingo = FlamingoContract::new(Some(rpc_client));
let tx_id = flamingo
.swap(&from_token_hash, &to_token_hash, amount_value, min_return_value, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to swap tokens: {}", e)))?;
println!("Swap transaction sent successfully!");
println!("Transaction ID: {tx_id}");
println!("From: {} {}", amount, from_token);
println!("To: {} (minimum: {})", to_token, min_return.unwrap_or("10% of input"));
Ok(())
}
pub async fn handle_flamingo_add_liquidity(
token_a: &str,
token_b: &str,
amount_a: &str,
amount_b: &str,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let rpc_client = state.get_rpc_client()?;
let token_a_hash =
resolve_token_to_scripthash_with_network(token_a, rpc_client, network_type).await?;
let token_b_hash =
resolve_token_to_scripthash_with_network(token_b, rpc_client, network_type).await?;
let amount_a_value = parse_amount(amount_a, &token_a_hash, rpc_client, network_type).await?;
let amount_b_value = parse_amount(amount_b, &token_b_hash, rpc_client, network_type).await?;
let flamingo = FlamingoContract::new(Some(rpc_client));
let tx_id = flamingo
.add_liquidity(&token_a_hash, &token_b_hash, amount_a_value, amount_b_value, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to add liquidity: {}", e)))?;
println!("Added liquidity successfully!");
println!("Transaction ID: {tx_id}");
println!("Token A: {} {}", amount_a, token_a);
println!("Token B: {} {}", amount_b, token_b);
Ok(())
}
pub async fn handle_flamingo_remove_liquidity(
token_a: &str,
token_b: &str,
liquidity: &str,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let rpc_client = state.get_rpc_client()?;
let token_a_hash =
resolve_token_to_scripthash_with_network(token_a, rpc_client, network_type).await?;
let token_b_hash =
resolve_token_to_scripthash_with_network(token_b, rpc_client, network_type).await?;
let liquidity_value = parse_amount(liquidity, &token_a_hash, rpc_client, network_type).await?;
let flamingo = FlamingoContract::new(Some(rpc_client));
let tx_id = flamingo
.remove_liquidity(&token_a_hash, &token_b_hash, liquidity_value, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to remove liquidity: {}", e)))?;
println!("Removed liquidity successfully!");
println!("Transaction ID: {tx_id}");
println!("Liquidity amount: {liquidity}");
println!("Tokens: {} and {}", token_a, token_b);
Ok(())
}
pub async fn handle_flamingo_stake(
token: &str,
amount: &str,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let rpc_client = state.get_rpc_client()?;
let token_hash =
resolve_token_to_scripthash_with_network(token, rpc_client, network_type).await?;
let amount_value = parse_amount(amount, &token_hash, rpc_client, network_type).await?;
let flamingo = FlamingoContract::new(Some(rpc_client));
let tx_id = flamingo
.stake(&token_hash, amount_value, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to stake tokens: {}", e)))?;
println!("Staked tokens successfully!");
println!("Transaction ID: {tx_id}");
println!("Amount: {} {}", amount, token);
Ok(())
}
pub async fn handle_flamingo_claim_rewards(state: &mut CliState) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let rpc_client = state.get_rpc_client()?;
let flamingo = FlamingoContract::new(Some(rpc_client));
let tx_id = flamingo
.claim_rewards(&account)
.await
.map_err(|e| CliError::Contract(format!("Failed to claim rewards: {}", e)))?;
println!("Claimed rewards successfully!");
println!("Transaction ID: {tx_id}");
Ok(())
}
pub async fn handle_neoburger_wrap(amount: &str, state: &mut CliState) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let rpc_client = state.get_rpc_client()?;
let neo_hash = ScriptHash::from_str("ef4073a0f2b305a38ec4050e4d3d28bc40ea63f5").unwrap();
let amount_value = parse_amount(amount, &neo_hash, rpc_client, network_type).await?;
let neoburger = NeoburgerContract::new(Some(rpc_client));
let tx_id = neoburger
.wrap(amount_value, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to wrap NEO: {}", e)))?;
println!("Wrapped NEO to bNEO successfully!");
println!("Transaction ID: {tx_id}");
println!("Amount: {amount} NEO");
Ok(())
}
pub async fn handle_neoburger_unwrap(amount: &str, state: &mut CliState) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let rpc_client = state.get_rpc_client()?;
let bneo_hash = ScriptHash::from_str("48c40d4666f93408be1bef038b6722404f5c4a5a").unwrap();
let amount_value = parse_amount(amount, &bneo_hash, rpc_client, network_type).await?;
let neoburger = NeoburgerContract::new(Some(rpc_client));
let tx_id = neoburger
.unwrap(amount_value, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to unwrap bNEO: {}", e)))?;
println!("Unwrapped bNEO to NEO successfully!");
println!("Transaction ID: {tx_id}");
println!("Amount: {amount} bNEO");
Ok(())
}
pub async fn handle_neoburger_claim_gas(state: &mut CliState) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let rpc_client = state.get_rpc_client()?;
let neoburger = NeoburgerContract::new(Some(rpc_client));
let tx_id = neoburger
.claim_gas(&account)
.await
.map_err(|e| CliError::Contract(format!("Failed to claim GAS: {}", e)))?;
println!("Claimed GAS successfully!");
println!("Transaction ID: {tx_id}");
Ok(())
}
pub async fn handle_neoburger_get_rate(state: &mut CliState) -> Result<(), CliError> {
let rpc_client = state.get_rpc_client()?;
let neoburger = NeoburgerContract::new(Some(rpc_client));
let rate = neoburger
.get_rate()
.await
.map_err(|e| CliError::Contract(format!("Failed to get exchange rate: {}", e)))?;
println!("Current bNEO to NEO exchange rate: {:.8}", rate);
println!("1 bNEO = {:.8} NEO", rate);
Ok(())
}
pub async fn handle_neocompound_deposit(
token: &str,
amount: &str,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let rpc_client = state.get_rpc_client()?;
let token_hash =
resolve_token_to_scripthash_with_network(token, rpc_client, network_type).await?;
let amount_value = parse_amount(amount, &token_hash, rpc_client, network_type).await?;
let neocompound = NeoCompoundContract::new(Some(rpc_client));
let tx_id = neocompound
.deposit(&token_hash, amount_value, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to deposit tokens: {}", e)))?;
println!("Deposited tokens successfully!");
println!("Transaction ID: {tx_id}");
println!("Amount: {} {}", amount, token);
Ok(())
}
pub async fn handle_neocompound_withdraw(
token: &str,
amount: &str,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let rpc_client = state.get_rpc_client()?;
let token_hash =
resolve_token_to_scripthash_with_network(token, rpc_client, network_type).await?;
let amount_value = parse_amount(amount, &token_hash, rpc_client, network_type).await?;
let neocompound = NeoCompoundContract::new(Some(rpc_client));
let tx_id = neocompound
.withdraw(&token_hash, amount_value, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to withdraw tokens: {}", e)))?;
println!("Withdrew tokens successfully!");
println!("Transaction ID: {tx_id}");
println!("Amount: {} {}", amount, token);
Ok(())
}
pub async fn handle_neocompound_compound(
token: &str,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let rpc_client = state.get_rpc_client()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let token_hash =
resolve_token_to_scripthash_with_network(token, rpc_client, network_type).await?;
let neocompound = NeoCompoundContract::new(Some(rpc_client));
let tx_id = neocompound
.compound(&token_hash, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to compound tokens: {}", e)))?;
println!("Compounded tokens successfully!");
println!("Transaction ID: {tx_id}");
println!("Token: {token}");
Ok(())
}
pub async fn handle_neocompound_get_apy(token: &str, state: &mut CliState) -> Result<(), CliError> {
let rpc_client = state.get_rpc_client()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let token_hash =
resolve_token_to_scripthash_with_network(token, rpc_client, network_type).await?;
let neocompound = NeoCompoundContract::new(Some(rpc_client));
let apy = neocompound
.get_apy(&token_hash)
.await
.map_err(|e| CliError::Contract(format!("Failed to get APY: {}", e)))?;
println!("Current APY for {}: {:.2}%", token, apy);
Ok(())
}
pub async fn handle_grandshare_submit_proposal(
title: &str,
description: &str,
amount: &str,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let rpc_client = state.get_rpc_client()?;
let gas_hash = ScriptHash::from_str("d2a4cff31913016155e38e474a2c06d08be276cf").unwrap();
let amount_value = parse_amount(amount, &gas_hash, rpc_client, network_type).await?;
let grandshare = GrandShareContract::new(Some(rpc_client));
let tx_id = grandshare
.submit_proposal(title, description, amount_value, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to submit proposal: {}", e)))?;
println!("Submitted proposal successfully!");
println!("Transaction ID: {tx_id}");
println!("Title: {title}");
println!("Requested amount: {amount}");
Ok(())
}
pub async fn handle_grandshare_vote(
proposal_id: i32,
approve: bool,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let rpc_client = state.get_rpc_client()?;
let grandshare = GrandShareContract::new(Some(rpc_client));
let tx_id = grandshare
.vote(proposal_id, approve, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to vote on proposal: {}", e)))?;
println!("Vote submitted successfully!");
println!("Transaction ID: {tx_id}");
println!("Proposal ID: {proposal_id}");
println!("Vote: {}", if approve { "Approve" } else { "Reject" });
Ok(())
}
pub async fn handle_grandshare_fund_project(
project_id: i32,
amount: &str,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let network_type = NetworkTypeCli::from_network_string(&state.get_network_type_string());
let rpc_client = state.get_rpc_client()?;
let gas_hash = ScriptHash::from_str("d2a4cff31913016155e38e474a2c06d08be276cf").unwrap();
let amount_value = parse_amount(amount, &gas_hash, rpc_client, network_type).await?;
let grandshare = GrandShareContract::new(Some(rpc_client));
let tx_id = grandshare
.fund_project(project_id, amount_value, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to fund project: {}", e)))?;
println!("Project funded successfully!");
println!("Transaction ID: {tx_id}");
println!("Project ID: {project_id}");
println!("Amount: {amount}");
Ok(())
}
pub async fn handle_grandshare_claim_funds(
project_id: i32,
state: &mut CliState,
) -> Result<(), CliError> {
if state.wallet.is_none() {
return Err(CliError::NoWallet);
}
let _wallet = state.wallet.as_ref().unwrap();
let account = state.get_account()?;
let rpc_client = state.get_rpc_client()?;
let grandshare = GrandShareContract::new(Some(rpc_client));
let tx_id = grandshare
.claim_funds(project_id, &account)
.await
.map_err(|e| CliError::Contract(format!("Failed to claim funds: {}", e)))?;
println!("Funds claimed successfully!");
println!("Transaction ID: {tx_id}");
println!("Project ID: {project_id}");
Ok(())
}
#[allow(dead_code)]
fn resolve_script_hash(input: &str) -> Result<ScriptHash, CliError> {
if let Ok(script_hash) = ScriptHash::from_str(input) {
return Ok(script_hash);
}
let hash_str = match input.to_uppercase().as_str() {
"NEO" => "ef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
"GAS" => "d2a4cff31913016155e38e474a2c06d08be276cf",
"FLM" | "FLAMINGO" => "f0151f528127558851b39c2cd8aa47da7418ab28",
"BNEO" | "BURGERNEO" => "48c40d4666f93408be1bef038b6722404f5c4a5a",
"CNEO" | "CGAS" => "c4c7061c4ef8b9e15d97d8b74ad86db9c3a5d89e",
"FNEO" => "c9c0fc72cf74056ccbc69482101d28fe8c60b956",
"FGAS" => "8b38b6b4c6a3b5f9e9ec8e5e2f7a1b1c2d2c3c4",
"USDT" => "2b0f4b3e87b35a8d98b9ef97b7c7b5b1b0a9a8a7",
"USDC" => "1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0",
"BUSD" => "7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6",
"DAI" => "5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4",
"WETH" => "3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2",
"WBTC" => "1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0",
"ONT" => "9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8",
"ONG" => "8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7",
"POLY" => "7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6",
"SWTH" => "6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5",
"NASH" => "5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4",
"LRB" => "4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3",
"SOUL" => "3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2",
"GHO" => "2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1",
"COZ" => "1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0",
"RED" => "0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9",
"MCT" => "f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8",
"EFX" => "e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7",
"NX" => "d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6",
"SPOT" => "c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5",
"RALLY" => "b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4",
"EPIC" => "a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3",
"DOGE" => "93b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"SHIB" => "84c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3",
"PEPE" => "75d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4",
"BONK" => "66e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5",
_ => {
let similar_tokens = find_similar_tokens(input);
let suggestion = if !similar_tokens.is_empty() {
format!(" Did you mean: {}?", similar_tokens.join(", "))
} else {
". Available tokens: NEO, GAS, FLM, BNEO, USDT, USDC, and more".to_string()
};
return Err(CliError::InvalidArgument(
format!("Unknown token or invalid script hash: {}{}", input, suggestion),
"Please provide a valid NEO address, script hash, or supported token symbol"
.to_string(),
));
},
};
ScriptHash::from_str(hash_str).map_err(|_| {
CliError::InvalidArgument(
format!("Invalid script hash format: {}", hash_str),
"Please provide a valid script hash".to_string(),
)
})
}
#[allow(dead_code)]
fn find_similar_tokens(input: &str) -> Vec<String> {
const SUPPORTED_TOKENS: &[&str] = &[
"NEO",
"GAS",
"FLM",
"FLAMINGO",
"BNEO",
"BURGERNEO",
"CNEO",
"CGAS",
"FNEO",
"FGAS",
"USDT",
"USDC",
"BUSD",
"DAI",
"WETH",
"WBTC",
"ONT",
"ONG",
"POLY",
"SWTH",
"NASH",
"LRB",
"SOUL",
"GHO",
"COZ",
"RED",
"MCT",
"EFX",
"NX",
"SPOT",
"RALLY",
"EPIC",
"DOGE",
"SHIB",
"PEPE",
"BONK",
];
let input_upper = input.to_uppercase();
let mut suggestions = Vec::new();
for token in SUPPORTED_TOKENS {
if token.contains(&input_upper) || input_upper.contains(token) {
suggestions.push(token.to_string());
}
}
if suggestions.is_empty() {
for token in SUPPORTED_TOKENS {
if input_upper.len() >= 2 && token.len() >= 2 {
if (input_upper.starts_with(&token[..2.min(token.len())])
&& input_upper.len() <= token.len() + 2)
|| (input_upper.ends_with(&token[token.len().saturating_sub(2)..])
&& input_upper.len() <= token.len() + 2)
{
suggestions.push(token.to_string());
}
}
}
}
suggestions.truncate(3);
suggestions
}
#[derive(Debug, Clone)]
pub struct TokenInfo {
pub symbol: String,
pub script_hash: String,
pub decimals: u8,
pub name: String,
pub category: TokenCategory,
}
#[derive(Debug, Clone)]
pub enum TokenCategory {
Core,
DeFi,
Stablecoin,
CrossChain,
Gaming,
Infrastructure,
Exchange,
Meme,
}
impl TokenInfo {
pub fn new(
symbol: &str,
script_hash: &str,
decimals: u8,
name: &str,
category: TokenCategory,
) -> Self {
Self {
symbol: symbol.to_string(),
script_hash: script_hash.to_string(),
decimals,
name: name.to_string(),
category,
}
}
}
pub fn get_token_registry() -> std::collections::HashMap<String, TokenInfo> {
use std::collections::HashMap;
let mut registry = HashMap::new();
registry.insert(
"NEO".to_string(),
TokenInfo::new(
"NEO",
"ef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
0,
"Neo Token",
TokenCategory::Core,
),
);
registry.insert(
"GAS".to_string(),
TokenInfo::new(
"GAS",
"d2a4cff31913016155e38e474a2c06d08be276cf",
8,
"GAS Token",
TokenCategory::Core,
),
);
registry.insert(
"FLM".to_string(),
TokenInfo::new(
"FLM",
"f0151f528127558851b39c2cd8aa47da7418ab28",
8,
"Flamingo Finance",
TokenCategory::DeFi,
),
);
registry.insert(
"FLAMINGO".to_string(),
TokenInfo::new(
"FLAMINGO",
"f0151f528127558851b39c2cd8aa47da7418ab28",
8,
"Flamingo Finance",
TokenCategory::DeFi,
),
);
registry.insert(
"BNEO".to_string(),
TokenInfo::new(
"BNEO",
"48c40d4666f93408be1bef038b6722404f5c4a5a",
8,
"Burger NEO",
TokenCategory::DeFi,
),
);
registry.insert(
"USDT".to_string(),
TokenInfo::new(
"USDT",
"2b0f4b3e87b35a8d98b9ef97b7c7b5b1b0a9a8a7",
6,
"Tether USD",
TokenCategory::Stablecoin,
),
);
registry.insert(
"USDC".to_string(),
TokenInfo::new(
"USDC",
"1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0",
6,
"USD Coin",
TokenCategory::Stablecoin,
),
);
registry
}
pub fn get_token_info(symbol: &str) -> Result<TokenInfo, CliError> {
let registry = get_token_registry();
let symbol_upper = symbol.to_uppercase();
registry.get(&symbol_upper).cloned().ok_or_else(|| {
let similar = find_similar_tokens(symbol);
let suggestion = if !similar.is_empty() {
format!(" Did you mean: {}?", similar.join(", "))
} else {
". Use 'neo-cli tokens list' to see all supported tokens".to_string()
};
CliError::InvalidArgument(
format!("Token '{}' not found{}", symbol, suggestion),
"Please provide a supported token symbol".to_string(),
)
})
}