Skip to content

Commit

Permalink
[Feature] Command Line Interface (CLI) (#177)
Browse files Browse the repository at this point in the history
* Added better gas estimation for low gas txs

* formatting

* prototype

* small improvements

* instantiate msg

* migrate

* small refactor

* Parse fields

* small refactor

* lints

* Set admin and add coins

* fix generics as variants

* add state_interface to cw_parse

Helps to read daemon state inside cw_parse

* StateInterface is enough

* parse into Empty

* sloppy addons implementation

* fmt

* addons

* from_cli for daemon

* clean unused dependencies

* move to trait and create custom error

* main cli

* addonscontext autoimpl for clone

* add key to keyring

* refactor

* clean ups

* edit comment

* no need to confirm

* messages fix

* format toml

* cosmwasm execute

* execute wasm

* finished execute wasm

* query method

* fix comment

* instantiate and upload

* small renames

* rename actions as actions

* taplo fmt

* add chain selector

* transfers

* get and transfer ownership cw-ownable

* auto-claim if given receiver

* accept and renounce ownerships

* fix show address

* disable warning

* Change parse_network return type + exposed supported networks

* Added env variable for disabling all logs

* Modified docs

* Added granter flag

* Stabilized snapshots

* Using bTreeMap and better syntax

* disable logs message

* add comment on disabling log messages

* fix macos keyring

* fix coin parse

* rename cw-ownable action

* move cw-ownable

* messages update

* raw query

* Save chain instead of chain-id after locking

* add latest command to the history

* fix cosmrs update

* ux improvement and balance queries

* initial readme

* cut out contract cli

* move cw-orch-cli upper

* update authors

* small description

* Fix link

* structure Cargo.toml

* remove tonic

* keys docs

* update readme and some prompts

* add spaces for emojis

* Validate and re-prompt json/base64 if needed

* format

* Address book in commands

* Address book sub-commands

* format

* fix cw-plus deps

* merge fixes

* cover null query responses

* disable back on the first menu

* restrict empty aliases

* Fix of help messages

* WIP: fetch cw_orch

* cw_orch state fetcher

* formatting of messages

* Disable skip of expiration

* small optimization

* few fixes

* multiline json

* enable logs with verbose flag

* Save signer and signer selector

* empty-out file before writing

* std fs fixes

* file management quickfix

* merge cw orch state option

* update readme script

* flag to merge cw-orch state

* fix clippy

* add file message input

* few fixes

* readme update

* move contract cli

* fix readme

* include cw-orch-cli as workspace member

* force disable cw-orch state merging for address_book::remove_address

* Remove msg postfix for a file message type

* clippy

* bump msrv

* merge-cw-orch-state > source-state-file

* newline on errors

* Log url of tx

* add explorer to any address

* base64 raw queries

* quick readme fix

* fix of explorers

* versionbump

* update to newest cw-orch

* clippy

* use daemon bank querier for querying balance

* unused cosmrs

* Use Daemon for sending coins

* post-merge fixes

* new contract queries

* toml format

* upgrade cw-orch-cli to 0.25 cw-orch

* small formatting

* cli doc formatting

* update cosmos action docs

* bump interactive clap

* wasm upload fixed

* remove rejected todos

---------

Co-authored-by: Kayanski <[email protected]>
Co-authored-by: cyberhoward <[email protected]>
  • Loading branch information
3 people authored Oct 31, 2024
1 parent 2e23f74 commit 51edcce
Show file tree
Hide file tree
Showing 57 changed files with 3,123 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ignore:
- "tests"
- "**/examples"
- "**/schema.rs"
- "cw-orch-cli"

# Make comments less noisy
comment:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
## 0.21.2

- Allow cw-orch wasm compilation without features
- Bumped MSRV to 1.74 because of dependency `[email protected]`
- Transaction Response now inspects logs and events to find matching events.

## 0.21.1
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"cw-orch",
"cw-orch-cli",
"cw-orch-daemon",
"cw-orch-interchain",
"packages/cw-orch-core",
Expand Down
45 changes: 45 additions & 0 deletions cw-orch-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[package]
name = "cw-orch-cli"
version = "0.2.4"
authors = ["Buckram <[email protected]>"]
edition.workspace = true
license.workspace = true
repository.workspace = true
description = "Command-line tool for managing Cosmos-based interaction."

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
cw-orch = { workspace = true, features = ["daemon"] }

# Logs
pretty_env_logger = { version = "0.5.0" }
async-trait = { version = "0.1" }

# Cosmos
cosmwasm-std = { workspace = true }
cw-utils = "2.0.0"
cw20 = { version = "2.0" }
cw-ownable = { version = "2.0.0" }
cosmrs = { workspace = true, features = ["cosmwasm", "grpc"] }
ibc-chain-registry = { workspace = true }

# Serde
serde_json = { workspace = true }
serde = { workspace = true }
base64 = { version = "0.22.1" }

# Interactive clap
interactive-clap = "0.3.0"
interactive-clap-derive = "0.3.0"
clap = { version = "4.0.18", features = ["derive"] }
color-eyre = { version = "0.6" }
strum = { version = "0.24", features = ["derive"] }
derive_more = "0.99"
shell-words = "1.0.0"
inquire = { version = "0.6", features = ["editor"] }

# Key management
keyring = "2.0.5"
bip32 = { version = "0.5", features = ["mnemonic"] }
rand_core = { version = "0.6", features = ["std"] }
69 changes: 69 additions & 0 deletions cw-orch-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# CosmWasm Orch Command Line Interface (CLI)

The CosmWasm Orch CLI is a tool designed to facilitate the development, deployment, and interaction with CosmWasm smart contracts on Cosmos blockchains. It enables developers to create, test, and manage contracts using the interactive CLI and easily deploy them onto supported Cosmos networks.

## Installation

### Prerequisites

- Rust
- OpenSSL
- Access to keyring

### Cargo

```bash
cargo install cw-orch-cli
```

### Add last command to the shell history (Optional)

If Cw Orch CLI ran in interactive mode it's executed command will **not** be appended to your shell history. This means you will not be able to `arrow up` to get the last command and tweak it to your liking.

To solve this you can add the function below to your `~/.bashrc` or similar.
This function wraps the CLI and appends its executed action to your current shell history, enabling you to retrieve it from the history.

```bash
cw-orch-cli() {
command=$(command cw-orch-cli "$@" | tee /dev/tty | grep 'Your console command' | cut -f2- -d':')
if [ "$command" != "cw-orch-cli" ]
then
history -s cw-orch-cli "$@" # if you still want to be able `arrow up` to the original command
fi
history -s $command
}
```

## Usage

The CLI supports two modes of execution: interactive and non-interactive.

### Interactive mode

In interactive mode the CLI will guide you through complex tasks by reducing the initial command's complexity, and ensuring a more intuitive user experience.

The interactive mode will prompt you for new information when needed as you go through the process of creating, testing, and deploying a contract.

Example:

```bash
cw-orch-cli --verbose
```

### Non-interactive mode

You can utilize the non-interactive mode for scripting, automated operations, and tweaking of the interactive mode's commands. Often you'll find yourself using the interactive mode to get the command you need, and then debug it with the non-interactive mode.

Example:

```bash
cw-orch-cli action uni-6 cw query raw juno1czkm9gq96zwwncxusgzruvpuex4wjf4ak7lms6q698938k529q3shmfl90 raw contract_info
```

### Global optional arguments

- `-v` or `--verbose` - enable verbose mode, this will log actions from cw-orch daemon executions that corresponds to your `RUST_LOG` level
- `-s` or `--source-state-file` - source cw-orch state file(`STATE_FILE` [cw-orch env variable]) to use together with address-book entries (address book have higher priority)
- --deployment-id <DEPLOYMENT_ID> - cw-orch state deployment-id, defaults to "default"

[cw-orch env variable]: ../docs/src/contracts/env-variable.md
35 changes: 35 additions & 0 deletions cw-orch-cli/src/commands/action/asset/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use super::CosmosContext;

use strum::{EnumDiscriminants, EnumIter, EnumMessage};

mod query_cw20;
mod query_native;
mod send_cw20;
mod send_native;

#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
#[interactive_clap(context = CosmosContext)]
pub struct AssetCommands {
#[interactive_clap(subcommand)]
action: AssetAction,
}

#[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)]
#[strum_discriminants(derive(EnumMessage, EnumIter))]
#[interactive_clap(context = CosmosContext)]
/// Select asset action
pub enum AssetAction {
/// Native or factory coin send
#[strum_discriminants(strum(message = "Send native coins"))]
SendNative(send_native::SendNativeCommands),
/// Cw20 coin transfer
#[strum_discriminants(strum(message = "Send cw20 coin"))]
SendCw20(send_cw20::Cw20TransferCommands),
/// Native or factory coins query
#[strum_discriminants(strum(message = "Query native coins"))]
QueryNative(query_native::QueryNativeCommands),
/// Cw20 coin query
#[strum_discriminants(strum(message = "Query cw20 coins"))]
QueryCw20(query_cw20::QueryCw20Commands),
// TODO: cw720?
}
50 changes: 50 additions & 0 deletions cw-orch-cli/src/commands/action/asset/query_cw20/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::types::CliAddress;

use super::CosmosContext;

use cw20::BalanceResponse;
use cw_orch::prelude::*;

#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
#[interactive_clap(input_context = CosmosContext)]
#[interactive_clap(output_context = QueryCw20Output)]
pub struct QueryCw20Commands {
/// Cw20 Address or alias from address-book
cw20_address: CliAddress,
/// Address or alias from address-book
address: CliAddress,
}

pub struct QueryCw20Output;

impl QueryCw20Output {
fn from_previous_context(
previous_context: CosmosContext,
scope: &<QueryCw20Commands as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope,
) -> color_eyre::eyre::Result<Self> {
let chain = previous_context.chain;

let cw20_account_id = scope
.cw20_address
.clone()
.account_id(chain.chain_info(), &previous_context.global_config)?;
let cw20_addr = Addr::unchecked(cw20_account_id);

let account_id = scope
.address
.clone()
.account_id(chain.chain_info(), &previous_context.global_config)?;

let daemon = chain.daemon_querier()?;

let balance: BalanceResponse = daemon.query(
&(cw20::Cw20QueryMsg::Balance {
address: account_id.to_string(),
}),
&cw20_addr,
)?;
println!("{}", serde_json::to_string_pretty(&balance)?);

Ok(QueryCw20Output)
}
}
47 changes: 47 additions & 0 deletions cw-orch-cli/src/commands/action/asset/query_native/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::types::{CliAddress, CliSkippable};

use super::CosmosContext;

use cw_orch::prelude::*;

#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
#[interactive_clap(input_context = CosmosContext)]
#[interactive_clap(output_context = QueryNativeOutput)]
pub struct QueryNativeCommands {
/// Input denom or leave empty to query all balances
denom: CliSkippable<String>,
/// Address or alias from address-book
address: CliAddress,
}

pub struct QueryNativeOutput;

impl QueryNativeOutput {
fn from_previous_context(
previous_context: CosmosContext,
scope: &<QueryNativeCommands as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope,
) -> color_eyre::eyre::Result<Self> {
let chain = previous_context.chain;
let denom = scope.denom.0.clone();

let account_id = scope
.address
.clone()
.account_id(chain.chain_info(), &previous_context.global_config)?;
let addr = Addr::unchecked(account_id);

let daemon = chain.daemon_querier()?;

if let Some(denom) = denom {
let balance = daemon.balance(&addr, Some(denom))?.swap_remove(0);
println!("balance: {balance}")
} else {
let balances = daemon.balance(&addr, None)?;
// `cosmwasm_std::Coins` have nice display
let coins = cosmwasm_std::Coins::try_from(balances).unwrap();
println!("balances: {coins}")
}

Ok(QueryNativeOutput)
}
}
66 changes: 66 additions & 0 deletions cw-orch-cli/src/commands/action/asset/send_cw20/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use crate::{
log::LogOutput,
types::{keys::seed_phrase_for_id, CliAddress},
};

use super::CosmosContext;

use cosmwasm_std::Uint128;
use cw_orch::prelude::*;

#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
#[interactive_clap(input_context = CosmosContext)]
#[interactive_clap(output_context = SendCw20Output)]
pub struct Cw20TransferCommands {
/// Cw20 Address or alias from address-book
cw20_address: CliAddress,
/// Cw20 Amount
amount: u128,
/// Recipient address or alias from address-book
to_address: CliAddress,
#[interactive_clap(skip_default_input_arg)]
signer: String,
}

impl Cw20TransferCommands {
fn input_signer(_context: &CosmosContext) -> color_eyre::eyre::Result<Option<String>> {
crate::common::select_signer()
}
}

pub struct SendCw20Output;

impl SendCw20Output {
fn from_previous_context(
previous_context: CosmosContext,
scope: &<Cw20TransferCommands as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope,
) -> color_eyre::eyre::Result<Self> {
let chain = previous_context.chain;

let to_address_account_id = scope
.to_address
.clone()
.account_id(chain.chain_info(), &previous_context.global_config)?;

let cw20_account_id = scope
.cw20_address
.clone()
.account_id(chain.chain_info(), &previous_context.global_config)?;
let cw20_addr = Addr::unchecked(cw20_account_id);

let seed = seed_phrase_for_id(&scope.signer)?;
let daemon = chain.daemon(seed)?;

let resp = daemon.execute(
&cw20::Cw20ExecuteMsg::Transfer {
recipient: to_address_account_id.to_string(),
amount: Uint128::new(scope.amount),
},
&[],
&cw20_addr,
)?;
resp.log(chain.chain_info());

Ok(SendCw20Output)
}
}
Loading

0 comments on commit 51edcce

Please sign in to comment.