quicknode-cli 0.6.0

Command-line interface for the Quicknode SDK
Documentation
//! `qn webhook …` — filter-template webhooks.

mod actions;
mod render;

use std::path::PathBuf;

use clap::{Args as ClapArgs, Subcommand, ValueEnum};
use quicknode_sdk::webhooks::WebhookStartFrom;

use crate::context::Ctx;
use crate::errors::CliError;

#[derive(Debug, ClapArgs)]
#[command(after_help = "Examples:\n  \
    qn webhook list\n  \
    qn webhook create --name alerts --network ethereum-mainnet \\\n      \
        --url https://hook.example.com --template evmWalletFilter --wallet 0xabc\n  \
    qn webhook pause wh-1234")]
pub struct Args {
    #[command(subcommand)]
    pub cmd: WebhookCmd,
}

#[derive(Debug, Subcommand)]
pub enum WebhookCmd {
    /// List webhooks on the account.
    #[command(visible_alias = "ls")]
    List(ListArgs),
    /// Show a single webhook.
    Show {
        #[arg(value_name = "WEBHOOK_ID")]
        id: String,
    },
    /// Create a webhook from a filter template.
    Create(Box<CreateArgs>),
    /// Update name/email/destination on a webhook (without changing the template).
    Update(UpdateArgs),
    /// Update the template arguments on a webhook (and optionally other fields).
    UpdateTemplate(Box<UpdateTemplateArgs>),
    /// Delete a webhook.
    Delete {
        #[arg(value_name = "WEBHOOK_ID")]
        id: String,
    },
    /// Activate a webhook (resume delivery).
    Activate(ActivateArgs),
    /// Pause a webhook.
    Pause {
        #[arg(value_name = "WEBHOOK_ID")]
        id: String,
    },
    /// Count of currently enabled webhooks.
    EnabledCount,
}

#[derive(Debug, ClapArgs)]
pub struct ListArgs {
    #[arg(long)]
    pub limit: Option<i64>,
    #[arg(long)]
    pub offset: Option<i64>,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum TemplateKind {
    EvmWallet,
    EvmContractEvents,
    EvmAbi,
    SolanaWallet,
    BitcoinWallet,
    XrplWallet,
    HyperliquidWalletEvents,
    StellarWalletTransactions,
}

#[derive(Debug, ClapArgs)]
pub struct CreateArgs {
    /// Webhook name.
    #[arg(long)]
    pub name: String,
    /// Network (e.g. `ethereum-mainnet`).
    #[arg(long)]
    pub network: String,
    /// Destination URL.
    #[arg(long)]
    pub url: String,
    /// Optional security token (server generates one if omitted).
    #[arg(long)]
    pub security_token: Option<String>,
    /// Payload compression (`gzip` or `none`).
    #[arg(long)]
    pub compression: String,
    /// Optional notification email.
    #[arg(long)]
    pub notification_email: Option<String>,

    /// Filter template.
    #[arg(long, value_enum)]
    pub template: TemplateKind,

    /// For wallet-style templates: wallet addresses (repeat).
    #[arg(long = "wallet")]
    pub wallets: Vec<String>,
    /// For Solana wallet template: account addresses (repeat).
    #[arg(long = "account")]
    pub accounts: Vec<String>,
    /// For contract-events and abi templates: contract addresses (repeat).
    #[arg(long = "contract")]
    pub contracts: Vec<String>,
    /// For contract-events template: optional event topic hashes (repeat).
    #[arg(long = "event-hash")]
    pub event_hashes: Vec<String>,
    /// For abi template: contract ABI JSON inline.
    #[arg(long, conflicts_with = "abi_file")]
    pub abi: Option<String>,
    /// For abi template: path to a file with the contract ABI JSON.
    #[arg(long)]
    pub abi_file: Option<PathBuf>,

    /// For wallet-style templates: reference a saved wallets list by name (instead of `--wallet`).
    #[arg(long)]
    pub wallets_list_name: Option<String>,
    /// For Solana wallet template: reference a saved accounts list by name (instead of `--account`).
    #[arg(long)]
    pub accounts_list_name: Option<String>,
    /// For contract-events and abi templates: reference a saved contracts list by name (instead of `--contract`).
    #[arg(long)]
    pub contracts_list_name: Option<String>,
    /// For contract-events template: reference a saved event-hashes list by name.
    #[arg(long)]
    pub event_hashes_list_name: Option<String>,
}

#[derive(Debug, ClapArgs)]
pub struct UpdateArgs {
    #[arg(value_name = "WEBHOOK_ID")]
    pub id: String,
    #[arg(long)]
    pub name: Option<String>,
    #[arg(long)]
    pub notification_email: Option<String>,
    /// New destination URL.
    #[arg(long)]
    pub url: Option<String>,
    #[arg(long)]
    pub security_token: Option<String>,
    #[arg(long)]
    pub compression: Option<String>,
}

#[derive(Debug, ClapArgs)]
pub struct UpdateTemplateArgs {
    #[arg(value_name = "WEBHOOK_ID")]
    pub id: String,
    /// New filter template (same flags as `create`).
    #[arg(long, value_enum)]
    pub template: TemplateKind,
    #[arg(long = "wallet")]
    pub wallets: Vec<String>,
    #[arg(long = "account")]
    pub accounts: Vec<String>,
    #[arg(long = "contract")]
    pub contracts: Vec<String>,
    #[arg(long = "event-hash")]
    pub event_hashes: Vec<String>,
    #[arg(long, conflicts_with = "abi_file")]
    pub abi: Option<String>,
    #[arg(long)]
    pub abi_file: Option<PathBuf>,

    /// For wallet-style templates: reference a saved wallets list by name (instead of `--wallet`).
    #[arg(long)]
    pub wallets_list_name: Option<String>,
    /// For Solana wallet template: reference a saved accounts list by name (instead of `--account`).
    #[arg(long)]
    pub accounts_list_name: Option<String>,
    /// For contract-events and abi templates: reference a saved contracts list by name (instead of `--contract`).
    #[arg(long)]
    pub contracts_list_name: Option<String>,
    /// For contract-events template: reference a saved event-hashes list by name.
    #[arg(long)]
    pub event_hashes_list_name: Option<String>,

    /// Optionally also rename.
    #[arg(long)]
    pub name: Option<String>,
    #[arg(long)]
    pub notification_email: Option<String>,
    #[arg(long)]
    pub url: Option<String>,
    #[arg(long)]
    pub security_token: Option<String>,
    /// Payload compression (`gzip` or `none`); required when `--url` is supplied.
    #[arg(long)]
    pub compression: Option<String>,
}

#[derive(Debug, ClapArgs)]
pub struct ActivateArgs {
    #[arg(value_name = "WEBHOOK_ID")]
    pub id: String,
    /// Where to resume from.
    #[arg(long, value_enum, default_value = "latest")]
    pub start_from: StartFromArg,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum StartFromArg {
    Last,
    Latest,
}

impl From<StartFromArg> for WebhookStartFrom {
    fn from(s: StartFromArg) -> Self {
        match s {
            StartFromArg::Last => WebhookStartFrom::Last,
            StartFromArg::Latest => WebhookStartFrom::Latest,
        }
    }
}

pub async fn run(args: Args, ctx: Ctx) -> Result<(), CliError> {
    match args.cmd {
        WebhookCmd::List(a) => actions::list(a, ctx).await,
        WebhookCmd::Show { id } => actions::show(&id, ctx).await,
        WebhookCmd::Create(a) => actions::create(*a, ctx).await,
        WebhookCmd::Update(a) => actions::update(a, ctx).await,
        WebhookCmd::UpdateTemplate(a) => actions::update_template(*a, ctx).await,
        WebhookCmd::Delete { id } => actions::delete(&id, ctx).await,
        WebhookCmd::Activate(a) => actions::activate(a, ctx).await,
        WebhookCmd::Pause { id } => actions::pause(&id, ctx).await,
        WebhookCmd::EnabledCount => actions::enabled_count(ctx).await,
    }
}