Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tuktuk-crank-turner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ lru = "0.13.0"
lazy_static = "1.5.0"
warp = "0.3.3"
rand = "0.9"
shellexpand = "3.1.1"

[features]
default=[]
46 changes: 44 additions & 2 deletions tuktuk-crank-turner/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod cache;
mod metrics;
pub mod profitability;
pub mod settings;
pub mod setup;
mod sync;
pub mod task_completion_processor;
pub mod task_context;
Expand All @@ -46,8 +47,34 @@ pub struct Cli {
/// Optional configuration file to use. If present the toml file at the
/// given path will be loaded. Environment variables can override the
/// settings in the given file.
#[clap(short = 'c')]
#[clap(
short = 'c',
long,
default_value = "~/.config/helium/cli/tuktuk-crank-turner/config.toml"
)]
pub config: Option<path::PathBuf>,

/// RPC endpoint URL
#[clap(short = 'u', long)]
pub rpc_url: Option<String>,

/// Path to Solana keypair file
#[clap(short = 'k', long)]
pub key_path: Option<String>,

/// Minimum crank fee in lamports
#[clap(short = 'm', long)]
pub min_crank_fee: Option<u64>,
}

impl Cli {
fn get_expanded_config_path(&self) -> Option<path::PathBuf> {
self.config.as_ref().map(|p| {
shellexpand::full(&p.to_string_lossy())
.map(|expanded| path::PathBuf::from(expanded.as_ref()))
.unwrap_or_else(|_| p.clone())
})
}
}

async fn metrics_handler() -> Result<impl Reply, Rejection> {
Expand Down Expand Up @@ -89,7 +116,19 @@ const PACKED_TX_CHANNEL_CAPACITY: usize = 32;
impl Cli {
pub async fn run(&self) -> Result<()> {
register_custom_metrics();
let settings = Settings::new(self.config.as_ref())?;
let mut settings = Settings::new(self.get_expanded_config_path().as_ref())?;

// Apply CLI overrides
if let Some(rpc_url) = &self.rpc_url {
settings.rpc_url = rpc_url.clone();
}
if let Some(key_path) = &self.key_path {
settings.key_path = key_path.clone();
}
if let Some(min_crank_fee) = self.min_crank_fee {
settings.min_crank_fee = min_crank_fee;
}

tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(&settings.log))
.with(tracing_subscriber::fmt::layer().with_span_events(FmtSpan::CLOSE))
Expand Down Expand Up @@ -295,6 +334,9 @@ impl Cli {

#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Create default config if it doesn't exist
setup::create_config_if_missing()?;

let cli = Cli::parse();
cli.run().await
}
13 changes: 10 additions & 3 deletions tuktuk-crank-turner/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl Settings {
/// Load Settings from a given path. Settings are loaded from a given
/// optional path and can be overriden with environment variables.
///
/// Environemnt overrides have the same name as the entries in the settings
/// Environment overrides have the same name as the entries in the settings
/// file in uppercase and prefixed with "QN_". For example
/// "QN_LOG" will override the log setting. A double underscore distinguishes
/// subsections in the settings file
Expand All @@ -79,9 +79,16 @@ impl Settings {
}
// Add in settings from the environment (with a prefix of APP)
// Eg.. `TUKTUK_DEBUG=1 ./target/app` would set the `debug` key
builder
let mut settings: Settings = builder
.add_source(Environment::with_prefix("TUKTUK").separator("__"))
.build()
.and_then(|config| config.try_deserialize())
.and_then(|config| config.try_deserialize())?;

// Expand environment variables in key_path (supports both $HOME and ~)
settings.key_path = shellexpand::full(&settings.key_path)
.map_err(|e| config::ConfigError::Message(format!("Failed to expand key_path: {}", e)))?
.into_owned();

Ok(settings)
}
}
51 changes: 51 additions & 0 deletions tuktuk-crank-turner/src/setup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use anyhow::Result;
use std::fs;
use std::path::PathBuf;

const DEFAULT_CONFIG: &str = r#"# RPC endpoint URL
rpc_url = "https://api.mainnet-beta.solana.com"

# Path to your Solana keypair file
key_path = "~/.config/solana/id.json"

# Minimum crank fee in lamports
min_crank_fee = 10000
"#;

pub fn get_default_config_path() -> Option<PathBuf> {
let home_dir = std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.ok()?;

Some(
PathBuf::from(home_dir)
.join(".config")
.join("helium")
.join("cli")
.join("tuktuk-crank-turner")
.join("config.toml"),
)
}

fn write_default_config(config_path: &PathBuf) -> Result<()> {
if let Some(parent) = config_path.parent() {
fs::create_dir_all(parent)?;
}

fs::write(config_path, DEFAULT_CONFIG)?;

Ok(())
}

pub fn create_config_if_missing() -> Result<()> {
let Some(config_path) = get_default_config_path() else {
eprintln!("Warning: Could not determine home directory. Skipping config creation.");
return Ok(());
};

if !config_path.exists() {
write_default_config(&config_path)?;
}

Ok(())
}