Summary

Your final file should look like the code below after following the steps, with a CLI structure for reading and incrementing the contract state, shared arguments, and functions for compiling, querying, and submitting solutions.

use clap::{Args, Parser, Subcommand};
use counter_app::{counter_key, extract_count, incremented_solution, CounterKey};
use essential_app_utils::compile::compile_pint_project;
use essential_rest_client::node_client::EssentialNodeClient;
use essential_types::{ContentAddress, PredicateAddress, Value};
use std::path::PathBuf;

#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Command,
}

#[derive(Subcommand)]
enum Command {
    ReadCount {
        #[command(flatten)]
        server: Shared,
    },
    IncrementCount {
        /// The address of the builder to connect to.
        builder_api: String,
        #[command(flatten)]
        server: Shared,
    },
}

#[derive(Args)]
pub struct Shared {
    /// The address of the node to connect to.
    pub node_api: String,
    /// The directory containing the pint files.
    pub pint_directory: PathBuf,
}

#[tokio::main]
async fn main() {
    let args = Cli::parse();
    if let Err(err) = run(args).await {
        eprintln!("Command failed because: {}", err);
    }
}

async fn run(cli: Cli) -> anyhow::Result<()> {
    let Cli { command } = cli;
    match command {
        Command::ReadCount {
            server: Shared {
                node_api,
                pint_directory,
            },
        } => {
            let address = compile_address(pint_directory).await?;
            let node = EssentialNodeClient::new(node_api)?;
            let key = counter_key();
            let count = query_count(node, address.contract, key).await?;
            let count_value = extract_count(count)?;
            println!("Current count is: {}", count_value);
        }
        Command::IncrementCount {
            builder_api,
            server: Shared {
                node_api,
                pint_directory,
            },
        } => {
            let address = compile_address(pint_directory).await?;
            let node = EssentialNodeClient::new(node_api)?;
            let key = counter_key();
            let count = query_count(node, address.contract.clone(), key).await?;
            let (solution, new_count) = incremented_solution(count)?; // Pass only count
            let builder =
                essential_rest_client::builder_client::EssentialBuilderClient::new(builder_api)?;
            let ca = builder.submit_solution(&solution).await?;
            println!("Submitted solution: {}", ca);
            println!("Incremented count to: {}", new_count);
        }
    }
    Ok(())
}

async fn query_count(
    node: EssentialNodeClient,
    address: ContentAddress,
    key: CounterKey,
) -> anyhow::Result<Option<Value>> {
    Ok(node.query_state(address, key.0).await?)
}

async fn compile_address(pint_directory: PathBuf) -> Result<PredicateAddress, anyhow::Error> {
    let counter = compile_pint_project(pint_directory).await?;
    let contract_address = essential_hash::contract_addr::from_contract(&counter);
    let predicate_address = essential_hash::content_addr(&counter.predicates[0]);
    let predicate_address = PredicateAddress {
        contract: contract_address,
        predicate: predicate_address,
    };
    Ok(predicate_address)
}