use crate::CommonOpts;
use crate::{display::format_table, ham::Ham};
use anyhow::Result;
use colored::Colorize;
use dialoguer::{Confirm, Input, Select};
use holochain_client::Timestamp;
use holochain_types::dna::ActionHash;
use rave_engine::types::entries::{
AddressBook, PieceDefinition, PieceDefinitionExt, PoolDefinition, PoolDefinitionExt,
SystemRaveAgreements, TransactionFeeCompute,
};
use rave_engine::types::{ActionHashB64, AgentPubKeyB64};
use serde_json::Value;
use std::str::FromStr;
use std::time::Duration;
use zfuel::fuel::ZFuel;
pub async fn initialize_pool_definition(
value: CommonOpts,
effective_start_date: Option<u64>,
expiration_duration_days: Option<u64>,
pay_transaction_fee_agreement: Option<ActionHashB64>,
compute_credit_limit_agreement: Option<ActionHashB64>,
executor: Option<Option<AddressBook>>,
additional_rave_agreements: Option<Vec<ActionHashB64>>,
) -> Result<()> {
let agent = Ham::connect(value.clone()).await?;
println!("{}", "\nPool Definition Setup".bold().underline());
let effective_start_date = if effective_start_date.is_none() {
let use_current = Select::new()
.with_prompt("When should this pool definition become effective?")
.items(&["Use current time", "Specify custom date"])
.default(0)
.interact()?;
if use_current == 0 {
None
} else {
let days: u64 = Input::new()
.with_prompt("Enter number of days from now")
.default(0)
.interact()?;
Some(Timestamp::now().as_micros() as u64 + (days * 24 * 60 * 60 * 1_000_000))
}
} else {
effective_start_date
};
let expiration_duration_days = if expiration_duration_days.is_none() {
let days: u64 = Input::new()
.with_prompt("Enter pool definition duration in days (default 30)")
.default(30)
.interact()?;
Some(days)
} else {
expiration_duration_days
};
let pay_transaction_fee_agreement = if pay_transaction_fee_agreement.is_none() {
let input: String = Input::new()
.with_prompt("Enter transaction fee agreement hash")
.interact()?;
Some(ActionHashB64::from_str(&input)?)
} else {
pay_transaction_fee_agreement
};
let compute_credit_limit_agreement = if compute_credit_limit_agreement.is_none() {
let input: String = Input::new()
.with_prompt("Enter credit limit agreement hash")
.interact()?;
Some(ActionHashB64::from_str(&input)?)
} else {
compute_credit_limit_agreement
};
let executor = if executor.is_none() {
let mut agents: Option<AddressBook> = None;
println!("\n{}", "Special Agents Setup".bold());
println!("These agents will be authorized to collect transaction fees");
loop {
let action = Select::new()
.with_prompt("Executor Menu")
.items(&["Add agent", "Done"])
.default(0)
.interact()?;
match action {
0 => {
let agent: String = Input::new()
.with_prompt("Enter agent public key")
.interact()?;
match AgentPubKeyB64::from_str(&agent) {
Ok(agent_key) => {
let address_book = AddressBook {
pub_key: agent_key,
address_book_data: Value::Null,
};
agents = Some(address_book);
println!("✓ Agent added");
}
Err(_) => println!("✗ Invalid agent public key, try again"),
};
}
_ => break,
}
}
if agents.is_none() {
println!("\n{}", "Warning: No executor added. This means no one will be able to collect transaction fees.".yellow());
if !Confirm::new()
.with_prompt("Continue without executor?")
.default(false)
.interact()?
{
return Err(anyhow::anyhow!("Cancelled - no executor added"));
}
}
agents
} else {
executor.unwrap()
};
let additional_rave_agreements = if additional_rave_agreements.is_none() {
let mut agreements = Vec::new();
println!("\n{}", "Additional RAVE Agreements Setup".bold());
println!("These are optional agreements that will be added to the pool");
loop {
let action = Select::new()
.with_prompt("Additional Agreements Menu")
.items(&[
"Add agreement",
"List agreements",
"Remove agreement",
"Done",
])
.default(0)
.interact()?;
match action {
0 => {
let agreement_hash: String = Input::new()
.with_prompt("Enter agreement hash")
.interact()?;
match ActionHashB64::from_str(&agreement_hash) {
Ok(agreement_id) => {
let mut agreement_agents = Vec::new();
loop {
let agent_action = Select::new()
.with_prompt("Special Agents for this Agreement")
.items(&["Add agent", "List agents", "Remove agent", "Done"])
.default(0)
.interact()?;
match agent_action {
0 => {
let agent: String = Input::new()
.with_prompt("Enter agent public key")
.interact()?;
match AgentPubKeyB64::from_str(&agent) {
Ok(agent_key) => {
agreement_agents.push(agent_key);
println!("✓ Agent added");
}
Err(_) => println!("✗ Invalid agent public key"),
}
}
1 => {
if agreement_agents.is_empty() {
println!("No agents added yet");
} else {
println!("\nCurrent agents for this agreement:");
for (i, agent) in agreement_agents.iter().enumerate() {
println!("{}. {}", i + 1, agent);
}
}
}
2 => {
if agreement_agents.is_empty() {
println!("No agents to remove");
continue;
}
let choices: Vec<String> = agreement_agents
.iter()
.map(|a| a.to_string())
.collect();
let selection = Select::new()
.with_prompt("Select agent to remove")
.items(&choices)
.interact()?;
agreement_agents.remove(selection);
println!("✓ Agent removed");
}
_ => break,
}
}
agreements.push(agreement_id.into());
println!("✓ Agreement added");
}
Err(_) => println!("✗ Invalid agreement hash"),
}
}
1 => {
if agreements.is_empty() {
println!("No agreements added yet");
} else {
println!("\nCurrent agreements:");
for (i, agreement) in agreements.iter().enumerate() {
println!("{}. Agreement ID: {}", i + 1, agreement);
}
}
}
2 => {
if agreements.is_empty() {
println!("No agreements to remove");
continue;
}
let choices: Vec<String> =
agreements.iter().map(|a| format!("ID: {} ", a)).collect();
let selection = Select::new()
.with_prompt("Select agreement to remove")
.items(&choices)
.interact()?;
agreements.remove(selection);
println!("✓ Agreement removed");
}
_ => break,
}
}
agreements
} else {
additional_rave_agreements.unwrap()
};
let effective_start_date = if let Some(start) = effective_start_date {
Timestamp::from_micros(start as i64)
} else {
Timestamp::now()
};
let days = expiration_duration_days.unwrap_or(30);
let duration = Duration::from_secs(days * 24 * 60 * 60);
let expiration_date = (effective_start_date + duration)?;
let additional_special_agents = if let Some(executor) = executor.clone() {
vec![executor]
} else {
vec![]
};
let pool_definition = PoolDefinition {
effective_start_date,
expiration_date,
system_rave_agreements: SystemRaveAgreements {
compute_credit_limit: compute_credit_limit_agreement.unwrap().into(),
compute_transaction_fee: TransactionFeeCompute {
agreement: pay_transaction_fee_agreement.unwrap().into(),
fee_trigger: ZFuel::from_str("10").unwrap(),
fee_percentage: 1,
},
},
additional_special_agents,
additional_rave_agreements: additional_rave_agreements.clone(),
};
println!("\n{}", "Review Pool Definition".bold());
println!("Effective Start: {}", effective_start_date);
println!("Expiration Date: {}", expiration_date);
println!("Executor: {:?}", executor);
println!(
"Additional Agreements: {}",
additional_rave_agreements.len()
);
let confirm = Select::new()
.with_prompt("Submit pool definition?")
.items(&["Yes", "No"])
.default(0)
.interact()?;
if confirm == 1 {
println!("Cancelled");
return Ok(());
}
let result: ActionHash = agent
.zome_call(
"alliance",
"transactor",
"initialize_pool_definition",
pool_definition,
)
.await?;
println!("\n{}", "Pool Definition Initialized:".green());
println!("Action Hash: {}", result);
Ok(())
}
pub async fn get_current_pool_definition(value: CommonOpts) -> Result<()> {
let agent = Ham::connect(value).await?;
let result: PoolDefinitionExt = agent
.zome_call("alliance", "transactor", "get_current_pool_definition", ())
.await?;
println!("{}", "Pool Definitions:".bold());
println!("{:?}", result);
Ok(())
}
pub async fn add_pool_pieces(value: CommonOpts, piece_definition: PieceDefinition) -> Result<()> {
let agent = Ham::connect(value).await?;
let result: ActionHash = agent
.zome_call(
"alliance",
"transactor",
"add_pool_pieces",
piece_definition,
)
.await?;
println!("Pool Pieces Added:");
println!("Action Hash: {:?}", result);
Ok(())
}
pub async fn update_pool_pieces(
value: CommonOpts,
piece_definition: PieceDefinitionExt,
) -> Result<()> {
let agent = Ham::connect(value).await?;
let result: ActionHash = agent
.zome_call(
"alliance",
"transactor",
"update_pool_pieces",
piece_definition,
)
.await?;
println!("Pool Pieces Updated:");
println!("Action Hash: {:?}", result);
Ok(())
}
pub async fn get_pool_pieces_details(value: CommonOpts) -> Result<()> {
let agent = Ham::connect(value).await?;
let result: Vec<PieceDefinitionExt> = agent
.zome_call("alliance", "transactor", "get_pool_pieces_details", ())
.await?;
println!("Pool Pieces Details:");
if !result.is_empty() {
let table = format_table(&result, "Pool Pieces");
table.printstd();
} else {
println!("{}", "No pool pieces".dimmed());
}
Ok(())
}