mod balance;
mod busses;
mod claim;
mod cu_limits;
#[cfg(feature = "admin")]
mod initialize;
mod mine;
mod register;
mod rewards;
mod send_and_confirm;
mod treasury;
#[cfg(feature = "admin")]
mod update_admin;
#[cfg(feature = "admin")]
mod update_difficulty;
mod utils;
use std::sync::Arc;
use clap::{command, Parser, Subcommand};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{
commitment_config::CommitmentConfig,
signature::{read_keypair_file, Keypair},
};
struct Miner {
pub keypair_filepath: Option<String>,
pub priority_fee: u64,
pub rpc_client: Arc<RpcClient>,
pub no_sound_notification: bool,
}
#[derive(Parser, Debug)]
#[command(about, version)]
struct Args {
#[arg(
long,
value_name = "NETWORK_URL",
help = "Network address of your RPC provider",
global = true
)]
rpc: Option<String>,
#[clap(
global = true,
short = 'C',
long = "config",
id = "PATH",
help = "Filepath to config file."
)]
pub config_file: Option<String>,
#[arg(
long,
value_name = "KEYPAIR_FILEPATH",
help = "Filepath to keypair to use",
global = true
)]
keypair: Option<String>,
#[arg(
long,
value_name = "MICROLAMPORTS",
help = "Number of microlamports to pay as priority fee per transaction",
default_value = "0",
global = true
)]
priority_fee: u64,
#[arg(
long,
value_name = "NO_SOUND_NOTIFICATION",
help = "Sound notification off by default",
default_value = "false",
global = true
)]
no_sound_notification: bool,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
#[command(about = "Fetch the Mars balance of an account")]
Balance(BalanceArgs),
#[command(about = "Fetch the distributable rewards of the busses")]
Busses(BussesArgs),
#[command(about = "Mine Mars using local compute")]
Mine(MineArgs),
#[command(about = "Claim available mining rewards")]
Claim(ClaimArgs),
#[command(about = "Fetch your balance of unclaimed mining rewards")]
Rewards(RewardsArgs),
#[command(about = "Fetch the treasury account and balance")]
Treasury(TreasuryArgs),
#[cfg(feature = "admin")]
#[command(about = "Initialize the program")]
Initialize(InitializeArgs),
#[cfg(feature = "admin")]
#[command(about = "Update the program admin authority")]
UpdateAdmin(UpdateAdminArgs),
#[cfg(feature = "admin")]
#[command(about = "Update the mining difficulty")]
UpdateDifficulty(UpdateDifficultyArgs),
}
#[derive(Parser, Debug)]
struct BalanceArgs {
#[arg(
// long,
value_name = "ADDRESS",
help = "The address of the account to fetch the balance of"
)]
pub address: Option<String>,
}
#[derive(Parser, Debug)]
struct BussesArgs {}
#[derive(Parser, Debug)]
struct RewardsArgs {
#[arg(
// long,
value_name = "ADDRESS",
help = "The address of the account to fetch the rewards balance of"
)]
pub address: Option<String>,
}
#[derive(Parser, Debug)]
struct MineArgs {
#[arg(
long,
short,
value_name = "THREAD_COUNT",
help = "The number of threads to dedicate to mining",
default_value = "1"
)]
threads: u64,
}
#[derive(Parser, Debug)]
struct TreasuryArgs {}
#[derive(Parser, Debug)]
struct ClaimArgs {
#[arg(
// long,
value_name = "AMOUNT",
help = "The amount of rewards to claim. Defaults to max."
)]
amount: Option<f64>,
#[arg(
// long,
value_name = "TOKEN_ACCOUNT_ADDRESS",
help = "Token account to receive mining rewards."
)]
beneficiary: Option<String>,
}
#[cfg(feature = "admin")]
#[derive(Parser, Debug)]
struct InitializeArgs {}
#[cfg(feature = "admin")]
#[derive(Parser, Debug)]
struct UpdateAdminArgs {
new_admin: String,
}
#[cfg(feature = "admin")]
#[derive(Parser, Debug)]
struct UpdateDifficultyArgs {}
#[tokio::main]
async fn main() {
let args = Args::parse();
let cli_config = if let Some(config_file) = &args.config_file {
solana_cli_config::Config::load(config_file).unwrap_or_else(|_| {
eprintln!("error: Could not find config file `{}`", config_file);
std::process::exit(1);
})
} else if let Some(config_file) = &*solana_cli_config::CONFIG_FILE {
solana_cli_config::Config::load(config_file).unwrap_or_default()
} else {
solana_cli_config::Config::default()
};
let cluster = args.rpc.unwrap_or(cli_config.json_rpc_url);
let default_keypair = args.keypair.unwrap_or(cli_config.keypair_path);
let rpc_client = RpcClient::new_with_commitment(cluster, CommitmentConfig::confirmed());
let miner = Arc::new(Miner::new(
Arc::new(rpc_client),
args.priority_fee,
Some(default_keypair),
args.no_sound_notification,
));
match args.command {
Commands::Balance(args) => {
miner.balance(args.address).await;
}
Commands::Busses(_) => {
miner.busses().await;
}
Commands::Rewards(args) => {
miner.rewards(args.address).await;
}
Commands::Treasury(_) => {
miner.treasury().await;
}
Commands::Mine(args) => {
miner.mine(args.threads).await;
}
Commands::Claim(args) => {
miner.claim(args.beneficiary, args.amount).await;
}
#[cfg(feature = "admin")]
Commands::Initialize(_) => {
miner.initialize().await;
}
#[cfg(feature = "admin")]
Commands::UpdateAdmin(args) => {
miner.update_admin(args.new_admin).await;
}
#[cfg(feature = "admin")]
Commands::UpdateDifficulty(_) => {
miner.update_difficulty().await;
}
}
}
impl Miner {
pub fn new(rpc_client: Arc<RpcClient>, priority_fee: u64, keypair_filepath: Option<String>, no_sound_notification: bool) -> Self {
Self {
rpc_client,
keypair_filepath,
priority_fee,
no_sound_notification,
}
}
pub fn signer(&self) -> Keypair {
match self.keypair_filepath.clone() {
Some(filepath) => read_keypair_file(filepath).unwrap(),
None => panic!("No keypair provided"),
}
}
}