Skip to content
Open
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
185 changes: 60 additions & 125 deletions Cargo.lock

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,32 @@ name = "mostro-cli"
path = "src/main.rs"

[dependencies]
anyhow = "1.0.99"
clap = { version = "4.5.46", features = ["derive"] }
anyhow = "1.0.100"
clap = { version = "4.5.54", features = ["derive"] }
nostr-sdk = { version = "0.43.0", features = ["nip06", "nip44", "nip59"] }
serde = "1.0.219"
serde_json = "1.0.143"
tokio = { version = "1.47.1", features = ["full"] }
comfy-table = "7.2.1"
chrono = "0.4.42"
log = "0.4.28"
serde = "1.0.228"
serde_json = "1.0.149"
tokio = { version = "1.49.0", features = ["full"] }
comfy-table = "7.2.2"
chrono = "0.4.43"
log = "0.4.29"
futures = "0.3"
uuid = { version = "1.18.1", features = [
uuid = { version = "1.19.0", features = [
"v4",
"fast-rng",
"macro-diagnostics",
"serde",
] }
lightning-invoice = { version = "0.33.2", features = ["std"] }
lightning-invoice = { version = "0.34.0", features = ["std"] }
reqwest = { version = "0.12.23", default-features = false, features = [
"json",
"rustls-tls",
] }
mostro-core = "0.6.56"
mostro-core = "0.7.0"
lnurl-rs = { version = "0.9.0", default-features = false, features = ["ureq"] }
pretty_env_logger = "0.5.0"
sqlx = { version = "0.8.6", features = ["sqlite", "runtime-tokio-rustls"] }
bip39 = { version = "2.2.0", features = ["rand"] }
bip39 = { version = "2.2.2", features = ["rand"] }
dirs = "6.0.0"

[package.metadata.release]
Expand Down
126 changes: 126 additions & 0 deletions docs/CODING_STANDARDS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Coding Standards

This document outlines the coding standards and best practices for the Mostro CLI project. These guidelines ensure code quality, maintainability, and consistency across the codebase.

## Core Principles

### 1. Readability and Reuse

**Priority**: Code should be written for humans first, machines second.

- **Clear naming**: Use descriptive names for functions, variables, and modules (e.g., `parse_dm_events` vs `pde`).
- **Function reuse**: Extract common logic into reusable functions. Place shared utilities in appropriate modules (`src/util/`, `src/parser/`, etc.).
- **Module organization**: Group related functionality together (CLI commands in `src/cli/`, Protocol parsing in `src/parser/`, Utilities in `src/util/`).

### 2. Avoid Code Duplication (DRY Principle)

**Don't Repeat Yourself**: If the same logic appears in multiple places, extract it.

- **Extract common patterns**: Create helper functions for repeated operations like DM sending.
- **Centralize constants**: Import from `mostro-core::prelude` instead of hardcoding values.

### 3. Simplicity

**Keep It Simple**: Prefer straightforward solutions over clever ones.

- **Avoid premature optimization**: Write clear code first, optimize only when needed.
- **Prefer explicit over implicit**: Use `Option` and `Result` types explicitly rather than hiding errors with `unwrap()`.

### 4. Function Length Limit

**Maximum 300 lines per function**: If a function exceeds this limit, split it into smaller, single-responsibility functions.

## Rust-Specific Guidelines

### Error Handling

- **Use `Result<T, E>`**: Functions that can fail should return `Result`.
- **Use `anyhow::Result`**: For application-level errors, use `anyhow::Result<T>`.
- **Propagate errors**: Use the `?` operator to propagate errors up the call stack.
- **Add context**: Use `.context("...")` from `anyhow` to add meaningful error messages.

### Type Safety

- **Use strong types**: Prefer newtypes or enums (`Action`, `Status`) over primitive types.
- **Leverage enums**: Use enums for state machines and role management.

### Async/Await

- **Prefer async/await**: Use `async fn` for I/O and network operations.
- **Handle timeouts**: Use `tokio::time::timeout` for network operations.

### Documentation

- **Document public APIs**: Use `///` doc comments for public functions and types.
- **Explain "why"**: Document the reasoning behind complex logic, not just "what".
- **Markdown standards**: All markdown documentation must pass linter checks with no errors.
- Run markdown linters to catch formatting issues (MD040, MD060, MD022, MD032, etc.).
- Fix all linter errors before committing documentation changes.
- Use proper table formatting with spaces around pipes.
- Specify language for all fenced code blocks.
- Add blank lines around headings and lists.

## Nostr and Mostro-Specific Guidelines

### Event Kinds

- **Use constants**: Always use constants from `mostro-core::prelude` (e.g., `NOSTR_ORDER_EVENT_KIND`).
- **Never hardcode**: Avoid hardcoding event kind numbers like 38383.

### Message Handling

- **Parse DMs consistently**: Use `parse_dm_events` for all DM parsing.
- **Support multiple message types**: Handle both GiftWrap (NIP-59) and PrivateDirectMessage (NIP-17).

### Key Management

- **Identity vs Trade Keys**:
- **Identity keys** (index 0): User's main identity, used for signing.
- **Trade keys** (index 1+): Ephemeral keys for each trade, ensuring privacy.

## Code Organization Patterns

### Module Structure

```text
src/
β”œβ”€β”€ main.rs # Entry point
β”œβ”€β”€ cli.rs # CLI definitions and Context
β”œβ”€β”€ db.rs # Database models (User, Order)
β”œβ”€β”€ cli/ # CLI command implementations
β”œβ”€β”€ parser/ # Event parsing and display
└── util/ # Core utilities (events, messaging, net)
```

### Re-export Pattern

Use `mod.rs` files to re-export commonly used items from submodules to keep imports clean.

## Database Patterns

- **User Model**: Use chainable setters and the `.save()` pattern.
- **Order Management**: Use `Order::new()` to handle both insertion and updates.

## CLI Command Pattern

All CLI commands follow a standard flow:

1. Validate inputs.
2. Get required keys (Identity/Trade).
3. Build the Mostro message.
4. Send to Mostro (NIP-59).
5. Wait for response (Subscription).
6. Parse and display results.

## Summary Checklist

- [ ] Code is readable and well-named.
- [ ] No code duplication (DRY).
- [ ] Functions are under 300 lines.
- [ ] Errors are handled properly (`Result`, `?`).
- [ ] Event kinds use constants from `mostro-core`.
- [ ] Both GiftWrap and PrivateDirectMessage are supported.
- [ ] Public APIs are documented.
- [ ] All markdown documentation passes linter checks with no errors.
- [ ] Code passes `cargo fmt` and `cargo clippy`.
- [ ] Tests pass (`cargo test`).
73 changes: 73 additions & 0 deletions docs/CREATING_ORDERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Creating Orders in Mostro CLI

This document explains how to create new buy and sell orders using Mostro CLI.

## Overview

Creating an order involves:

1. Specifying order parameters (type, amount, currency, etc.)
2. Building a Mostro message
3. Sending it to the Mostro coordinator via Nostr
4. Receiving confirmation
5. Waiting for someone to take the order

## Order Types

### Sell Order (Maker sells Bitcoin)

User wants to **sell Bitcoin** for fiat currency.

```bash
mostro-cli neworder -k sell -c USD -f 100 -a 50000 -m "PayPal"
```

### Buy Order (Maker buys Bitcoin)

User wants to **buy Bitcoin** with fiat currency.

```bash
mostro-cli neworder -k buy -c EUR -f 200 -m "Bank Transfer" -i lnbc...
```

## Order Parameters

### Required Parameters

| Parameter | Flag | Description | Example |
| ----------- | ------ | ------------- | --------- |
| Kind | `-k`, `--kind` | "buy" or "sell" | `sell` |
| Fiat Code | `-c`, `--fiat-code` | Currency code | `USD`, `EUR`, `ARS` |
| Fiat Amount | `-f`, `--fiat-amount` | Amount in fiat | `100` or `100-500` (range) |
| Payment Method | `-m`, `--payment-method` | How to pay | `"PayPal"`, `"Bank Transfer"` |

### Optional Parameters

| Parameter | Flag | Description | Default |
| ----------- | ------ | ------------- | --------- |
| Amount | `-a`, `--amount` | Bitcoin in sats | 0 (market price) |
| Premium | `-p`, `--premium` | Price premium % | 0 |
| Invoice | `-i`, `--invoice` | Lightning invoice (buy orders) | None |
| Expiration Days | `-e`, `--expiration-days` | Days until expired | 0 |

## Order Examples

### 1. Simple Sell Order (Market Price)

```bash
mostro-cli neworder -k sell -c USD -f 100 -m "PayPal"
```

### 2. Range Order (Flexible Amount)

```bash
mostro-cli neworder -k sell -c USD -f 100-500 -m "PayPal,Venmo"
```

### 3. Buy Order with Lightning Invoice

```bash
mostro-cli neworder -k buy -c USD -f 50 -i lnbc500u1p3.... -m "Cash App"
```

For technical details on the code flow and message structures, see [ORDER_FLOW_TECHNICAL.md](./ORDER_FLOW_TECHNICAL.md).
43 changes: 43 additions & 0 deletions docs/DATABASE_SCHEMA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Database Schema

Mostro CLI uses a local SQLite database (`mcli.db`) to store user identity and trade history.

## Table: `users`

Stores the BIP-39 mnemonic and identity information.

| Column | Type | Description |
| -------- | ------ | ------------- |
| `i0_pubkey` | `CHAR(64)` | Primary Key. The user's identity pubkey. |
| `mnemonic` | `TEXT` | The 12-word seed phrase. |
| `last_trade_index` | `INTEGER` | The last derived trade key index. |
| `created_at` | `INTEGER` | Timestamp of creation. |

## Table: `orders`

Stores details of orders created or taken by the user.

| Column | Type | Description |
| -------- | ------ | ------------- |
| `id` | `TEXT` | Primary Key. Order UUID. |
| `kind` | `TEXT` | "buy" or "sell". |
| `status` | `TEXT` | Current status (pending, active, etc.). |
| `amount` | `INTEGER` | Satoshis amount. |
| `min_amount` | `INTEGER` | Minimum satoshis for range orders. |
| `max_amount` | `INTEGER` | Maximum satoshis for range orders. |
| `fiat_code` | `TEXT` | Fiat currency code (e.g., "USD"). |
| `fiat_amount` | `INTEGER` | Fiat units. |
| `payment_method` | `TEXT` | Payment method name. |
| `premium` | `INTEGER` | Premium percentage (basis points). |
| `trade_keys` | `TEXT` | Hex-encoded private key for this trade. |
| `counterparty_pubkey` | `TEXT` | Pubkey of the other party in the trade. |
| `is_mine` | `BOOLEAN` | True if the user created the order. |
| `buyer_invoice` | `TEXT` | Lightning invoice for the buyer. |
| `request_id` | `INTEGER` | Request ID for tracking messages. |
| `created_at` | `INTEGER` | Creation timestamp. |
| `expires_at` | `INTEGER` | Expiration timestamp. |

## Implementation Reference

- `src/db.rs`: Contains the `User` and `Order` structs and SQL queries.
- `src/util/storage.rs`: Helper functions for database interaction.
53 changes: 53 additions & 0 deletions docs/DISPUTE_MANAGEMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Dispute Management

This document covers how disputes are handled in Mostro CLI by both users and administrators.

## User Dispute Flow

When a trade goes wrong (e.g., fiat sent but Bitcoin not released), either party can initiate a dispute.

### Initiate a Dispute

```bash
mostro-cli dispute -o <order-id>
```

Mostro changes the order status to `Dispute`. This prevents any further automated transitions and flags the trade for manual intervention.

## Admin/Solver Flow

Admins or designated solvers use special commands to resolve conflicts. These commands require the `ADMIN_NSEC` environment variable to be set.

### 1. List Active Disputes

```bash
mostro-cli listdisputes
```

### 2. Take a Dispute

Before resolving, an admin must "take" the dispute to indicate they are handling it.

```bash
mostro-cli admtakedispute -d <dispute-id>
```

### 3. Settle (Pay Buyer)

If the buyer proved they sent fiat, the admin settles the hold invoice to pay the buyer.

```bash
mostro-cli admsettle -o <order-id>
```

### 4. Cancel (Refund Seller)

If the buyer failed to pay, the admin cancels the order to refund the locked Bitcoin to the seller.

```bash
mostro-cli admcancel -o <order-id>
```

## Security

Admin commands are gated by public key verification on the Mostro coordinator side. The CLI must sign these messages with the private key corresponding to a registered admin pubkey.
Loading