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
710 changes: 428 additions & 282 deletions Cargo.lock

Large diffs are not rendered by default.

16 changes: 10 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = "1.0.95"
anyhow = "1.0.99"
clap = { version = "4", features = ["derive", "env"] }
regex = "1.11.1"
reqwest = { version = "0.12.12", features = ["json"] }
serde = {version = "1.0.217", features = ["derive"] }
serde_json = "1.0.138"
regex = "1.11.2"
reqwest = { version = "0.12.23", features = ["json"] }
serde = {version = "1.0.219", features = ["derive"] }
serde_json = "1.0.143"
strum = { version = "0.26.3", features = ["derive"] }
hex = "0.4.3"
secp256k1 = "0.30.0"
tokio = { version = "1.43.0", features = ["full"] }
tokio = { version = "1.47.1", features = ["full"] }

[dev-dependencies]
insta = { version = "1", features = ["filters"] }
insta-cmd = "0.6.0"
9 changes: 3 additions & 6 deletions src/address.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use anyhow::Result;
use clap::Parser;
use serde_json::Value;

use crate::utils::get;
use crate::utils::{get, print_output};

#[derive(Parser)]
pub enum AddressSubcommands {
Expand Down Expand Up @@ -32,10 +31,8 @@ impl AddressSubcommands {
},
};

let value: Value = get(&url, &endpoint).await?;

serde_json::to_writer_pretty(std::io::stdout(), &value)?;
println!();
let output = get(&url, &endpoint).await?;
print_output(output)?;

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
#[command(version)]
pub struct Kermit {
#[clap(long, short, env, value_hint = ValueHint::Url,
default_value = "https://node.mainnet.alephium.org")]
default_value = "http://localhost:22973")]
pub url: String,

#[clap(subcommand)]
Expand Down
8 changes: 3 additions & 5 deletions src/events.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use anyhow::Result;
use clap::Parser;
use serde_json::Value;

use crate::utils::get;
use crate::utils::{get, print_output};

/// CLI arguments for `kermit events`.
#[derive(Parser)]
Expand Down Expand Up @@ -34,7 +33,7 @@ pub enum EventsSubcommands {

impl EventsSubcommands {
pub async fn run(self, url: &str) -> Result<()> {
let value: Value = match self {
let output = match self {
Self::ContractEvents {
contract_address,
start,
Expand Down Expand Up @@ -70,8 +69,7 @@ impl EventsSubcommands {
},
};

serde_json::to_writer_pretty(std::io::stdout(), &value)?;
println!();
print_output(output)?;

Ok(())
}
Expand Down
23 changes: 12 additions & 11 deletions src/infos.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use anyhow::{Ok, Result};
use anyhow::Result;
use clap::Parser;
use serde_json::{Value, json};
use serde_json::json;

use crate::utils::{get, post};
use crate::utils::{get, post, print_output};

/// CLI arguments for `kermit infos`.
#[derive(Parser)]
Expand Down Expand Up @@ -51,7 +51,7 @@ pub enum InfosSubcommands {
#[command(visible_alias = "hh")]
HistoryHashrate { from_ts: i64, to_ts: Option<i64> },

/// Get average hashrate from now - timespan(millis) to now.
/// Get average hashrate from `now - timespan(millis)` to now.
#[command(visible_alias = "ch")]
CurrentHashrate { timespan: Option<i64> },

Expand All @@ -62,7 +62,7 @@ pub enum InfosSubcommands {

impl InfosSubcommands {
pub async fn run(self, url: &str) -> Result<()> {
let value: Value = match self {
let output = match self {
Self::Node => get(url, "/infos/node").await?,
Self::Version => get(url, "/infos/version").await?,
Self::ChainParams => get(url, "/infos/chain-params").await?,
Expand All @@ -85,7 +85,7 @@ impl InfosSubcommands {
Self::Discovery { r#type, peers } => {
post(
url,
"/infos/misbehaviors",
"/infos/discovery",
json!({
"type": r#type,
"peers": peers
Expand All @@ -94,24 +94,25 @@ impl InfosSubcommands {
.await?
},
Self::HistoryHashrate { from_ts, to_ts } => {
let mut endpoint = format!("/infos/history-hashrate?fromTs={}", from_ts);
let mut endpoint = format!("/infos/history-hashrate?fromTs={from_ts}");
if let Some(to_ts) = to_ts {
endpoint.push_str(&format!("&toTs={}", to_ts));
endpoint.push_str(&format!("&toTs={to_ts}"));
}

get(url, &endpoint).await?
},
Self::CurrentHashrate { timespan } => {
let mut endpoint = "/infos/current-hashrate".to_string();
if let Some(timespan) = timespan {
endpoint.push_str(&format!("?timespan={}", timespan));
endpoint.push_str(&format!("?timespan={timespan}"));
}

get(url, &endpoint).await?
},
Self::CurrentDifficulty => get(url, "/infos/current-difficulty").await?,
};

serde_json::to_writer_pretty(std::io::stdout(), &value)?;
println!();
print_output(output)?;

Ok(())
}
Expand Down
3 changes: 1 addition & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
mod address;
mod args;
mod contract_encoding;
mod contracts;
mod events;
mod infos;
mod transactions;
mod utils;
mod wallet;

mod contract_encoding;

use anyhow::Result;
use args::{Kermit, KermitSubcommand};
use clap::Parser;
Expand Down
20 changes: 11 additions & 9 deletions src/transactions.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use anyhow::{Result, anyhow};
use anyhow::{Result, anyhow, bail};
use clap::Parser;
use secp256k1::{Message, Secp256k1, SecretKey};
use serde::{Deserialize, de::DeserializeOwned};
use serde_json::{Value, json};

use crate::utils::{get, post};
use crate::utils::{get, post, print_output};

/// CLI arguments for `kermit transactions`.
#[derive(Parser)]
Expand Down Expand Up @@ -79,7 +79,7 @@ async fn build<T: DeserializeOwned>(
amount: String,
gas_amount: Option<u64>,
gas_price: Option<String>,
) -> Result<T> {
) -> Result<Option<T>> {
post(
url,
"/transactions/build",
Expand Down Expand Up @@ -115,7 +115,7 @@ fn sign(tx_id: &str, private_key: &str) -> Result<String> {
Ok(signature)
}

async fn submit(url: &str, unsigned_tx: &str, signature: &str) -> Result<Value> {
async fn submit(url: &str, unsigned_tx: &str, signature: &str) -> Result<Option<Value>> {
post(
url,
"/transactions/submit",
Expand All @@ -129,7 +129,7 @@ async fn submit(url: &str, unsigned_tx: &str, signature: &str) -> Result<Value>

impl TransactionsSubcommands {
pub async fn run(self, url: &str) -> Result<()> {
let value: Value = match self {
let output = match self {
Self::Build {
public_key,
to_addr,
Expand All @@ -153,8 +153,11 @@ impl TransactionsSubcommands {
gas_price,
private_key,
} => {
let BuildTransactionResponse { tx_id, unsigned_tx } =
build(url, public_key, to_addr, amount, gas_amount, gas_price).await?;
let Some(BuildTransactionResponse { tx_id, unsigned_tx }) =
build(url, public_key, to_addr, amount, gas_amount, gas_price).await?
else {
bail!("Failed to build transaction");
};

let signature = sign(&tx_id, &private_key)?;
submit(url, &unsigned_tx, &signature).await?
Expand All @@ -172,8 +175,7 @@ impl TransactionsSubcommands {
},
};

serde_json::to_writer_pretty(std::io::stdout(), &value)?;
println!();
print_output(output)?;

Ok(())
}
Expand Down
3 changes: 3 additions & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
mod reqwest;
pub use reqwest::*;

mod print;
pub use print::*;
13 changes: 13 additions & 0 deletions src/utils/print.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use anyhow::Result;
use serde_json::Value;

pub fn print_output(output: Option<Value>) -> Result<()> {
if let Some(output) = output {
serde_json::to_writer_pretty(std::io::stdout(), &output)?;
println!();
} else {
println!("Success!");
}

Ok(())
}
50 changes: 34 additions & 16 deletions src/utils/reqwest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ struct Error {
}

/// Perform a GET request to the given URL
pub async fn get<T: DeserializeOwned>(url: &str, endpoint: &str) -> Result<T> {
pub async fn get<T: DeserializeOwned>(url: &str, endpoint: &str) -> Result<Option<T>> {
let client = Client::new();

let url = format!("{}{}", url, endpoint);
let url = format!("{url}{endpoint}");

let res = client
.get(&url)
.get(url)
.header("Content-Type", "application/json")
.send()
.await?;
Expand All @@ -24,7 +24,11 @@ pub async fn get<T: DeserializeOwned>(url: &str, endpoint: &str) -> Result<T> {
bail!(err.detail);
}

let data = res.json().await?;
let data = if res.content_length() > Some(0) {
Some(res.json().await?)
} else {
None
};

Ok(data)
}
Expand All @@ -34,13 +38,13 @@ pub async fn post<T: DeserializeOwned, U: Serialize>(
url: &str,
endpoint: &str,
body: U,
) -> Result<T> {
) -> Result<Option<T>> {
let client = Client::new();

let url = format!("{}{}", url, endpoint);
let url = format!("{url}{endpoint}");

let res = client
.post(&url)
.post(url)
.header("Content-Type", "application/json")
.json(&body)
.send()
Expand All @@ -51,7 +55,11 @@ pub async fn post<T: DeserializeOwned, U: Serialize>(
bail!(err.detail);
}

let data = res.json().await?;
let data = if res.content_length() > Some(0) {
Some(res.json().await?)
} else {
None
};

Ok(data)
}
Expand All @@ -61,13 +69,13 @@ pub async fn put<T: DeserializeOwned, U: Serialize>(
url: &str,
endpoint: &str,
body: U,
) -> Result<T> {
) -> Result<Option<T>> {
let client = Client::new();

let url = format!("{}{}", url, endpoint);
let url = format!("{url}{endpoint}");

let res = client
.put(&url)
.put(url)
.header("Content-Type", "application/json")
.json(&body)
.send()
Expand All @@ -78,19 +86,23 @@ pub async fn put<T: DeserializeOwned, U: Serialize>(
bail!(err.detail);
}

let data = res.json().await?;
let data = if res.content_length() > Some(0) {
Some(res.json().await?)
} else {
None
};

Ok(data)
}

/// Perform a DELETE request to the given URL
pub async fn delete(url: &str, endpoint: &str) -> Result<()> {
pub async fn delete<T: DeserializeOwned>(url: &str, endpoint: &str) -> Result<Option<T>> {
let client = Client::new();

let url = format!("{}{}", url, endpoint);
let url = format!("{url}{endpoint}");

let res = client
.delete(&url)
.delete(url)
.header("Content-Type", "application/json")
.send()
.await?;
Expand All @@ -100,5 +112,11 @@ pub async fn delete(url: &str, endpoint: &str) -> Result<()> {
bail!(err.detail);
}

Ok(())
let data = if res.content_length() > Some(0) {
Some(res.json().await?)
} else {
None
};

Ok(data)
}
Loading
Loading