diff --git a/test-cli/src/cmd/mod.rs b/test-cli/src/cmd/mod.rs index d93c6fe..73c28e7 100644 --- a/test-cli/src/cmd/mod.rs +++ b/test-cli/src/cmd/mod.rs @@ -9,6 +9,7 @@ use crate::Cli; pub mod create_order; pub mod initialize; pub mod settle; +pub mod solver; /// Shared context threaded through every subcommand. pub struct Context { diff --git a/test-cli/src/cmd/solver.rs b/test-cli/src/cmd/solver.rs new file mode 100644 index 0000000..12e2d5f --- /dev/null +++ b/test-cli/src/cmd/solver.rs @@ -0,0 +1,91 @@ +use anyhow::Context as _; +use clap::{Args as ClapArgs, Parser, Subcommand}; +use cow_settlement_client::{ + cow_settlement_interface::{pda::state::find_state_pda, Pubkey}, + instructions::AddSolver, +}; +use solana_sdk::{ + signature::{read_keypair_file, Signer}, + transaction::Transaction, +}; + +use crate::helpers::print_summary; + +use super::Context; + +#[derive(Parser)] +pub struct SolverArgs { + #[command(subcommand)] + command: SolverCommand, +} + +#[derive(Subcommand)] +enum SolverCommand { + #[command(about = "Authorize a solver to settle orders")] + Add(AddArgs), +} + +#[derive(ClapArgs)] +pub struct AddArgs { + /// Address of the solver to authorize + solver: Pubkey, + + /// Path to the manager keypair, which authorizes the change and must sign + /// it (defaults to the payer keypair, which always funds the state PDA's + /// growth) + #[arg(long)] + manager: Option, +} + +pub fn run(ctx: Context, args: SolverArgs) -> anyhow::Result<()> { + match args.command { + SolverCommand::Add(args) => add(ctx, args), + } +} + +fn add(ctx: Context, args: AddArgs) -> anyhow::Result<()> { + let payer = ctx.payer.pubkey(); + // Without `--manager` the payer manages the program, so reuse the keypair + // the context already holds instead of reading a file again. + let from_file = args + .manager + .map(|path| { + read_keypair_file(&path) + .map_err(|e| anyhow::anyhow!("failed to read manager keypair from {path}: {e}")) + }) + .transpose()?; + let manager = from_file.as_ref().unwrap_or(&ctx.payer); + let manager_pubkey = manager.pubkey(); + + let ix = AddSolver { + program_id: ctx.program_id, + manager: manager_pubkey, + payer, + solver: args.solver, + }; + + let blockhash = ctx + .rpc + .get_latest_blockhash() + .context("failed to fetch blockhash")?; + let tx = Transaction::new_signed_with_payer( + &[ix.into()], + Some(&payer), + &[&ctx.payer, manager], + blockhash, + ); + let sig = ctx + .rpc + .send_and_confirm_transaction(&tx) + .context("transaction failed")?; + + let (state_pda, _) = find_state_pda(&ctx.program_id); + print_summary(&[ + ("signature", &sig), + ("added solver", &args.solver), + ("manager", &manager_pubkey), + ("statePda", &state_pda), + ]); + + Ok(()) +} diff --git a/test-cli/src/main.rs b/test-cli/src/main.rs index 352c309..e045a95 100644 --- a/test-cli/src/main.rs +++ b/test-cli/src/main.rs @@ -73,6 +73,8 @@ enum Commands { Buy(cmd::create_order::BuyOrSellArgs), #[command(about = "Settle one or more orders")] Settle(cmd::settle::SettleArgs), + #[command(about = "Manage the program's solvers (e.g. `cow solver add
`)")] + Solver(cmd::solver::SolverArgs), } fn main() -> anyhow::Result<()> { @@ -83,5 +85,6 @@ fn main() -> anyhow::Result<()> { Commands::Sell(args) => cmd::create_order::run_sell(ctx, args), Commands::Buy(args) => cmd::create_order::run_buy(ctx, args), Commands::Settle(args) => cmd::settle::run(ctx, args), + Commands::Solver(args) => cmd::solver::run(ctx, args), } }