From 75d71ddfa4a212bcc2747d07f6213b3d2b9cd5dc Mon Sep 17 00:00:00 2001 From: Maximiliano Duthey Date: Wed, 24 Sep 2025 17:12:39 -0300 Subject: [PATCH 1/8] feat: add tx3 examples and update tx3 dependencies --- Cargo.lock | 10 ++--- Cargo.toml | 6 +-- examples/README.md | 84 +++++++++++++++++++++++++++++++++++ examples/mint_token.tx3 | 27 +++++++++++ examples/transfer.tx3 | 21 +++++++++ examples/utxo_consolidate.tx3 | 15 +++++++ examples/utxo_split.tx3 | 24 ++++++++++ 7 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 examples/README.md create mode 100644 examples/mint_token.tx3 create mode 100644 examples/transfer.tx3 create mode 100644 examples/utxo_consolidate.tx3 create mode 100644 examples/utxo_split.tx3 diff --git a/Cargo.lock b/Cargo.lock index 140daec..38da55c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -607,7 +607,7 @@ checksum = "382ce8820a5bb815055d3553a610e8cb542b2d767bbacea99038afda96cd760d" [[package]] name = "cshell" -version = "0.11.1" +version = "0.12.0" dependencies = [ "anyhow", "backoff", @@ -3589,9 +3589,9 @@ dependencies = [ [[package]] name = "tx3-cardano" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5f14e3e1b68b1bff81d58c891604a3087f34be70c2a2ad58a8605b6f15ed82" +checksum = "9532e9d3e4f40b02b88023c082bd1e27c3e3467bb6a749f7b261e82712b0941c" dependencies = [ "hex", "pallas", @@ -3604,9 +3604,9 @@ dependencies = [ [[package]] name = "tx3-lang" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7607218726810f53079b50a63ff4b5e25a8ccf53a82a80d7b6cc1815feec7a65" +checksum = "f9ffdc30232bfe0bfeced3d48768b8f00ed9f3015d9e78323ecda4cf45cbbd56" dependencies = [ "bincode", "hex", diff --git a/Cargo.toml b/Cargo.toml index 7611b22..df81caa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,12 @@ repository = "https://github.com/txpipe/cshell" license = "Apache-2.0" homepage = "https://docs.txpipe.io/cshell" -keywords = ["cardano", "blockchain", "wallet", "cardano"] +keywords = ["cardano", "blockchain", "wallet"] categories = ["command-line-utilities", "blockchain", "cardano", "wallet"] [dependencies] -tx3-lang = "0.11.1" -tx3-cardano = "0.11.1" +tx3-lang = "0.11.5" +tx3-cardano = "0.11.5" tx3-sdk = "^0" # tx3-lang = { git = "https://github.com/tx3-lang/tx3.git" } diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..f994b98 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,84 @@ +# TX3 Transaction Examples + +This directory contains example TX3 files to execute through CShell invoke. + +## Prerequisites + +Before running TX3 files, ensure you have: + +1. **CShell installed** - See the main [README](../README.md) for installation instructions +2. **A CShell configuration file** - Created using `cshell provider create` and `cshell wallet create` +3. **At least one provider configured** - For blockchain connectivity +4. **At least one wallet configured** - For signing transactions + +## How to Execute TX3 Files + +Use the following command pattern to execute a TX3 file: + +```bash +cshell -s ./cshell.toml tx invoke --tx3-file ./ +``` + +### Command Breakdown + +- `cshell` - Runs CShell +- `-s ` - Specifies the CShell configuration file path +- `tx invoke` - Invokes a transaction +- `--tx3-file ` - Path to the TX3 file to execute + +### Configuration File + +The `-s` flag points to your CShell configuration file (usually `cshell.toml`), which contains: +- Provider settings +- Wallet configurations + +## Available Examples + +### 1. `transfer.tx3` - Basic ADA Transfer + +Transfers ADA from one party to another with change calculation. + +**Parties**: `Sender`, `Receiver` +**Parameters**: `quantity` (amount to transfer in lovelace) + +```bash +cshell -s ~/.tx3/tmp/devnet_756435378c8d3771/cshell.toml tx invoke --tx3-file ./transfer.tx3 +``` + +### 2. `utxo_consolidate.tx3` - UTXO Consolidation + +Combines multiple UTXOs into a single output to optimize wallet organization. + +**Parties**: `Wallet` +**Parameters**: `utxo_list` (list of UTXO references to consolidate) + +```bash +cshell -s ~/.tx3/tmp/devnet_756435378c8d3771/cshell.toml tx invoke --tx3-file ./utxo_consolidate.tx3 +``` + +### 3. `utxo_split.tx3` - UTXO Splitting + +Splits a single UTXO into multiple outputs of specified amounts. + +**Parties**: `Wallet` +**Parameters**: +- `input_utxo` (UTXO reference to split) +- `quantity` (amount for the new output) + +```bash +cshell -s ~/.tx3/tmp/devnet_756435378c8d3771/cshell.toml tx invoke --tx3-file ./utxo_split.tx3 +``` + +### 4. `mint_token.tx3` - Token Minting + +Creates new native tokens on Cardano. + +**Parties**: `Minter` +**Parameters**: +- `token_policy` (policy ID in bytes) +- `token_name` (token name in bytes) +- `quantity` (amount to mint) + +```bash +cshell -s ~/.tx3/tmp/devnet_756435378c8d3771/cshell.toml tx invoke --tx3-file ./mint_token.tx3 +``` diff --git a/examples/mint_token.tx3 b/examples/mint_token.tx3 new file mode 100644 index 0000000..c727785 --- /dev/null +++ b/examples/mint_token.tx3 @@ -0,0 +1,27 @@ +party Minter; + +tx mint_token ( + token_policy: Bytes, + token_name: Bytes, + quantity: Int, +) { + + locals { + token: AnyAsset(token_policy, token_name, quantity), + } + + mint { + amount: token, + redeemer: (), + } + + input source { + from: Minter, + min_amount: fees, + } + + output destination { + to: Minter, + amount: token - fees, + } +} \ No newline at end of file diff --git a/examples/transfer.tx3 b/examples/transfer.tx3 new file mode 100644 index 0000000..6fb975f --- /dev/null +++ b/examples/transfer.tx3 @@ -0,0 +1,21 @@ +party Sender; +party Receiver; + +tx transfer( + quantity: Int, +) { + input source { + from: Sender, + min_amount: Ada(quantity) + fees, + } + + output destination { + to: Receiver, + amount: Ada(quantity), + } + + output { + to: Sender, + amount: source - Ada(quantity) - fees, + } +} diff --git a/examples/utxo_consolidate.tx3 b/examples/utxo_consolidate.tx3 new file mode 100644 index 0000000..d1214d6 --- /dev/null +++ b/examples/utxo_consolidate.tx3 @@ -0,0 +1,15 @@ +party Wallet; + +tx utxo_consolidate( + utxo_list: List, // Not supported currently +) { + input * utxos { + from: Wallet, + ref: utxo_list, + } + + output consolidated { + to: Wallet, + amount: utxos - fees, + } +} diff --git a/examples/utxo_split.tx3 b/examples/utxo_split.tx3 new file mode 100644 index 0000000..2c12833 --- /dev/null +++ b/examples/utxo_split.tx3 @@ -0,0 +1,24 @@ +party Wallet; + +tx utxo_split( + input_utxo: UtxoRef, + quantity: Int, + // Received list of quantities and generate N outputs based on N quantities? +) { + input source { + from: Wallet, + ref: input_utxo, + min_amount: Ada(quantity) + fees, + } + + output { + to: Wallet, + amount: Ada(quantity), + } + + output change { + to: Wallet, + amount: source - Ada(quantity) - fees, + } +} + From b0f25567ce4995577268d2f9abae46d8f88ab0b9 Mon Sep 17 00:00:00 2001 From: Maximiliano Duthey Date: Fri, 26 Sep 2025 17:43:40 -0300 Subject: [PATCH 2/8] feat: add new construct command --- examples/utxo_consolidate.tx3 | 15 -- examples/utxo_split.tx3 | 24 --- src/tx/construct.rs | 333 ++++++++++++++++++++++++++++++++++ src/tx/mod.rs | 5 + 4 files changed, 338 insertions(+), 39 deletions(-) delete mode 100644 examples/utxo_consolidate.tx3 delete mode 100644 examples/utxo_split.tx3 create mode 100644 src/tx/construct.rs diff --git a/examples/utxo_consolidate.tx3 b/examples/utxo_consolidate.tx3 deleted file mode 100644 index d1214d6..0000000 --- a/examples/utxo_consolidate.tx3 +++ /dev/null @@ -1,15 +0,0 @@ -party Wallet; - -tx utxo_consolidate( - utxo_list: List, // Not supported currently -) { - input * utxos { - from: Wallet, - ref: utxo_list, - } - - output consolidated { - to: Wallet, - amount: utxos - fees, - } -} diff --git a/examples/utxo_split.tx3 b/examples/utxo_split.tx3 deleted file mode 100644 index 2c12833..0000000 --- a/examples/utxo_split.tx3 +++ /dev/null @@ -1,24 +0,0 @@ -party Wallet; - -tx utxo_split( - input_utxo: UtxoRef, - quantity: Int, - // Received list of quantities and generate N outputs based on N quantities? -) { - input source { - from: Wallet, - ref: input_utxo, - min_amount: Ada(quantity) + fees, - } - - output { - to: Wallet, - amount: Ada(quantity), - } - - output change { - to: Wallet, - amount: source - Ada(quantity) - fees, - } -} - diff --git a/src/tx/construct.rs b/src/tx/construct.rs new file mode 100644 index 0000000..ae28483 --- /dev/null +++ b/src/tx/construct.rs @@ -0,0 +1,333 @@ +use std::{fs, path::PathBuf, str::FromStr}; + +use anyhow::{Result, Context}; +use clap::Parser; +use pallas::ledger::addresses::Address; +use serde_json::json; +use tracing::instrument; +use tx3_lang::Protocol; +use inquire::{Text, Confirm}; + +#[derive(Parser, Clone)] +pub struct Args { + /// Path for tx3 file to create the transaction + #[arg(long)] + tx3_file: PathBuf, +} + +struct TransactionBuilder { + ast: tx3_lang::ast::Program, + def_index: usize, +} + +#[instrument("construct", skip_all)] +pub async fn run(args: Args, _ctx: &crate::Context) -> Result<()> { + tracing::debug!("Creating transaction from {}", args.tx3_file.display()); + let ast_path_buf = args.tx3_file.with_extension("ast"); + + // let ast = if args.tx3_file.exists() { + // let protocol = Protocol::from_file(&args.tx3_file) + // .load() + // .context("Failed to load existing tx3 file")?; + // protocol.ast().clone() + // } else if ast_path_buf.exists() { + // let ast_content = fs::read_to_string(&ast_path_buf) + // .context("Failed to read existing AST file")?; + + // dbg!("Loaded existing AST from {}", ast_path_buf.display()); + + // serde_json::from_str(&ast_content) + // .context("Failed to parse existing AST file")? + // } else { + // tx3_lang::ast::Program::default() + // }; + + let ast = if ast_path_buf.exists() { + let ast_content = fs::read_to_string(&ast_path_buf) + .context("Failed to read existing AST file")?; + + dbg!("Loaded existing AST from {}", ast_path_buf.display()); + + serde_json::from_str(&ast_content) + .context("Failed to parse existing AST file")? + } else { + tx3_lang::ast::Program::default() + }; + + dbg!("Initial AST: {:#?}", &ast); + + let mut tx_builder = TransactionBuilder::new("new_transaction".to_string(), ast); + + tx_builder.collect_inputs()?; + + tx_builder.collect_outputs()?; + + let ast = tx_builder.ast.clone(); + + // Generate the tx3 content + let tx3_content = tx_builder.generate_tx3_content(); + + // Write to file + fs::write(&args.tx3_file, tx3_content) + .context("Failed to write tx3 file")?; + + fs::write(ast_path_buf, serde_json::to_string_pretty(&ast).unwrap()) + .context("Failed to write tx3 AST file")?; + + println!("\nāœ… Transaction created successfully!"); + println!("šŸ“„ File saved to: {}", args.tx3_file.display()); + + Ok(()) +} + +impl TransactionBuilder { + fn new(name: String, mut ast: tx3_lang::ast::Program) -> Self { + let mut def_index = ast.txs.iter().position(|tx| tx.name.value == name); + + if def_index.is_none() { + println!("Creating new transaction: {}", name); + // Create it as JSON and parse it as TxDef + // TODO: Make scope pub in tx3_lang and construct directly or implement `Default` + let value = json!({ + "name": tx3_lang::ast::Identifier::new(name), + "parameters": tx3_lang::ast::ParameterList { + parameters: Vec::new(), + span: tx3_lang::ast::Span::default(), + }, + "references": [], + "inputs": [], + "outputs": [], + "mints": [], + "burns": [], + "adhoc": [], + "span": tx3_lang::ast::Span::default(), + "collateral": [], + }); + ast.txs.push(serde_json::from_value(value).unwrap()); + + def_index = Some(ast.txs.len() - 1); + } + + Self { + ast: ast.clone(), + def_index: def_index.unwrap(), + } + } + + fn collect_inputs(&mut self) -> Result<()> { + println!("\nšŸ“„ Transaction Inputs"); + println!("===================="); + + let add_inputs = Confirm::new("Do you want to add inputs to your transaction?") + .with_default(true) + .prompt()?; + + if !add_inputs { + return Ok(()); + } + + loop { + let input_name = Text::new("Input name:") + .with_help_message("Enter input name (or 'done' to finish)") + .prompt()?; + + if input_name.eq_ignore_ascii_case("done") { + break; + } + + let mut input_block = tx3_lang::ast::InputBlock { + name: input_name.clone(), + span: tx3_lang::ast::Span::default(), + many: false, + fields: Vec::new(), + }; + + let from_address = Text::new("From address:") + .with_help_message("Enter the address this input comes from") + .prompt()?; + + // Validate address + let address = Address::from_str(&from_address) + .context("Invalid address")?; + + input_block.fields.push(tx3_lang::ast::InputBlockField::From( + tx3_lang::ast::DataExpr::String(tx3_lang::ast::StringLiteral::new(address.to_bech32().unwrap())), + )); + + let min_amount = Text::new("Minimum amount value:") + .with_default("1000000") + .prompt()?; + + input_block.fields.push(tx3_lang::ast::InputBlockField::MinAmount( + tx3_lang::ast::DataExpr::StaticAssetConstructor(tx3_lang::ast::StaticAssetConstructor { + amount: Box::new(tx3_lang::ast::DataExpr::Number(min_amount.parse::().unwrap())), + span: tx3_lang::ast::Span::default(), + r#type: tx3_lang::ast::Identifier::new("Ada".to_string()), + }) + )); + + self.ast.txs[self.def_index].inputs.push(input_block); + + let add_more = Confirm::new("Add another input?") + .with_default(false) + .prompt()?; + + if !add_more { + break; + } + } + + Ok(()) + } + + fn collect_outputs(&mut self) -> Result<()> { + println!("\nšŸ“¤ Transaction Outputs"); + println!("====================="); + + let add_outputs = Confirm::new("Do you want to add outputs to your transaction?") + .with_default(true) + .prompt()?; + + if !add_outputs { + return Ok(()); + } + + loop { + let has_name = Confirm::new("Does this output have a name?") + .with_default(true) + .prompt()?; + + let output_name = if has_name { + Some(Text::new("Output name:") + .with_help_message("Enter output name") + .prompt()?) + } else { + None + }; + + let mut output_block = tx3_lang::ast::OutputBlock { + name: if let Some(name) = &output_name { + Some(tx3_lang::ast::Identifier::new(name.clone())) + } else { + None + }, + span: tx3_lang::ast::Span::default(), + fields: Vec::new(), + }; + + let to_address = Text::new("To address:") + .with_help_message("Enter the address this output goes to") + .prompt()?; + + // Validate address + let address = Address::from_str(&to_address) + .context("Invalid address")?; + + output_block.fields.push(tx3_lang::ast::OutputBlockField::To( + Box::new(tx3_lang::ast::DataExpr::String(tx3_lang::ast::StringLiteral::new(address.to_bech32().unwrap()))), + )); + + let amount = Text::new("Amount:") + .with_default("1000000") + .prompt()?; + + output_block.fields.push(tx3_lang::ast::OutputBlockField::Amount( + Box::new(tx3_lang::ast::DataExpr::StaticAssetConstructor(tx3_lang::ast::StaticAssetConstructor { + amount: Box::new(tx3_lang::ast::DataExpr::Number(amount.parse::().unwrap())), + span: tx3_lang::ast::Span::default(), + r#type: tx3_lang::ast::Identifier::new("Ada".to_string()), + })) + )); + + self.ast.txs[self.def_index].outputs.push(output_block); + + + let add_more = Confirm::new("Add another output?") + .with_default(true) + .prompt()?; + + if !add_more { + break; + } + } + + Ok(()) + } + + fn generate_tx3_content(self) -> String { + let mut content = String::new(); + + // Add transaction + content.push_str(&format!("tx {}() {{\n", self.ast.txs[self.def_index].name.value)); + + // Add inputs + for input in &self.ast.txs[self.def_index].inputs { + content.push_str(&format!("\tinput {} {{\n", input.name)); + input.fields.iter().for_each(|field| { + match field { + tx3_lang::ast::InputBlockField::From(expr) => { + match expr { + tx3_lang::ast::DataExpr::String(literal) => { + content.push_str(&format!("\t\tfrom: \"{}\",\n", literal.value)); + } + _ => {} + } + }, + tx3_lang::ast::InputBlockField::MinAmount(expr) => { + match expr { + tx3_lang::ast::DataExpr::StaticAssetConstructor(constructor) => { + let amount = match *constructor.amount { + tx3_lang::ast::DataExpr::Number(num) => num.to_string(), + _ => "unknown".to_string(), + }; + content.push_str(&format!("\t\tmin_amount: {}({}),\n", constructor.r#type.value, amount)); + } + _ => {} + } + }, + _ => {} + } + }); + content.push_str("\t}\n\n"); + } + + // Add outputs + for output in &self.ast.txs[self.def_index].outputs { + if let Some(name) = &output.name { + content.push_str(&format!("\toutput {} {{\n", name.value)); + } else { + content.push_str("\toutput {\n"); + } + + output.fields.iter().for_each(|field| { + match field { + tx3_lang::ast::OutputBlockField::To(expr) => { + match expr.as_ref() { + tx3_lang::ast::DataExpr::String(literal) => { + content.push_str(&format!("\t\tto: \"{}\",\n", literal.value)); + } + _ => {} + } + }, + tx3_lang::ast::OutputBlockField::Amount(expr) => { + match expr.as_ref() { + tx3_lang::ast::DataExpr::StaticAssetConstructor(constructor) => { + let amount = match *constructor.amount { + tx3_lang::ast::DataExpr::Number(num) => num.to_string(), + _ => "unknown".to_string(), + }; + content.push_str(&format!("\t\tamount: {}({}),\n", constructor.r#type.value, amount)); + } + _ => {} + } + }, + _ => {} + } + }); + content.push_str("\t}\n\n"); + } + + content.push_str("}\n"); + content + } +} diff --git a/src/tx/mod.rs b/src/tx/mod.rs index bef0ea8..da9bd82 100644 --- a/src/tx/mod.rs +++ b/src/tx/mod.rs @@ -7,6 +7,7 @@ mod invoke; mod resolve; mod sign; mod submit; +mod construct; #[derive(Parser)] pub struct Args { @@ -27,6 +28,9 @@ enum Commands { /// Submit a CBOR transaction Submit(submit::Args), + + /// Construct a new transaction using tx3 + Construct(construct::Args), } #[instrument("transaction", skip_all)] @@ -36,5 +40,6 @@ pub async fn run(args: Args, ctx: &crate::Context) -> anyhow::Result<()> { Commands::Resolve(args) => resolve::run(args, ctx).await, Commands::Sign(args) => sign::run(args, ctx).await, Commands::Submit(args) => submit::run(args, ctx).await, + Commands::Construct(args) => construct::run(args, ctx).await, } } From 48ec7dd7b946c58479a239ac1620f2e813cddf4e Mon Sep 17 00:00:00 2001 From: Maximiliano Duthey Date: Tue, 30 Sep 2025 10:21:38 -0300 Subject: [PATCH 3/8] feat: update TX3 examples for token minting and add Plutus script example --- examples/README.md | 36 ++++++++++++----------------------- examples/mint_token.tx3 | 12 ++++++------ examples/mint_with_script.tx3 | 34 +++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 30 deletions(-) create mode 100644 examples/mint_with_script.tx3 diff --git a/examples/README.md b/examples/README.md index f994b98..f83a34c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -45,40 +45,28 @@ Transfers ADA from one party to another with change calculation. cshell -s ~/.tx3/tmp/devnet_756435378c8d3771/cshell.toml tx invoke --tx3-file ./transfer.tx3 ``` -### 2. `utxo_consolidate.tx3` - UTXO Consolidation +### 2. `mint_token.tx3` - Token Minting -Combines multiple UTXOs into a single output to optimize wallet organization. - -**Parties**: `Wallet` -**Parameters**: `utxo_list` (list of UTXO references to consolidate) - -```bash -cshell -s ~/.tx3/tmp/devnet_756435378c8d3771/cshell.toml tx invoke --tx3-file ./utxo_consolidate.tx3 -``` - -### 3. `utxo_split.tx3` - UTXO Splitting - -Splits a single UTXO into multiple outputs of specified amounts. +Creates new native tokens on Cardano. -**Parties**: `Wallet` +**Parties**: `Minter` **Parameters**: -- `input_utxo` (UTXO reference to split) -- `quantity` (amount for the new output) +- `token_policy` (policy ID in bytes) +- `token_name` (token name in bytes) +- `quantity` (amount to mint) ```bash -cshell -s ~/.tx3/tmp/devnet_756435378c8d3771/cshell.toml tx invoke --tx3-file ./utxo_split.tx3 +cshell -s ~/.tx3/tmp/devnet_756435378c8d3771/cshell.toml tx invoke --tx3-file ./mint_token.tx3 ``` -### 4. `mint_token.tx3` - Token Minting +### 3. `mint_with_script.tx3` - Plutus Script Token Minting -Creates new native tokens on Cardano. +Demonstrates token minting using a Plutus v3 script with a PIN-based vending machine mechanism. This example shows how to interact with smart contracts for token minting. -**Parties**: `Minter` +**Parties**: `Customer` **Parameters**: -- `token_policy` (policy ID in bytes) -- `token_name` (token name in bytes) -- `quantity` (amount to mint) +- `pin` (PIN code in bytes) ```bash -cshell -s ~/.tx3/tmp/devnet_756435378c8d3771/cshell.toml tx invoke --tx3-file ./mint_token.tx3 +cshell -s ~/.tx3/tmp/devnet_756435378c8d3771/cshell.toml tx invoke --tx3-file ./mint_with_script.tx3 ``` diff --git a/examples/mint_token.tx3 b/examples/mint_token.tx3 index c727785..722cdff 100644 --- a/examples/mint_token.tx3 +++ b/examples/mint_token.tx3 @@ -10,18 +10,18 @@ tx mint_token ( token: AnyAsset(token_policy, token_name, quantity), } - mint { - amount: token, - redeemer: (), - } - input source { from: Minter, min_amount: fees, } + mint { + amount: token, + redeemer: (), + } + output destination { to: Minter, - amount: token - fees, + amount: source + token - fees, } } \ No newline at end of file diff --git a/examples/mint_with_script.tx3 b/examples/mint_with_script.tx3 new file mode 100644 index 0000000..43ce50f --- /dev/null +++ b/examples/mint_with_script.tx3 @@ -0,0 +1,34 @@ +party Customer; + +policy VendingMachine = 0x3f2e288b4a19ab51d1775d2c6e397b43fad17cc8c47b988603136d73; + +asset COIN = 0x3f2e288b4a19ab51d1775d2c6e397b43fad17cc8c47b988603136d73."COIN"; + +tx claim_tokens( + pin: Bytes, +) { + input gas { + from: Customer, + min_amount: fees, + } + + collateral { + from: Customer, + min_amount: fees, + } + + mint { + amount: COIN(1), + redeemer: pin, + } + + output { + to: Customer, + amount: gas - fees + COIN(1), + } + + cardano::plutus_witness { + version: 3, + script: 0x5903e501010029800aba4aba2aba1aab9faab9eaab9dab9cab9a488888888c96600264653001300900198049805000cc0240092225980099b8748000c024dd500144c966002009007803c01e00f1323233223233225330103372c9210e70726f76696465642070696e3a20003732660046ea40052201001325330113372c92011370726f76696465642070696e20686173683a20003732660066ea400522010013371e0029111c4f43be03892183d6ea41480f616be43b58937aad2c7511e71be9f80d00379000260020026eb8024888c9660020071323233223300a00233714910101280059800800c4cdc52441035b5d2900006899b8a489035b5f20009800800ccdc52441025d2900006914c00402a00530070014029229800805400a002805100920305980099b880014803a266e0120f2010018acc004cdc4000a41000513370066e01208014001480362c80910121bac3015002375a60260026466ec0dd418098009ba73014001375400713259800800c4cdc52441027b7d00003899b8a489037b5f20003232330010010032259800800c400e264b30010018994c00402a6030003337149101023a200098008054c06400600a805100a180d00144ca6002015301800199b8a489023a200098008054c064006600e66008008004805100a180d0012032301a001406066e29220102207d0000340546eac00e264b3001001899b8a489025b5d00003899b8a489035b5f20009800800ccdc52441015d00003914c00401e0053004001401d229800803c00a0028039006202a3758007133006375a0060051323371491102682700329800800ccdc01b8d0024800666e292210127000044004444b3001337100049000440062646645300100699b800054800666e2ccdc00012cc004cdc40012402914818229037202e3371666e000056600266e2000520148a40c11481b9017002200c33706002901019b8600148080cdc70020012028375c00680b8dc5245022c2000223233001001003225980099b8700148002266e292210130000038acc004cdc4000a40011337149101012d0033002002337029000000c4cc014cdc2000a402866e2ccdc019b85001480512060003403880708888c8cc004004014896600200310058992cc004006266008602c00400d1330053016002330030030014054602c00280a0c0040048896600266e2400920008800c6600200733708004900a4cdc599b803370a004900a240c0002801900b201e375c601860146ea800a29410070c024004c014dd500545268a99801a4811856616c696461746f722072657475726e65642066616c7365001365640082a660049201206578706563742070696e3a20427974654172726179203d2072656465656d6572001601, + } +} \ No newline at end of file From 764e77858f6d6ad786a7817b39981f1ee19fe56f Mon Sep 17 00:00:00 2001 From: Maximiliano Duthey Date: Tue, 30 Sep 2025 10:23:18 -0300 Subject: [PATCH 4/8] feat: implement transaction construction commands with input, output, and build functionalities --- src/tx/construct/add_input.rs | 32 ++++ src/tx/construct/add_output.rs | 32 ++++ src/tx/construct/build.rs | 31 ++++ src/tx/{construct.rs => construct/common.rs} | 162 ++++++++----------- src/tx/construct/mod.rs | 41 +++++ src/tx/construct/wizard.rs | 53 ++++++ 6 files changed, 255 insertions(+), 96 deletions(-) create mode 100644 src/tx/construct/add_input.rs create mode 100644 src/tx/construct/add_output.rs create mode 100644 src/tx/construct/build.rs rename src/tx/{construct.rs => construct/common.rs} (72%) create mode 100644 src/tx/construct/mod.rs create mode 100644 src/tx/construct/wizard.rs diff --git a/src/tx/construct/add_input.rs b/src/tx/construct/add_input.rs new file mode 100644 index 0000000..a0e49a4 --- /dev/null +++ b/src/tx/construct/add_input.rs @@ -0,0 +1,32 @@ +use std::{fs, path::PathBuf}; + +use anyhow::{Result, Context}; +use clap::Parser; +use tracing::instrument; + +#[derive(Parser, Clone)] +pub struct Args { + /// Path for tx3 file to create the transaction + #[arg(long)] + tx3_file: PathBuf, +} + +#[instrument("add-input", skip_all)] +pub async fn run(args: Args, _ctx: &crate::Context) -> Result<()> { + let ast_path_buf = args.tx3_file.with_extension("ast"); + + let mut tx_builder = super::common::TransactionBuilder::from_ast(&ast_path_buf)?; + + tx_builder.collect_inputs(true)?; + + let ast = tx_builder.ast.clone(); + + // Write to AST file + fs::write(&ast_path_buf, serde_json::to_string_pretty(&ast).unwrap()) + .context("Failed to write tx3 AST file")?; + + println!("\nāœ… Input added successfully!"); + println!("šŸ“„ File saved to: {}", ast_path_buf.display()); + + Ok(()) +} \ No newline at end of file diff --git a/src/tx/construct/add_output.rs b/src/tx/construct/add_output.rs new file mode 100644 index 0000000..7f00eb6 --- /dev/null +++ b/src/tx/construct/add_output.rs @@ -0,0 +1,32 @@ +use std::{fs, path::PathBuf}; + +use anyhow::{Result, Context}; +use clap::Parser; +use tracing::instrument; + +#[derive(Parser, Clone)] +pub struct Args { + /// Path for tx3 file to create the transaction + #[arg(long)] + tx3_file: PathBuf, +} + +#[instrument("add-output", skip_all)] +pub async fn run(args: Args, _ctx: &crate::Context) -> Result<()> { + let ast_path_buf = args.tx3_file.with_extension("ast"); + + let mut tx_builder = super::common::TransactionBuilder::from_ast(&ast_path_buf)?; + + tx_builder.collect_outputs(true)?; + + let ast = tx_builder.ast.clone(); + + // Write to AST file + fs::write(&ast_path_buf, serde_json::to_string_pretty(&ast).unwrap()) + .context("Failed to write tx3 AST file")?; + + println!("\nāœ… Output added successfully!"); + println!("šŸ“„ File saved to: {}", ast_path_buf.display()); + + Ok(()) +} \ No newline at end of file diff --git a/src/tx/construct/build.rs b/src/tx/construct/build.rs new file mode 100644 index 0000000..6f6f36b --- /dev/null +++ b/src/tx/construct/build.rs @@ -0,0 +1,31 @@ +use std::{fs, path::PathBuf}; + +use anyhow::{Result, Context}; +use clap::Parser; +use tracing::instrument; + +#[derive(Parser, Clone)] +pub struct Args { + /// Path for tx3 file to create the transaction + #[arg(long)] + tx3_file: PathBuf, +} + +#[instrument("build", skip_all)] +pub async fn run(args: Args, _ctx: &crate::Context) -> Result<()> { + let ast_path_buf = args.tx3_file.with_extension("ast"); + + let tx_builder = super::common::TransactionBuilder::from_ast(&ast_path_buf)?; + + // Generate the tx3 content + let tx3_content = tx_builder.generate_tx3_content(); + + // Write to file + fs::write(&args.tx3_file, tx3_content) + .context("Failed to write tx3 file")?; + + println!("\nāœ… Transaction created successfully!"); + println!("šŸ“„ File saved to: {}", args.tx3_file.display()); + + Ok(()) +} \ No newline at end of file diff --git a/src/tx/construct.rs b/src/tx/construct/common.rs similarity index 72% rename from src/tx/construct.rs rename to src/tx/construct/common.rs index ae28483..d27eb7d 100644 --- a/src/tx/construct.rs +++ b/src/tx/construct/common.rs @@ -1,87 +1,20 @@ + + use std::{fs, path::PathBuf, str::FromStr}; use anyhow::{Result, Context}; -use clap::Parser; +use hex::ToHex; use pallas::ledger::addresses::Address; use serde_json::json; -use tracing::instrument; -use tx3_lang::Protocol; use inquire::{Text, Confirm}; -#[derive(Parser, Clone)] -pub struct Args { - /// Path for tx3 file to create the transaction - #[arg(long)] - tx3_file: PathBuf, -} - -struct TransactionBuilder { - ast: tx3_lang::ast::Program, - def_index: usize, -} - -#[instrument("construct", skip_all)] -pub async fn run(args: Args, _ctx: &crate::Context) -> Result<()> { - tracing::debug!("Creating transaction from {}", args.tx3_file.display()); - let ast_path_buf = args.tx3_file.with_extension("ast"); - - // let ast = if args.tx3_file.exists() { - // let protocol = Protocol::from_file(&args.tx3_file) - // .load() - // .context("Failed to load existing tx3 file")?; - // protocol.ast().clone() - // } else if ast_path_buf.exists() { - // let ast_content = fs::read_to_string(&ast_path_buf) - // .context("Failed to read existing AST file")?; - - // dbg!("Loaded existing AST from {}", ast_path_buf.display()); - - // serde_json::from_str(&ast_content) - // .context("Failed to parse existing AST file")? - // } else { - // tx3_lang::ast::Program::default() - // }; - - let ast = if ast_path_buf.exists() { - let ast_content = fs::read_to_string(&ast_path_buf) - .context("Failed to read existing AST file")?; - - dbg!("Loaded existing AST from {}", ast_path_buf.display()); - - serde_json::from_str(&ast_content) - .context("Failed to parse existing AST file")? - } else { - tx3_lang::ast::Program::default() - }; - - dbg!("Initial AST: {:#?}", &ast); - - let mut tx_builder = TransactionBuilder::new("new_transaction".to_string(), ast); - - tx_builder.collect_inputs()?; - - tx_builder.collect_outputs()?; - - let ast = tx_builder.ast.clone(); - - // Generate the tx3 content - let tx3_content = tx_builder.generate_tx3_content(); - - // Write to file - fs::write(&args.tx3_file, tx3_content) - .context("Failed to write tx3 file")?; - - fs::write(ast_path_buf, serde_json::to_string_pretty(&ast).unwrap()) - .context("Failed to write tx3 AST file")?; - - println!("\nāœ… Transaction created successfully!"); - println!("šŸ“„ File saved to: {}", args.tx3_file.display()); - - Ok(()) +pub struct TransactionBuilder { + pub ast: tx3_lang::ast::Program, + pub def_index: usize, } impl TransactionBuilder { - fn new(name: String, mut ast: tx3_lang::ast::Program) -> Self { + pub fn new(name: String, mut ast: tx3_lang::ast::Program) -> Self { let mut def_index = ast.txs.iter().position(|tx| tx.name.value == name); if def_index.is_none() { @@ -114,18 +47,35 @@ impl TransactionBuilder { } } - fn collect_inputs(&mut self) -> Result<()> { + pub fn from_ast(ast_path_buf: &PathBuf) -> Result { + let ast = if ast_path_buf.exists() { + let ast_content = fs::read_to_string(&ast_path_buf) + .context("Failed to read existing AST file")?; + + serde_json::from_str(&ast_content) + .context("Failed to parse existing AST file")? + } else { + tx3_lang::ast::Program::default() + }; + + Ok(TransactionBuilder::new("new_transaction".to_string(), ast)) + } + + pub fn collect_inputs(&mut self, skip_question: bool) -> Result<()> { println!("\nšŸ“„ Transaction Inputs"); println!("===================="); - let add_inputs = Confirm::new("Do you want to add inputs to your transaction?") - .with_default(true) - .prompt()?; - - if !add_inputs { - return Ok(()); + if !skip_question { + let add_inputs = Confirm::new("Do you want to add inputs to your transaction?") + .with_default(true) + .prompt()?; + + if !add_inputs { + return Ok(()); + } } + loop { let input_name = Text::new("Input name:") .with_help_message("Enter input name (or 'done' to finish)") @@ -142,16 +92,26 @@ impl TransactionBuilder { fields: Vec::new(), }; - let from_address = Text::new("From address:") - .with_help_message("Enter the address this input comes from") + let utxo_ref = Text::new("Utxo Ref:") + .with_help_message("Enter the Utxo for this input (txid#index)") .prompt()?; - // Validate address - let address = Address::from_str(&from_address) - .context("Invalid address")?; + let parts: Vec<&str> = utxo_ref.split('#').collect(); + if parts.len() != 2 { + println!("Invalid Utxo Ref format. Expected format: txid#index"); + continue; + } - input_block.fields.push(tx3_lang::ast::InputBlockField::From( - tx3_lang::ast::DataExpr::String(tx3_lang::ast::StringLiteral::new(address.to_bech32().unwrap())), + + // input_block.fields.push(tx3_lang::ast::InputBlockField::From( + // tx3_lang::ast::DataExpr::String(tx3_lang::ast::StringLiteral::new(address.to_bech32().unwrap())), + // )); + input_block.fields.push(tx3_lang::ast::InputBlockField::Ref( + tx3_lang::ast::DataExpr::UtxoRef(tx3_lang::ast::UtxoRef { + txid: hex::decode(parts[0]).unwrap(), + index: parts[1].parse::().unwrap(), + span: tx3_lang::ast::Span::default(), + }), )); let min_amount = Text::new("Minimum amount value:") @@ -180,16 +140,18 @@ impl TransactionBuilder { Ok(()) } - fn collect_outputs(&mut self) -> Result<()> { + pub fn collect_outputs(&mut self, skip_question: bool) -> Result<()> { println!("\nšŸ“¤ Transaction Outputs"); println!("====================="); - let add_outputs = Confirm::new("Do you want to add outputs to your transaction?") - .with_default(true) - .prompt()?; - - if !add_outputs { - return Ok(()); + if !skip_question { + let add_outputs = Confirm::new("Do you want to add outputs to your transaction?") + .with_default(true) + .prompt()?; + + if !add_outputs { + return Ok(()); + } } loop { @@ -254,7 +216,7 @@ impl TransactionBuilder { Ok(()) } - fn generate_tx3_content(self) -> String { + pub fn generate_tx3_content(self) -> String { let mut content = String::new(); // Add transaction @@ -273,6 +235,14 @@ impl TransactionBuilder { _ => {} } }, + tx3_lang::ast::InputBlockField::Ref(expr) => { + match expr { + tx3_lang::ast::DataExpr::UtxoRef(utxoref) => { + content.push_str(&format!("\t\tref: 0x{}#{},\n", hex::encode(&utxoref.txid), utxoref.index)); + } + _ => {} + } + }, tx3_lang::ast::InputBlockField::MinAmount(expr) => { match expr { tx3_lang::ast::DataExpr::StaticAssetConstructor(constructor) => { diff --git a/src/tx/construct/mod.rs b/src/tx/construct/mod.rs new file mode 100644 index 0000000..9c356de --- /dev/null +++ b/src/tx/construct/mod.rs @@ -0,0 +1,41 @@ +use clap::{Parser, Subcommand}; +use tracing::instrument; + +mod wizard; +mod common; +mod add_input; +mod add_output; +mod build; + +#[derive(Parser)] +pub struct Args { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Generate a tx3 transaction using a wizard + Wizard(wizard::Args), + + /// Add input to transaction + #[command(name = "add-input")] + AddInput(add_input::Args), + + /// Add output to transaction + #[command(name = "add-output")] + AddOutput(add_output::Args), + + /// Build the transaction + Build(build::Args), +} + +#[instrument("construct", skip_all)] +pub async fn run(args: Args, ctx: &crate::Context) -> anyhow::Result<()> { + match args.command { + Commands::Wizard(args) => wizard::run(args, ctx).await, + Commands::AddInput(args) => add_input::run(args, ctx).await, + Commands::AddOutput(args) => add_output::run(args, ctx).await, + Commands::Build(args) => build::run(args, ctx).await, + } +} diff --git a/src/tx/construct/wizard.rs b/src/tx/construct/wizard.rs new file mode 100644 index 0000000..ab87982 --- /dev/null +++ b/src/tx/construct/wizard.rs @@ -0,0 +1,53 @@ +use std::{fs, path::PathBuf}; + +use anyhow::{Result, Context}; +use clap::Parser; +use tracing::instrument; +use inquire::Confirm; + +#[derive(Parser, Clone)] +pub struct Args { + /// Path for tx3 file to create the transaction + #[arg(long)] + tx3_file: PathBuf, +} + +#[instrument("wizard", skip_all)] +pub async fn run(args: Args, _ctx: &crate::Context) -> Result<()> { + let ast_path_buf = args.tx3_file.with_extension("ast"); + + if args.tx3_file.exists() { + println!("āš ļø Warning: The specified tx3 file already exists and will be overwritten."); + let proceed = Confirm::new("Do you want to continue?") + .with_default(false) + .prompt()?; + + if !proceed { + println!("Operation cancelled by user."); + return Ok(()); + } + } + + let mut tx_builder = super::common::TransactionBuilder::from_ast(&ast_path_buf)?; + + tx_builder.collect_inputs(false)?; + + tx_builder.collect_outputs(false)?; + + let ast = tx_builder.ast.clone(); + + // Generate the tx3 content + let tx3_content = tx_builder.generate_tx3_content(); + + // Write to file + fs::write(&args.tx3_file, tx3_content) + .context("Failed to write tx3 file")?; + + fs::write(ast_path_buf, serde_json::to_string_pretty(&ast).unwrap()) + .context("Failed to write tx3 AST file")?; + + println!("\nāœ… Transaction created successfully!"); + println!("šŸ“„ File saved to: {}", args.tx3_file.display()); + + Ok(()) +} From 42d820adbf808f7ebd7d843750cef3210df89f00 Mon Sep 17 00:00:00 2001 From: Maximiliano Duthey Date: Tue, 30 Sep 2025 10:55:44 -0300 Subject: [PATCH 5/8] ci: fix clippy warnings --- src/tx/construct/common.rs | 72 ++++++++++++++------------------------ 1 file changed, 26 insertions(+), 46 deletions(-) diff --git a/src/tx/construct/common.rs b/src/tx/construct/common.rs index d27eb7d..0d558b3 100644 --- a/src/tx/construct/common.rs +++ b/src/tx/construct/common.rs @@ -3,7 +3,6 @@ use std::{fs, path::PathBuf, str::FromStr}; use anyhow::{Result, Context}; -use hex::ToHex; use pallas::ledger::addresses::Address; use serde_json::json; use inquire::{Text, Confirm}; @@ -49,7 +48,7 @@ impl TransactionBuilder { pub fn from_ast(ast_path_buf: &PathBuf) -> Result { let ast = if ast_path_buf.exists() { - let ast_content = fs::read_to_string(&ast_path_buf) + let ast_content = fs::read_to_string(ast_path_buf) .context("Failed to read existing AST file")?; serde_json::from_str(&ast_content) @@ -168,11 +167,7 @@ impl TransactionBuilder { }; let mut output_block = tx3_lang::ast::OutputBlock { - name: if let Some(name) = &output_name { - Some(tx3_lang::ast::Identifier::new(name.clone())) - } else { - None - }, + name: output_name.as_ref().map(|name| tx3_lang::ast::Identifier::new(name.clone())), span: tx3_lang::ast::Span::default(), fields: Vec::new(), }; @@ -227,33 +222,24 @@ impl TransactionBuilder { content.push_str(&format!("\tinput {} {{\n", input.name)); input.fields.iter().for_each(|field| { match field { - tx3_lang::ast::InputBlockField::From(expr) => { - match expr { - tx3_lang::ast::DataExpr::String(literal) => { - content.push_str(&format!("\t\tfrom: \"{}\",\n", literal.value)); - } - _ => {} - } + tx3_lang::ast::InputBlockField::From( + tx3_lang::ast::DataExpr::String(literal) + ) => { + content.push_str(&format!("\t\tfrom: \"{}\",\n", literal.value)); }, - tx3_lang::ast::InputBlockField::Ref(expr) => { - match expr { - tx3_lang::ast::DataExpr::UtxoRef(utxoref) => { - content.push_str(&format!("\t\tref: 0x{}#{},\n", hex::encode(&utxoref.txid), utxoref.index)); - } - _ => {} - } + tx3_lang::ast::InputBlockField::Ref( + tx3_lang::ast::DataExpr::UtxoRef(utxoref) + ) => { + content.push_str(&format!("\t\tref: 0x{}#{},\n", hex::encode(&utxoref.txid), utxoref.index)); }, - tx3_lang::ast::InputBlockField::MinAmount(expr) => { - match expr { - tx3_lang::ast::DataExpr::StaticAssetConstructor(constructor) => { - let amount = match *constructor.amount { - tx3_lang::ast::DataExpr::Number(num) => num.to_string(), - _ => "unknown".to_string(), - }; - content.push_str(&format!("\t\tmin_amount: {}({}),\n", constructor.r#type.value, amount)); - } - _ => {} - } + tx3_lang::ast::InputBlockField::MinAmount( + tx3_lang::ast::DataExpr::StaticAssetConstructor(constructor) + ) => { + let amount = match *constructor.amount { + tx3_lang::ast::DataExpr::Number(num) => num.to_string(), + _ => "unknown".to_string(), + }; + content.push_str(&format!("\t\tmin_amount: {}({}),\n", constructor.r#type.value, amount)); }, _ => {} } @@ -272,23 +258,17 @@ impl TransactionBuilder { output.fields.iter().for_each(|field| { match field { tx3_lang::ast::OutputBlockField::To(expr) => { - match expr.as_ref() { - tx3_lang::ast::DataExpr::String(literal) => { - content.push_str(&format!("\t\tto: \"{}\",\n", literal.value)); - } - _ => {} + if let tx3_lang::ast::DataExpr::String(literal) = expr.as_ref() { + content.push_str(&format!("\t\tto: \"{}\",\n", literal.value)); } }, tx3_lang::ast::OutputBlockField::Amount(expr) => { - match expr.as_ref() { - tx3_lang::ast::DataExpr::StaticAssetConstructor(constructor) => { - let amount = match *constructor.amount { - tx3_lang::ast::DataExpr::Number(num) => num.to_string(), - _ => "unknown".to_string(), - }; - content.push_str(&format!("\t\tamount: {}({}),\n", constructor.r#type.value, amount)); - } - _ => {} + if let tx3_lang::ast::DataExpr::StaticAssetConstructor(constructor) = expr.as_ref() { + let amount = match *constructor.amount { + tx3_lang::ast::DataExpr::Number(num) => num.to_string(), + _ => "unknown".to_string(), + }; + content.push_str(&format!("\t\tamount: {}({}),\n", constructor.r#type.value, amount)); } }, _ => {} From 25c613fb031f658656ca9db463c58e2a98a9a461 Mon Sep 17 00:00:00 2001 From: Maximiliano Duthey Date: Tue, 30 Sep 2025 10:55:54 -0300 Subject: [PATCH 6/8] fix: correct output format for transaction hash in table display --- src/tx/invoke.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tx/invoke.rs b/src/tx/invoke.rs index 1c0ec3d..a9b9c49 100644 --- a/src/tx/invoke.rs +++ b/src/tx/invoke.rs @@ -91,7 +91,7 @@ pub async fn run(args: Args, ctx: &crate::Context) -> Result<()> { } OutputFormat::Table => { - println!("Tx Hash: {}", hex::encode(&hash)); + println!("Tx Hash: {}", &hash); println!("Tx CBOR: {}", hex::encode(&cbor)); } } From e3415bb9db3273b05dcd79bfc06b04d9329f42ae Mon Sep 17 00:00:00 2001 From: Maximiliano Duthey Date: Tue, 30 Sep 2025 11:11:43 -0300 Subject: [PATCH 7/8] fix: apply CodeRabbit suggestions --- src/tx/construct/add_input.rs | 9 ++++--- src/tx/construct/add_output.rs | 9 ++++--- src/tx/construct/common.rs | 47 ++++++++++++++++++++++++++-------- src/tx/construct/wizard.rs | 8 ++++-- 4 files changed, 52 insertions(+), 21 deletions(-) diff --git a/src/tx/construct/add_input.rs b/src/tx/construct/add_input.rs index a0e49a4..fbb3f07 100644 --- a/src/tx/construct/add_input.rs +++ b/src/tx/construct/add_input.rs @@ -19,11 +19,12 @@ pub async fn run(args: Args, _ctx: &crate::Context) -> Result<()> { tx_builder.collect_inputs(true)?; - let ast = tx_builder.ast.clone(); + // Serialize and write AST + let ast_json = serde_json::to_string_pretty(&tx_builder.ast) + .context("Failed to serialize tx3 AST")?; - // Write to AST file - fs::write(&ast_path_buf, serde_json::to_string_pretty(&ast).unwrap()) - .context("Failed to write tx3 AST file")?; + fs::write(&ast_path_buf, ast_json) + .with_context(|| format!("Failed to write tx3 AST file: {}", ast_path_buf.display()))?; println!("\nāœ… Input added successfully!"); println!("šŸ“„ File saved to: {}", ast_path_buf.display()); diff --git a/src/tx/construct/add_output.rs b/src/tx/construct/add_output.rs index 7f00eb6..93cda06 100644 --- a/src/tx/construct/add_output.rs +++ b/src/tx/construct/add_output.rs @@ -19,11 +19,12 @@ pub async fn run(args: Args, _ctx: &crate::Context) -> Result<()> { tx_builder.collect_outputs(true)?; - let ast = tx_builder.ast.clone(); + // Serialize and write AST + let ast_json = serde_json::to_string_pretty(&tx_builder.ast) + .context("Failed to serialize tx3 AST")?; - // Write to AST file - fs::write(&ast_path_buf, serde_json::to_string_pretty(&ast).unwrap()) - .context("Failed to write tx3 AST file")?; + fs::write(&ast_path_buf, ast_json) + .with_context(|| format!("Failed to write tx3 AST file: {}", ast_path_buf.display()))?; println!("\nāœ… Output added successfully!"); println!("šŸ“„ File saved to: {}", ast_path_buf.display()); diff --git a/src/tx/construct/common.rs b/src/tx/construct/common.rs index 0d558b3..efd724d 100644 --- a/src/tx/construct/common.rs +++ b/src/tx/construct/common.rs @@ -13,7 +13,7 @@ pub struct TransactionBuilder { } impl TransactionBuilder { - pub fn new(name: String, mut ast: tx3_lang::ast::Program) -> Self { + pub fn new(name: String, mut ast: tx3_lang::ast::Program) -> Result { let mut def_index = ast.txs.iter().position(|tx| tx.name.value == name); if def_index.is_none() { @@ -35,15 +35,19 @@ impl TransactionBuilder { "span": tx3_lang::ast::Span::default(), "collateral": [], }); - ast.txs.push(serde_json::from_value(value).unwrap()); + let tx_def: tx3_lang::ast::TxDef = + serde_json::from_value(value) + .context("Failed to materialize TxDef from JSON template")?; + + ast.txs.push(tx_def); def_index = Some(ast.txs.len() - 1); } - Self { + Ok(Self { ast: ast.clone(), def_index: def_index.unwrap(), - } + }) } pub fn from_ast(ast_path_buf: &PathBuf) -> Result { @@ -57,7 +61,7 @@ impl TransactionBuilder { tx3_lang::ast::Program::default() }; - Ok(TransactionBuilder::new("new_transaction".to_string(), ast)) + TransactionBuilder::new("new_transaction".to_string(), ast) } pub fn collect_inputs(&mut self, skip_question: bool) -> Result<()> { @@ -101,14 +105,21 @@ impl TransactionBuilder { continue; } - // input_block.fields.push(tx3_lang::ast::InputBlockField::From( // tx3_lang::ast::DataExpr::String(tx3_lang::ast::StringLiteral::new(address.to_bech32().unwrap())), // )); + + let txid = hex::decode(parts[0]) + .context("Invalid txid hex in UTxO reference")?; + + let index = parts[1] + .parse::() + .context("Invalid UTxO index")?; + input_block.fields.push(tx3_lang::ast::InputBlockField::Ref( tx3_lang::ast::DataExpr::UtxoRef(tx3_lang::ast::UtxoRef { - txid: hex::decode(parts[0]).unwrap(), - index: parts[1].parse::().unwrap(), + txid, + index, span: tx3_lang::ast::Span::default(), }), )); @@ -117,9 +128,13 @@ impl TransactionBuilder { .with_default("1000000") .prompt()?; + let min_amount_value = min_amount + .parse::() + .context("Invalid minimum amount value")?; + input_block.fields.push(tx3_lang::ast::InputBlockField::MinAmount( tx3_lang::ast::DataExpr::StaticAssetConstructor(tx3_lang::ast::StaticAssetConstructor { - amount: Box::new(tx3_lang::ast::DataExpr::Number(min_amount.parse::().unwrap())), + amount: Box::new(tx3_lang::ast::DataExpr::Number(min_amount_value)), span: tx3_lang::ast::Span::default(), r#type: tx3_lang::ast::Identifier::new("Ada".to_string()), }) @@ -180,17 +195,27 @@ impl TransactionBuilder { let address = Address::from_str(&to_address) .context("Invalid address")?; + let bech32 = address + .to_bech32() + .context("Failed to encode bech32 address")?; + output_block.fields.push(tx3_lang::ast::OutputBlockField::To( - Box::new(tx3_lang::ast::DataExpr::String(tx3_lang::ast::StringLiteral::new(address.to_bech32().unwrap()))), + Box::new(tx3_lang::ast::DataExpr::String( + tx3_lang::ast::StringLiteral::new(bech32) + )), )); let amount = Text::new("Amount:") .with_default("1000000") .prompt()?; + let amount_value = amount + .parse::() + .context("Invalid Ada amount")?; + output_block.fields.push(tx3_lang::ast::OutputBlockField::Amount( Box::new(tx3_lang::ast::DataExpr::StaticAssetConstructor(tx3_lang::ast::StaticAssetConstructor { - amount: Box::new(tx3_lang::ast::DataExpr::Number(amount.parse::().unwrap())), + amount: Box::new(tx3_lang::ast::DataExpr::Number(amount_value)), span: tx3_lang::ast::Span::default(), r#type: tx3_lang::ast::Identifier::new("Ada".to_string()), })) diff --git a/src/tx/construct/wizard.rs b/src/tx/construct/wizard.rs index ab87982..5ea5423 100644 --- a/src/tx/construct/wizard.rs +++ b/src/tx/construct/wizard.rs @@ -43,8 +43,12 @@ pub async fn run(args: Args, _ctx: &crate::Context) -> Result<()> { fs::write(&args.tx3_file, tx3_content) .context("Failed to write tx3 file")?; - fs::write(ast_path_buf, serde_json::to_string_pretty(&ast).unwrap()) - .context("Failed to write tx3 AST file")?; + // Serialize and write AST + let ast_json = serde_json::to_string_pretty(&ast) + .context("Failed to serialize tx3 AST")?; + + fs::write(&ast_path_buf, ast_json) + .with_context(|| format!("Failed to write tx3 AST file: {}", ast_path_buf.display()))?; println!("\nāœ… Transaction created successfully!"); println!("šŸ“„ File saved to: {}", args.tx3_file.display()); From 170403d2281cfa47c6dceb708e6015e2f6d61cb2 Mon Sep 17 00:00:00 2001 From: Maximiliano Duthey Date: Thu, 2 Oct 2025 12:10:12 -0300 Subject: [PATCH 8/8] feat: add documentation for transaction and search commands --- docs/commands/_meta.yml | 3 + docs/commands/search.mdx | 305 +++++++++++++++++++++++++++++++ docs/commands/tx.mdx | 374 +++++++++++++++++++++++++++++++++++++++ docs/usage.mdx | 129 +++++++++++--- 4 files changed, 788 insertions(+), 23 deletions(-) create mode 100644 docs/commands/_meta.yml create mode 100644 docs/commands/search.mdx create mode 100644 docs/commands/tx.mdx diff --git a/docs/commands/_meta.yml b/docs/commands/_meta.yml new file mode 100644 index 0000000..cf38a40 --- /dev/null +++ b/docs/commands/_meta.yml @@ -0,0 +1,3 @@ +label: Commands +order: 5 +collapsed: true \ No newline at end of file diff --git a/docs/commands/search.mdx b/docs/commands/search.mdx new file mode 100644 index 0000000..641724c --- /dev/null +++ b/docs/commands/search.mdx @@ -0,0 +1,305 @@ +--- +title: Search Commands +sidebar: + order: 6 + label: search +--- + +import { Aside, CardGrid, LinkCard, Tabs, TabItem } from '@astrojs/starlight/components'; + +The `search` command allows you to query blockchain data directly from the command line. You can search for blocks and transactions using various parameters. + + + +## Available Commands + +- **`search block`**: Query block information by tx-hash,slot +- **`search transaction`**: Query transaction details by hash + +## Output Formats + +All search commands support multiple output formats: + +```bash +# Table format (default) +cshell search block + +# JSON format +cshell search block --output-format json +``` + +### Supported Formats + +- **table**: Human-readable table format (default) +- **json**: Machine-readable JSON format + + + +--- + +## search block + +The `search block` command queries blockchain data to retrieve information about a specific block. + +### Usage + +```bash +cshell search block +``` + +### Options + +Run `cshell search block --help` to see all available options. + +#### Output Format + +Use the `--output-format` flag to change the output format: + +```bash +cshell search block --output-format json +``` + +Supported formats: +- `table` (default): Human-readable table +- `json`: Machine-readable JSON + +### Block Identifiers + +You can search for blocks using: + +```bash +cshell search block 9a28855928d8a94ac0ec7a5c0a45298cdbf939d1f302deb2b9e54bafb48789f4,91460405 +``` + +### Examples + +#### Basic Block Query + + + +```bash +cshell search block +``` + +Output: +``` +ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” +│ Block │ │ Hash │ Inputs │ Outputs │ Certificates │ Ref Inputs │ Datum │ +ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ +│ 8f3a...1f2 │ 0 │ 7b2c3d4e5f6a7b8c9d0e1f2a3b4c5... │ 2 │ 3 │ 0 │ 0 │ empty │ +│ 8f3a...1f2 │ 1 │ 9c1d2e3f4a5b6c7d8e9f0a1b2c3d4... │ 1 │ 2 │ 0 │ 0 │ empty │ +ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ +``` + + +```bash +cshell search block --output-format json +``` + +Output: +```json +{ + "auxiliary": {}, + "collateral": {}, + "fee": "200000", + "hash": "....", + "inputs": [ + ... + ], + "outputs": [ + ... + ], + "successful": true, + "validity": {}, + "witnesses": { + ... + } +} +``` + + + +#### Export Block Data + +Save block data to a file for later analysis: + +```bash +cshell search block --output-format json > block-data.json +``` + +--- + +## search transaction + +The `search transaction` command queries blockchain data to retrieve detailed information about a specific transaction. + +### Usage + +```bash +cshell search transaction +``` + +### Options + +Run `cshell search transaction --help` to see all available options. + +#### Output Format + +Use the `--output-format` flag to change the output format: + +```bash +cshell search transaction --output-format json +``` + +Supported formats: +- `table` (default): Human-readable table +- `json`: Machine-readable JSON + +### Examples + +#### Basic Transaction Query + + + +```bash +cshell search transaction ef6350af39d35caa3130218f8fb103fa6bb585258d52366966ede8da2100d3c1 +``` + +Output: +``` +ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” +│ Block │ │ Hash │ Inputs │ Outputs │ Certificates │ Ref Inputs │ Datum │ +ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ +│ f55c...19db │ 0 │ ef6350af39d35caa3130218f8fb10... │ 1 │ 2 │ 0 │ 1 │ contain │ +ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ +``` + + +```bash +cshell search transaction --output-format json +``` + +Output: +```json +{ + "auxiliary": {}, + "collateral": {}, + "fee": "174389", + "hash": "72NQrznTXKoxMCGPj7ED+mu1hSWNUjZpZu3o2iEA08E=", + "inputs": [ + { + "outputIndex": 1, + "txHash": "2IQhCcVhqReK7BKTf604tlW/mG1LyDA3tKfX4eE9XBY=" + } + ], + "outputs": [ + { + "address": "cIe32DcyeHxWkqWXPdWwJEdAYiQQa6InY+6DaNY=", + "coin": "30000000", + "datum": { + ... + } + }, + { + "address": "AFmvO2CJMAJJi9D8hL1k+Eh+7LDfhFrZ5gkyqoZ06yLq0b9uxm0QfpFnclhfG8g5C3ZJj74KBTZe", + "coin": "9729302444", + "datum": {} + } + ], + "referenceInputs": [ + ... + ], + "successful": true, + "validity": {}, + "witnesses": { + ... + } +} +``` + + + +#### Export Transaction Data + +Save transaction details to a file: + +```bash +cshell search transaction --output-format json > transaction.json +``` + +#### Check Transaction Outputs + +Extract specific outputs from a transaction: + +```bash +cshell search transaction --output-format json | jq '.outputs' +``` + +### Use Cases + +#### 1. Transaction Confirmation + +Verify your submitted transaction: + +```bash +cshell search transaction +``` + +If found, the transaction was successfully submitted and confirmed. + +#### 2. UTxO Verification + +Check if a specific UTxO exists and is unspent: + +```bash +cshell search transaction --output-format json | jq '.outputs[0]' +``` + +#### 3. Payment Verification + +Confirm payment was received: + +```bash +# Search for transaction +cshell search transaction --output-format json + +# Check outputs for your address +jq '.outputs[] | select(.address == "addr1...")' transaction.json +``` + +#### 4. Smart Contract Debugging + +Inspect transaction datum and redeemers: + +```bash +cshell search transaction --output-format json | jq '.outputs[].datum' +``` + +#### 5. Token Verification + +Check native token transfers: + +```bash +cshell search transaction --output-format json | jq '.outputs[].assets' +``` + +## Quick Reference + + + +## Related Commands + +- [`tx invoke`](/cshell/commands/tx) - Submit transactions +- [`wallet balance`](/cshell/wallet) - Check wallet balance +- [`explorer`](/cshell/explorer) - Visual block and transaction explorer diff --git a/docs/commands/tx.mdx b/docs/commands/tx.mdx new file mode 100644 index 0000000..3a0af8a --- /dev/null +++ b/docs/commands/tx.mdx @@ -0,0 +1,374 @@ +--- +title: Transaction Commands +sidebar: + order: 0 + label: tx +--- + +import { Aside, CardGrid, LinkCard, Tabs, TabItem } from '@astrojs/starlight/components'; + +The `tx` command (or `transactions`) provides a complete workflow for managing Cardano transactions in Cshell. You can execute transactions using [**tx3**](https://docs.txpipe.io/tx3) files, or work with individual steps of the transaction lifecycle. + + + +## Transaction Workflow + +Understanding the transaction lifecycle: + +1. **Write**: Create a tx3 file describing your transaction +2. **Resolve**: Convert tx3 to CBOR format (handled by TRP) +3. **Sign**: Add cryptographic signatures using your wallet +4. **Submit**: Send the transaction to the blockchain + +The `tx invoke` command handles all these steps automatically, while individual commands let you control each step separately for advanced use cases. + +## Available Commands + +- **`tx invoke`**: Resolve, sign, and submit a tx3 transaction in one step +- **`tx construct`**: Build transactions interactively or programmatically +- **`tx resolve`**: Resolve a tx3 transaction to CBOR +- **`tx sign`**: Sign a CBOR transaction +- **`tx submit`**: Submit a CBOR transaction to the blockchain + +--- + +## tx invoke + +The `tx invoke` command executes a complete transaction workflow: resolving a tx3 file, signing it with your wallet, and submitting it to the blockchain. + +### Usage + +```bash +cshell tx invoke --tx3-file +``` + +### Options + +Run `cshell tx invoke --help` to see all available options. + +#### Using with Different Provider + +You can specify a different provider using flags: + +```bash +cshell tx invoke --tx3-file ./transfer.tx3 --provider testnet +``` + +### How it Works + +When you run `tx invoke`, Cshell: + +1. **Reads** your tx3 file +2. **Resolves** the transaction via your configured TRP server +3. **Signs** the transaction using the appropriate wallet(s) +4. **Submits** the signed transaction to the blockchain +5. **Returns** the transaction hash + + + +--- + +## tx construct + +The `tx construct` command provides tools for building transactions programmatically or interactively, offering more control than tx3 files for advanced use cases. + +### Available Subcommands + +The construct command includes several subcommands: + +```bash +cshell tx construct --help +``` + +#### Wizard + +Generate a tx3 transaction using an interactive wizard: + +```bash +cshell tx construct wizard +``` + +The wizard guides you through the transaction creation process step by step, asking for inputs, outputs, and other transaction details. + +#### Add Input + +Add a UTxO input to a transaction: + +```bash +cshell tx construct add-input --tx3-file +``` + +**Options:** +- `--tx3-file`: Path to the final tx3 file used to identify current transaction (tx3 file will be created on build command) +- Additional options available with `--help` + +#### Add Output + +Add an output to a transaction: +```bash +cshell tx construct add-output --tx3-file +``` + +**Options:** +- `--tx3-file`: Path to the final tx3 file used to identify current transaction (tx3 file will be created on build command) +- Additional options for assets and datum with `--help` + +#### Build + +Generate the tx3 file from accumulated inputs and outputs: + +```bash +cshell tx construct build --tx3-file +``` + +This will take all the inputs/outputs you've added by add-input / add-output commands and create a complete tx3 file. + +### Workflow Example + +Here's a typical workflow for constructing a transaction manually: + +```bash +# Start with the wizard to set up basic structure +cshell tx construct wizard + +# Or build step by step: + +# 1. Add input UTxO +cshell tx construct add-input --tx3-file my-transaction.tx3 + +# 2. Add output +cshell tx construct add-output --tx3-file my-transaction.tx3 + +# 3. Add change output +cshell tx construct add-output --tx3-file my-transaction.tx3 + +# 4. Build the transaction +cshell tx construct build --tx3-file my-transaction.tx3 +``` + +### Additional Help + +Each subcommand has its own help documentation: + +```bash +cshell tx construct wizard --help +cshell tx construct add-input --help +cshell tx construct add-output --help +cshell tx construct build --help +``` + +--- + +## tx resolve + +The `tx resolve` command converts a tx3 file into CBOR format without signing or submitting it. This is useful for inspecting transaction details or preparing transactions for external signing. + +### Usage + +```bash +cshell tx resolve --tx3-file +``` + +### Options + +Run `cshell tx resolve --help` to see all available options. + +### Examples + +#### Basic Resolution + +```bash +cshell tx resolve --tx3-file ./transfer.tx3 +``` + +This outputs the resolved CBOR transaction to stdout. + + + +#### With Different Provider + +```bash +cshell tx resolve --tx3-file ./transfer.tx3 --provider testnet +``` + +--- + +## tx sign + +The `tx sign` command signs a CBOR transaction with your wallet's private keys, preparing it for submission to the blockchain. + +### Usage + +```bash +cshell tx sign +``` + +### Options + +Run `cshell tx sign --help` to see all available options. + +### Examples + +#### Basic Signing + +```bash +# Sign a resolved transaction +cshell tx sign +``` + +The signed transaction will be output to stdout. + +#### Specify Output File + +```bash +cshell tx sign > signed.cbor +``` + +#### Sign with Specific Wallet + +If you have multiple wallets, specify which one to use: + +```bash +cshell tx sign --signer my-wallet +``` + +### Workflow + +Typically used as part of a manual transaction workflow: + +```bash +# 1. Resolve the transaction +cshell tx resolve --tx3-file transfer.tx3 + +# 2. Sign the transaction +cshell tx sign + +# 3. Submit to blockchain +cshell tx submit +``` + + + +--- + +## tx submit + +The `tx submit` command broadcasts a signed CBOR transaction to the Cardano blockchain via your configured TRP provider. + +### Usage + +```bash +cshell tx submit +``` + +### Options + +Run `cshell tx submit --help` to see all available options. + +### Examples + +#### Basic Submission + +```bash +cshell tx submit +``` + +Successful submission returns a transaction hash: + +``` +Submitted TX: +TX Hash: 8f3a2b1c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2 +``` + +#### With Different Provider + +Submit to a specific provider: + +```bash +cshell tx submit --provider mainnet +``` + +#### Save Transaction Hash + +Capture the transaction hash for later reference: + +```bash +cshell tx submit > tx-submit-result.txt +``` + +### Complete Workflow + +Here's the full manual transaction workflow: + +```bash +# 1. Resolve transaction +cshell tx resolve --tx3-file transfer.tx3 + +# 2. Sign transaction +cshell tx sign + +# 3. Submit to blockchain +cshell tx submit + +# 4. Monitor transaction (using the returned hash) +cshell search transaction +``` + + + +### Verifying Submission + +After submitting, verify your transaction: + +#### Using Cshell Explorer + + + +```bash +# Open explorer and check recent transactions +cshell explorer +``` + +Navigate to the Transactions tab to see your submitted transaction. + +#### Using Search Command + +```bash +# Search by transaction hash +cshell search transaction +``` + +#### Check Wallet Balance + +```bash +# Verify balance changes +cshell wallet balance +``` + +--- + +## Quick Reference + + diff --git a/docs/usage.mdx b/docs/usage.mdx index a1c6194..dbbab9d 100644 --- a/docs/usage.mdx +++ b/docs/usage.mdx @@ -4,49 +4,132 @@ sidebar: order: 3 --- -import { Aside } from '@astrojs/starlight/components'; +import { Aside, CardGrid, LinkCard } from '@astrojs/starlight/components'; -With Cshell fully configured ([provider](/cshell/provider) and [wallet](/cshell/wallet)), it's time to start using it to manage transactions or fetch on-chain data. - -You can execute any transaction in Cshell using [**tx3**](https://docs.txpipe.io/tx3). Cshell will call the TRP to resolve and submit the transaction. +With Cshell fully configured ([provider](/cshell/provider) and [wallet](/cshell/wallet)), you're ready to manage transactions and explore blockchain data. - +## Quick Start -## Commands -The commands below are used to handle transactions and fetch on-chain data. +### Execute a Transaction -### Transactions -You can manage transactions by using Cshell's `tx` or `transactions` commands. To see which tx commands are available, run the following: +The quickest way to submit a transaction is using [**tx3**](https://docs.txpipe.io/tx3) files: ```bash -cshell tx --help +cshell tx invoke --tx3-file ./my-transaction.tx3 ``` -Use the `--help` flag with any command to view its available options. +This will resolve, sign, and submit your transaction in one step. + +### Search Blockchain Data + +Query blocks and transactions: ```bash -cshell tx invoke --help +# Search for a specific block +cshell search block + +# Search for a transaction +cshell search transaction ``` -### Search -You can search by block or transactions using the commands search. To see which search commands are available, run the following +### Explore with GUI - +Launch the terminal-based explorer: ```bash -cshell search --help +cshell explorer ``` -Use the `--help` flag with any command to view its available options. +## Available Commands + +Cshell provides several command groups for different operations: + + + + + + + + + +## Common Workflows + +### Send ADA ```bash -cshell search block --help +# 1. Create a tx3 file describing the transfer +# 2. Execute the transaction +cshell tx invoke --tx3-file ./transfer.tx3 ``` + +### Mint Tokens + +```bash +# 1. Create a tx3 file with minting instructions +# 2. Execute the transaction +cshell tx invoke --tx3-file ./mint-token.tx3 +``` + +### Verify a Transaction + +```bash +# After submission, verify it was confirmed +cshell search transaction +``` + +### Check Wallet Balance + +```bash +cshell wallet balance +``` + +## Output Formats + +Many commands support multiple output formats: + +```bash +# Human-readable table (default) +cshell search block + +# Machine-readable JSON +cshell search block --output-format json +``` + + + +## Next Steps + +- Learn about [transaction commands](/cshell/commands/tx) for managing transactions +- Explore [search commands](/cshell/commands/search) for querying blockchain data +- Check out the [examples](https://github.com/txpipe/cshell/tree/main/examples) for sample tx3 files