# Development Fee Technical Specification

## Overview

The development fee mechanism provides sustainable funding for Mostro development by automatically sending a configurable percentage of the Mostro fee to a lightning address set on `DEV_FEE_LIGHTNING_ADDRESS` on each successful order.

**Key Design Principles:**
- Transparent and configurable
- Non-blocking (failures don't prevent order completion)
- Full audit trail for accountability
- Node operator contribution model (mostrod pays from its earnings, not users)

## Implementation Status

### Phase 1: Infrastructure ✅ COMPLETE

**What's Implemented:**
- Configuration constants (MIN/MAX percentages, Lightning address) in `src/config/constants.rs`
- Settings validation on daemon startup in `src/config/util.rs`
- Database schema with 3 columns: `dev_fee`, `dev_fee_paid`, `dev_fee_payment_hash`
- Database fields initialized in order creation (currently hardcoded to 0)
- Default dev_fee_percentage: 0.30 (30%)

**Status:** Ready for Phase 2 implementation

### Phase 2: Fee Calculation ✅ COMPLETE

**Implemented Components:**
- `calculate_dev_fee()` pure function in `src/util.rs`
- `get_dev_fee()` wrapper function in `src/util.rs`
- Dev fee calculation (total amount paid by mostrod from its earnings)
- Integration in message amount calculations across 4 critical locations
- Unit tests for fee calculation logic (4 tests passing)

**Implementation Details:**
- Two-function approach: Pure calculation function + Settings wrapper
- Simple rounding for whole satoshi amounts
- Dev fee tracked for payment by mostrod
- User payments simplified (no dev_fee in buyer/seller amounts)

**Status:** ✅ Complete - Fee calculations implemented and tested across all order flows

### Phase 3: Payment Execution ✅ COMPLETE

**Implemented Components:**
- `send_dev_fee_payment()` function in `src/app/release.rs`
- Scheduler job `process_dev_fee_payment()` in `src/scheduler.rs`
- Database query function `find_unpaid_dev_fees()` in `src/db.rs`
- **Database field updates:**
  - `dev_fee`: Always set to 0 at order creation, calculated when order is taken (unified for all order types)
  - `dev_fee_paid`: Changed from 0 to 1 after successful payment in scheduler
  - `dev_fee_payment_hash`: Set to Lightning payment hash on successful payment
- Error handling and retry logic with automatic retries every 60 seconds
- Payment timeout handling (LNURL: 15s, send_payment: 5s, result: 25s, total: 50s per attempt)
- Dev fee amount validation (rejects payments with dev_fee <= 0)
- Enhanced logging with BEFORE/AFTER state tracking and database verification
- Race condition handling (query checks both 'settled-hold-invoice' AND 'success' statuses)
- Unified dev_fee calculation at order take time for both fixed and market price orders
- Invoice validation fix ensuring dev_fee is calculated before validating buyer invoices

**Status:** ✅ Complete - Automated dev fee payment system fully operational with unified calculation logic

**Implementation Commits:**
- f508669: feat: implement automated development fee payment system
- 102cfed: fix: resolve dev fee payment failures with two critical fixes
- eaf3319: Validate dev_fee amount before attempting payment
- 42253f1: Fix edge case on dev fund payment
- 2655943: fix: improve dev fee payment reliability and coverage
- 2349669: refactor: unify dev_fee calculation at order take time
- 7e9b0a0: fix: calculate dev_fee before invoice validation in take_sell

### Phase 4: Audit Events via Nostr ✅ COMPLETE

**What Was Implemented:**
- Custom Nostr event kind (8383) for dev fee payment audits
- Event publishing in scheduler after successful payment
- Public relay distribution for third-party verification and total tracking
- Complete payment details: order_id, dev_fee, payment_hash, timestamp
- Non-blocking event publication (payment succeeds even if event fails)

**Status:** ✅ Complete - Dev fee payments are auditable via Nostr events (see Phase 4 section for full specification)

**Key Design Decisions:**

1. **Scheduler-Based vs Inline Payment:**
   - Chose scheduler-based to avoid blocking order completion
   - 60-second interval balances responsiveness with resource usage
   - Automatic retry mechanism handles transient failures

2. **Dual Status Query:**
   - Critical for handling race conditions
   - Ensures dev fee is collected even if buyer payment completes during dev fee payment
   - Prevents revenue loss from timing edge cases

3. **Enhanced Logging:**
   - BEFORE/AFTER/VERIFY pattern provides complete audit trail
   - Essential for debugging database persistence issues
   - Helps identify race conditions and timing problems

4. **Validation Before Payment:**
   - Fail-fast approach for invalid amounts
   - Reduces unnecessary network calls
   - Clearer error messages for debugging

## Architecture

### Fee Flow Diagram

```
Order Creation → Fee Calculation → Hold Invoice → Seller Release → Buyer Payment → Dev Payment
     ↓                 ↓                ↓              ↓                ↓             ↓
  amount         mostro_fee        seller pays    settle hold      buyer paid    mostrod pays
                     ↓             amount + fee       ↓                ↓             ↓
                 dev_fee                       mostrod collects   status=success  send dev_fee
                 (tracking)                      mostro fee                       from earnings
                                                                                      ↓
                                                                              update db fields
```

### System Components

1. **Configuration Layer** (`src/config/`)
   - `constants.rs`: Hardcoded constraints (10-100%, Lightning Address)
   - `types.rs`: MostroSettings with dev_fee_percentage
   - `util.rs`: Startup validation

2. **Fee Calculation** (`src/util.rs`)
   - `get_dev_fee()`: Computes percentage of Mostro fee
   - `prepare_new_order()`: Calculates fees during order creation
   - `show_hold_invoice()`: Includes dev fee in seller's hold invoice

3. **Payment Execution** (`src/app/release.rs`)
   - `send_dev_fee_payment()`: LNURL resolution and payment
   - `payment_success()`: Integration point after buyer payment

4. **Database Schema** (`migrations/20251126120000_dev_fee.sql`)
   - `dev_fee`: Amount in satoshis
   - `dev_fee_paid`: Boolean (0/1)
   - `dev_fee_payment_hash`: Payment hash for reconciliation

## Configuration

### Constants (`src/config/constants.rs`)

```rust
pub const MIN_DEV_FEE_PERCENTAGE: f64 = 0.10;  // 10% minimum
pub const MAX_DEV_FEE_PERCENTAGE: f64 = 1.0;   // 100% maximum
pub const DEV_FEE_LIGHTNING_ADDRESS: &str = "<dev@lightning.address>";
```

### Settings (`~/.mostro/settings.toml`)

```toml
[mostro]
# Development sustainability fee
# Percentage of Mostro fee sent to development fund (minimum 10%)
dev_fee_percentage = 0.30
```

**Validation Rules:**
- Must be between 0.10 (10%) and 1.0 (100%)
- Validated on daemon startup
- Invalid values cause startup failure with error message

### Default Values

- `dev_fee_percentage`: 0.30 (30%)
- Configured in `src/config/types.rs::MostroSettings::default()`

## Technical Implementation

### Fee Calculation

**Current State:** ✅ IMPLEMENTED - Two functions provide fee calculation with proper satoshi handling.

**Implementation:**

Two-function approach in `src/util.rs`:

1. **Pure calculation function**:
```rust
/// Pure function for calculating dev fee - useful for testing
pub fn calculate_dev_fee(total_mostro_fee: i64, percentage: f64) -> i64 {
    let dev_fee = (total_mostro_fee as f64) * percentage;
    dev_fee.round() as i64
}
```

2. **Settings wrapper**:
```rust
/// Wrapper that uses configured dev_fee_percentage from Settings
pub fn get_dev_fee(total_mostro_fee: i64) -> i64 {
    let mostro_settings = Settings::get_mostro();
    calculate_dev_fee(total_mostro_fee, mostro_settings.dev_fee_percentage)
}
```

**Benefits of Two-Function Approach:**
- `calculate_dev_fee()` is pure, testable without global config
- `get_dev_fee()` provides convenient access using Settings
- Tests can verify behavior with different percentages
- Production code uses Settings seamlessly

**Formula Specification:**
```
total_dev_fee = round(total_mostro_fee × dev_fee_percentage)
```

**Why This Formula:**
- Simple percentage calculation of total Mostro fee
- Mostrod pays this amount from its earnings to the development fund
- No need for buyer/seller split since users don't pay dev_fee
- Rounding ensures whole satoshi amounts

**Examples:**
- Total Mostro fee: 1,000 sats, Percentage: 30% → Dev fee: 300 sats (paid by mostrod) ✓
- Total Mostro fee: 1,003 sats, Percentage: 30% → Dev fee: 301 sats (paid by mostrod) ✓
- Total Mostro fee: 333 sats, Percentage: 30% → Dev fee: 100 sats (paid by mostrod) ✓
- Mostro fee: 0 sats → Dev fee: 0 sats ✓

### Order Creation

**Current State:** ✅ IMPLEMENTED - ALL orders are created with `dev_fee = 0`. The `dev_fee` is calculated when the order is taken, unifying the behavior for both fixed price and market price orders. Database fields `dev_fee_paid` and `dev_fee_payment_hash` are initialized to `false` and `None` respectively.

**Implementation:** `src/util.rs::prepare_new_order()`

When creating a new order:
1. Calculate Mostro fee: `fee = get_fee(amount)` (for fixed price orders where amount > 0)
2. Set `dev_fee = 0` for ALL orders (both fixed price and market price)
3. Store in Order struct with `dev_fee_paid = false` and `dev_fee_payment_hash = None`

**Dev Fee Calculation at Take Time:**
- ALL orders (both fixed price and market price) have `dev_fee = 0` at creation
- Dev fee is calculated when order is taken (see "Dev Fee Calculation at Take Time" section)
- Implemented in `take_buy.rs` and `take_sell.rs`
- Formula: `dev_fee = get_dev_fee(fee * 2)` where fee is calculated based on the order amount

### Market Price Orders and Dev Fee Reset

**Critical Behavior for Market Price Orders:**

When a user takes a market price order, the following sequence occurs:

1. **Order Taken** (status changes from `pending` to `waiting-buyer-invoice` or `waiting-payment`):
   - Dev fee is calculated based on current market price
   - `order.dev_fee` field is updated with the calculated amount
   - Order waits for taker to provide invoice (sell order) or make payment (buy order)

2. **Taker Abandons Order** (doesn't provide invoice or make payment):
   - Order status is reset back to `pending`
   - **CRITICAL:** `order.dev_fee` field **MUST** be reset to `0`
   - This prevents incorrect dev fee charges if order is re-taken at different price

3. **Why Reset is Necessary:**
   - Market prices fluctuate continuously
   - Next taker may take order at different fiat amount
   - Mostro fee and dev fee must be recalculated for new amount
   - Leaving old dev_fee value would cause incorrect accounting

**Example Scenario:**

```
Initial Order: 100,000 sats at $50,000/BTC (market price)
- Mostro fee: 1,000 sats
- Dev fee (30%): 300 sats
- Order status: pending, dev_fee: 0

Taker 1 takes order at $50,000/BTC:
- Order status: waiting-buyer-invoice
- Dev fee calculated: 300 sats
- Order dev_fee: 300

Taker 1 abandons (doesn't provide invoice):
- Order status: pending
- Dev fee RESET: 0  ← CRITICAL RESET
- Order dev_fee: 0

Taker 2 takes order at $52,000/BTC (price increased):
- Order status: waiting-buyer-invoice
- Dev fee recalculated: 310 sats (new amount)
- Order dev_fee: 310

Taker 2 completes order:
- Order status: success
- Dev fee paid: 310 sats (correct amount for actual trade)
```

**Implementation Requirements:**

When order status transitions back to `pending` from any intermediate state (`waiting-buyer-invoice`, `waiting-payment`, etc.):

```rust
// Reset order state for market price orders
if order.is_market_price() {
    order.dev_fee = 0;  // Reset to zero
    order.status = Status::Pending;
    order.update(pool).await?;
}
```

**Database Consistency:**

Orders in `pending` status should always have `dev_fee = 0` for market price orders. You can verify this:

```sql
-- Should return 0 rows (all pending market orders should have dev_fee = 0)
SELECT id, premium, dev_fee
FROM orders
WHERE status = 'pending'
  AND premium IS NULL  -- market price indicator
  AND dev_fee != 0;
```

### Dev Fee Calculation at Take Time

**Implementation Status:** ✅ IMPLEMENTED - Unified calculation for both fixed price and market price orders

**Critical Implementation Detail:**

When ANY order is taken (both fixed price and market price), the `dev_fee` is calculated. This ensures consistent behavior across all order types.

**Locations:**
- `/home/negrunch/dev/mostro/src/app/take_buy.rs`
- `/home/negrunch/dev/mostro/src/app/take_sell.rs`

**Actual Implementation (take_buy.rs and take_sell.rs):**
```rust
// For market price orders: calculate amount, fee, and dev_fee
if order.has_no_amount() {
    match get_market_amount_and_fee(order.fiat_amount, &order.fiat_code, order.premium).await {
        Ok(amount_fees) => {
            order.amount = amount_fees.0;
            order.fee = amount_fees.1;
            let total_mostro_fee = order.fee * 2;
            order.dev_fee = get_dev_fee(total_mostro_fee);
        }
        Err(_) => return Err(MostroInternalErr(ServiceError::WrongAmountError)),
    };
} else {
    // For fixed price orders: calculate dev_fee only (amount and fee already set at creation)
    let total_mostro_fee = order.fee * 2;
    order.dev_fee = get_dev_fee(total_mostro_fee);
}
```

**Why This Is Critical:**

1. **Unified Behavior:** Both fixed price and market price orders calculate `dev_fee` at the same time (when taken)
2. **Consistency:** All pending orders have `dev_fee = 0`, all taken orders have `dev_fee > 0`
3. **Simplicity:** Single point of calculation makes the code easier to understand and maintain
4. **Correctness:** Ensures all order types have correct dev_fee for payment collection

**Example Scenarios:**

**Fixed Price Order:**
```
Order Created:
- amount: 100,000 sats (known at creation)
- fee: 1,000 sats (calculated at creation)
- dev_fee: 0 (NOT calculated at creation anymore)
- Status: pending

Order Taken:
- amount: 100,000 sats (unchanged)
- fee: 1,000 sats (unchanged)
- total_mostro_fee: 2,000 sats (both parties)
- dev_fee: 600 sats (30%)        ← Calculated when taken
- Status: waiting-buyer-invoice

Order Completes:
- Dev fee payment triggered: 600 sats sent to dev fund ✓
```

**Market Price Order:**
```
Order Created:
- Fiat: $100 USD
- amount: 0 (unknown until taken)
- fee: 0
- dev_fee: 0
- Status: pending

Order Taken:
- Market Price Lookup: $100 @ $50,000 = 200,000 sats
- amount: 200,000 sats           ← Calculated
- fee: 2,000 sats (1%)           ← Calculated
- total_mostro_fee: 4,000 sats (both parties)
- dev_fee: 1,200 sats (30%)      ← Calculated
- Status: waiting-buyer-invoice

Order Completes:
- Dev fee payment triggered: 1,200 sats sent to dev fund ✓
```

### Taker Abandonment and Order Reset

**Implementation Status:** ✅ IMPLEMENTED

**Critical Behavior:** When a taker abandons an order (doesn't proceed and order times out), all price-dependent fields must be reset to ensure correct recalculation when the order is re-taken.

**Two Reset Paths:**

1. **Explicit Cancellation** (`src/app/cancel.rs::reset_api_quotes()`):
   - Taker explicitly calls cancel action
   - Resets: `amount = 0`, `fee = 0`, `dev_fee = 0`
   - Status: ✅ Implemented

2. **Automatic Timeout** (`src/scheduler.rs::job_cancel_orders()`):
   - Scheduler detects taker hasn't proceeded within `expiration_seconds`
   - Resets: `amount = 0`, `fee = 0`, `dev_fee = 0`
   - Status: ✅ Implemented

**Why All Three Must Reset:**
- Market price can change between takes
- Fee is calculated from amount
- Dev fee is calculated from fee
- Leaving stale `dev_fee` value causes incorrect charges on re-take

**Example Flow:**
```
Order Created (market price):
- amount: 0, fee: 0, dev_fee: 0, status: pending

Order Taken at BTC=$50,000:
- amount: 200,000 sats, fee: 2,000 sats, dev_fee: 600 sats
- status: waiting-buyer-invoice

Taker Abandons (timeout after expiration_seconds):
- Scheduler detects timeout (taken_at > expiration_seconds)
- Resets: amount: 0, fee: 0, dev_fee: 0
- status: pending (ready for new taker)

Order Re-taken at BTC=$52,000 (price increased):
- amount: 192,308 sats, fee: 1,923 sats, dev_fee: 577 sats
- Correct dev_fee for new market price ✓
```

**Implementation Details:**
- `src/app/cancel.rs`: `reset_api_quotes()` function
- `src/scheduler.rs`: Automatic timeout handler in `job_cancel_orders()`
- Both paths use same logic:
  ```rust
  if order.price_from_api {
      order.amount = 0;
      order.fee = 0;
      order.dev_fee = 0;
  }
  ```
- Database function: `update_order_to_initial_state()` persists the reset values

**Database Persistence Fix:**

Prior to commit `c803471`, the `update_order_to_initial_state()` function in `src/db.rs`
did not include `dev_fee` in its SQL UPDATE statement, causing stale dev_fee values to
remain in the database even though the in-memory Order struct had `dev_fee = 0`.

**Before Fix:**
- Memory: `order.dev_fee = 0` ✓
- Database: `dev_fee = 300` (stale) ✗
- After `edit_pubkeys_order()` fetches from DB: `order.dev_fee = 300` (wrong!) ✗

**After Fix:**
- Memory: `order.dev_fee = 0` ✓
- Database: `dev_fee = 0` ✓
- After `edit_pubkeys_order()` fetches from DB: `order.dev_fee = 0` ✓

The fix added `dev_fee` as a parameter to `update_order_to_initial_state()` and
included it in the SQL UPDATE statement, ensuring the value is properly persisted
to the database.

**Configuration:**
- Timeout duration: Configured via `expiration_seconds` in settings
- Default: Orders return to pending after taker hasn't proceeded for configured time

### Hold Invoice Generation

**Current State:** ✅ IMPLEMENTED - Hold invoices include only the Mostro fee (no dev fee).

**Implementation:** `src/util.rs::show_hold_invoice()`

Seller's hold invoice includes only the order amount and Mostro fee:
```rust
// Seller pays the order amount plus their Mostro fee
// Dev fee is NOT charged to seller - it's paid by mostrod from its earnings
let new_amount = order.amount + order.fee;

// Now we generate the hold invoice that seller should pay
let (invoice_response, preimage, hash) = ln_client...
```

**Key Points:**
- `order.dev_fee` stores the total dev fee for tracking purposes only
- Seller pays only `order.amount + order.fee` (no dev_fee added)
- Buyer receives only `order.amount - order.fee` (no dev_fee subtracted)
- Hold invoice amount = `order.amount + order.fee` (transparent, as advertised)
- Mostrod pays `dev_fee` from its earnings after collecting the Mostro fee

### Message Amount Calculations

**Implementation Status:** ✅ COMPLETE

When creating messages for buyers and sellers during order flow, the amounts include only the Mostro fee (not dev_fee). The implementation ensures correct amounts are communicated to both parties.

**Seller Messages:**
```rust
seller_order.amount = order.amount.saturating_add(order.fee);
```

**Buyer Messages:**
```rust
buyer_order.amount = order.amount.saturating_sub(order.fee);
```

**Critical Implementation Locations:**

1. **`src/flow.rs::hold_invoice_paid()`**
   - Purpose: Status updates after seller payment
   - Seller amount: `order.amount + order.fee` (no dev_fee)
   - Buyer amount: `order.amount - order.fee` (no dev_fee)
   - Impact: Initial payment confirmation messages

2. **`src/app/add_invoice.rs::add_invoice_action()`**
   - Purpose: Invoice acceptance flow
   - Seller amount: `order.amount + order.fee` (no dev_fee)
   - Buyer amount: `order.amount - order.fee` (no dev_fee)
   - Impact: Order acceptance notifications

3. **`src/app/release.rs::check_failure_retries()`**
   - Purpose: Payment failure handling
   - Buyer amount: `order.amount - order.fee` (no dev_fee)
   - Impact: Failure notification amounts

4. **`src/app/release.rs::do_payment()`** ⚠️ **CRITICAL**
   - Purpose: Actual Lightning payment calculation
   - Payment amount: `order.amount - order.fee` (no dev_fee)
   - Impact: **Real sats transferred** to buyer via Lightning
   - Why critical: Determines actual payment amount, not just messages

**Implementation Pattern:**

All locations follow the same simplified pattern:
```rust
// No dev_fee split needed - users only pay Mostro fee
seller_amount = order.amount + order.fee;
buyer_amount = order.amount - order.fee;
```

### Example Calculation

**Order Amount**: 100,000 sats
**Mostro Fee (1%)**: 1,000 sats (split: 500 buyer + 500 seller)
**Dev Fee Percentage**: 30%
**Total Dev Fee**: 1,000 × 0.30 = 300 sats

**Seller Pays**: 100,000 + 500 = **100,500 sats**
**Buyer Receives**: 100,000 - 500 = **99,500 sats**

**Mostrod pays**: 300 sats (to development fund)

**Fee Distribution:**
- Buyer pays: 500 (Mostro fee) = **500 sats total**
- Seller pays: 500 (Mostro fee) = **500 sats total**
- Mostrod receives: 1,000 - 300 = **700 sats** (keeps after donating to dev fund)
- Dev fund receives: **300 sats** (paid by mostrod from its earnings)

### Edge Cases

**Rounding**:
- Total: 333 sats Mostro fee × 30% = 99.9 → **100 sats dev fee** (paid by mostrod)
- Total: 3 sats Mostro fee × 30% = 0.9 → **1 sat dev fee** (paid by mostrod)
- **Odd or even numbers**: No longer matters, mostrod pays the total rounded amount
- Formula: `dev_fee = round(total_mostro_fee × dev_fee_percentage)`
- Implementation: `src/util.rs`

**Zero Fee Orders**:
- If `mostro_fee = 0`, then `dev_fee = 0`
- No dev payment attempted

**Tiny Amounts**:
- Smallest: 1 sat Mostro fee × 10% = 0.1 → **0 sats** dev fee (rounds to zero)
- No dev payment attempted for 0 sat dev fees

### Payment Execution

**Current State:** ✅ IMPLEMENTED - Automated dev fee payment system fully operational with scheduler-based processing.

**Implementation:**

**Scheduler-Based Payment Trigger:**

The dev fee payment is executed by mostrod **from its earnings**, not from users. The payment happens asynchronously:

1. **Order Release** (`src/app/release.rs::release_action()`):
   - Seller's hold invoice is settled
   - Order is marked as `status = 'settled-hold-invoice'`
   - Mostrod collects the Mostro fee (1,000 sats in our example)
   - Order is **enqueued for scheduler processing** by marking `dev_fee_paid = false`
   - Mostro then proceeds to pay buyer's invoice
   - **Key Point:** Dev fee payment happens asynchronously AFTER mostrod collects the Mostro fee

2. **Scheduler Processing** (`src/scheduler.rs::job_process_dev_fee_payment()`):
   - Runs every 60 seconds
   - Uses `find_unpaid_dev_fees()` to query database for orders where: `(status = 'settled-hold-invoice' OR status = 'success') AND dev_fee > 0 AND dev_fee_paid = 0`
   - **Important:** Query checks BOTH statuses to handle race conditions where buyer payment succeeds during dev fee payment
   - Processes each unpaid dev fee asynchronously with 50-second timeout per payment attempt
   - Mostrod pays the dev_fee amount (e.g., 300 sats) from its earnings to the development fund
   - Enhanced logging: Logs BEFORE/AFTER state, database update results, and verification queries

**Why This Timing?**
- **Fee Earned:** Seller has released funds, so mostrod has earned the Mostro fee
- **Contribution from earnings:** Mostrod donates a percentage of what it earned to the dev fund
- **Risk Mitigation:** Dev fee payment independent of buyer payment status
- **Non-blocking:** Order flow continues regardless of dev fee payment status
- **Retry mechanism:** Failed payments are automatically retried every 60 seconds

**Why Scheduler-Based?**
- **Non-blocking order completion:** Seller release and buyer payment happen immediately, dev fee payment happens asynchronously
- **Retry mechanism:** Failed payments are automatically retried on the next cycle (60 seconds)
- **Fault tolerance:** Order completes successfully even if dev fee payment fails temporarily
- **Better user experience:** Users don't wait for dev fee payment during order release

**Payment Flow Specification (4 Steps with Timeouts):**

Implementation in `src/app/release.rs::send_dev_fee_payment()`:

```rust
// [Step 0] Validation - Reject invalid amounts
if order.dev_fee <= 0 {
    return Err(MostroInternalErr(ServiceError::WrongAmountError));
}

// [Step 1/4] LNURL resolution (15 second timeout)
let payment_request = tokio::time::timeout(
    std::time::Duration::from_secs(15),
    resolv_ln_address(DEV_FEE_LIGHTNING_ADDRESS, dev_fee_amount)
).await?;

// [Step 2/4] Create LND connector
let ln_client = LndConnector::new().await?;

// [Step 3/4] Send payment (5 second timeout for send_payment call + 25 second timeout for payment result)
let send_result = tokio::time::timeout(
    std::time::Duration::from_secs(5),
    ln_client.send_payment(&payment_request, dev_fee_amount, tx)
).await?;

// Wait for payment result (25 second timeout)
let payment_result = tokio::time::timeout(
    std::time::Duration::from_secs(25),
    rx.recv()
).await?;
```

**Total Time Budget:**
- Validation: Instant (< 1ms)
- LNURL resolution: 15s max
- send_payment call: 5s max (prevents hanging on self-payments or network issues)
- Payment result wait: 25s max
- **Total: ~45s max** (under 50s scheduler timeout)

**Validation Note:** The function validates `dev_fee > 0` before attempting payment. This was added in commit eaf3319 to prevent unnecessary payment attempts for orders with zero dev fees.

**Success Response:**
```rust
Ok(hash) => {
    order.dev_fee_paid = true;
    order.dev_fee_payment_hash = Some(hash);
    // Database updated, won't be retried
}
```

**Failure Response:**
```rust
Err(e) => {
    order.dev_fee_paid = false;
    // Logged for audit
    // Will be retried on next scheduler cycle (60 seconds)
}
```

### Database Field Updates

This section details exactly when and how the database fields (`dev_fee`, `dev_fee_paid`, `dev_fee_payment_hash`) are modified throughout the order lifecycle.

#### `dev_fee` Field Lifecycle

**When Initialized:** During order creation in `src/util.rs::prepare_new_order()`

**Initialization:**
```rust
let mut fee = 0;
let dev_fee = 0;
if new_order.amount > 0 {
    fee = get_fee(new_order.amount);
    // dev_fee is NOT calculated here — always initialized to 0
    // It is calculated later when the order is taken
}
```

**Initial Value:** Always 0 (zero) — dev fee is calculated later when the order is taken (in `take_buy.rs` / `take_sell.rs`), not at creation time.

**Purpose:** Tracking amount that mostrod will pay to the development fund from its earnings (not charged to users)

**Special Case - Market Price Orders:** When a market price order returns to `pending` status (taker abandons), the `dev_fee` field **MUST** be reset to `0` to allow recalculation at the new market price when re-taken. This is documented in detail in the "Market Price Orders and Dev Fee Reset" section.

**Database State:** Persists throughout order lifecycle unless order returns to pending status (market price orders only).

#### `dev_fee_paid` Field Updates

**Initial Value:** `0` (false) - Set during order creation in `src/util.rs::prepare_new_order()`

**When Changed to `1` (true):** After successful dev fee payment in the scheduler job `process_dev_fee_payment()` in `src/scheduler.rs`

**Trigger Sequence:**
1. Seller releases order (`status = 'settled-hold-invoice'`)
2. Scheduler runs every 60 seconds
3. Query identifies unpaid orders: `SELECT * FROM orders WHERE (status = 'settled-hold-invoice' OR status = 'success') AND dev_fee > 0 AND dev_fee_paid = 0`
4. Scheduler calls `send_dev_fee_payment()` for each unpaid order
5. On payment success, scheduler updates: `order.dev_fee_paid = true` (stored as `1` in database)
6. Important: Query includes BOTH statuses to handle race conditions where buyer payment completes during dev fee payment failure

**Database Update:**
```sql
UPDATE orders
SET dev_fee_paid = 1, dev_fee_payment_hash = ?
WHERE id = ?
```

**Timing:** Asynchronously after order completes, typically within 60 seconds (next scheduler cycle)

**Remains `0` When:**
- Payment hasn't been attempted yet (order just completed)
- Payment failed (LNURL resolution error, routing failure, timeout)
- Will retry on next scheduler cycle (60 seconds)

#### `dev_fee_payment_hash` Field Updates

**Initial Value:** `NULL` - Set during order creation in `src/util.rs::prepare_new_order()`

**When Set:** Simultaneously with `dev_fee_paid = 1` after successful payment

**Value Source:** Lightning payment hash returned from the Lightning Network payment result. The hash comes from the `ln_client.send_payment()` call's result channel in `src/app/release.rs::send_dev_fee_payment()`.

**Payment Hash Retrieval Flow:**
```rust
// Step 1: LNURL resolution (15s timeout)
let payment_request = resolv_ln_address(DEV_FEE_LIGHTNING_ADDRESS, dev_fee_amount).await?;

// Step 2: Send payment (5s timeout for send, 25s for result)
let payment_result = ln_client.send_payment(&payment_request, dev_fee_amount, tx).await?;

// Step 3: Extract hash from successful payment result
let payment_hash = rx.recv().await?;  // ← THIS is what goes into dev_fee_payment_hash
```

**Format:** 64-character hexadecimal string (standard Lightning payment hash)

**Purpose:**
- Audit trail for reconciliation
- Proof of payment for accountability
- Debugging and payment verification

**Remains `NULL` When:**
- Payment hasn't been attempted yet
- Payment failed (no hash generated for failed payments)

#### Payment Flow Timeline

Complete timeline showing database field states at each stage:

```
Order Creation (t=0):
  └─> dev_fee = 0 (always zero at creation)
  └─> dev_fee_paid = 0
  └─> dev_fee_payment_hash = NULL
  └─> Database: INSERT INTO orders (dev_fee, dev_fee_paid, dev_fee_payment_hash) VALUES (0, 0, NULL)

Order Taken (t=take):
  └─> dev_fee = calculated_value (e.g., 300 sats)
  └─> Calculated in take_buy.rs / take_sell.rs based on total_mostro_fee × dev_fee_percentage
  └─> Database: UPDATE orders SET dev_fee = 300 WHERE id = ?

Order Processing (t=minutes):
  └─> Buyer and seller complete trade
  └─> Dev fee fields remain unchanged
  └─> Database: No updates to dev_fee fields

Order Release (t=seller_release):
  └─> Seller's hold invoice settled
  └─> status = 'settled-hold-invoice'
  └─> Dev fee fields unchanged (dev_fee_paid = 0)
  └─> Order enters scheduler queue
  └─> Buyer payment initiated (asynchronous)

Scheduler Cycle (t=next_60s_cycle, typically within 60s of seller release):
  └─> Scheduler wakes up every 60 seconds
  └─> Query: SELECT * FROM orders WHERE (status = 'settled-hold-invoice' OR status = 'success') AND dev_fee > 0 AND dev_fee_paid = 0
  └─> Order found in unpaid queue
  └─> Call: send_dev_fee_payment(order)

Payment Attempt (t=payment_start):
  └─> Step 1/3: LNURL resolution (timeout: 15s)
  └─> Step 2/3: LND send_payment call (timeout: 5s)
  └─> Step 3/3: Wait for payment result (timeout: 25s)

Dev Fee Payment Success (t=payment_complete, ~3-8 seconds typical):
  └─> Payment hash received: "a1b2c3d4e5f6..."
  └─> dev_fee_paid = 1
  └─> dev_fee_payment_hash = "a1b2c3d4e5f6..."
  └─> Database: UPDATE orders SET dev_fee_paid = 1, dev_fee_payment_hash = 'a1b2c3d4e5f6...' WHERE id = ?
  └─> Order removed from retry queue
  └─> DONE ✓

Order Success (t=order_complete, after dev fee payment):
  └─> status = 'success'
  └─> Buyer receives satoshis
  └─> Dev fee already paid (dev_fee_paid = 1)
  └─> Database: UPDATE orders SET status = 'success' WHERE id = ?

Dev Fee Payment Failure (t=payment_timeout, could be 15s, 25s, or 50s timeout):
  └─> Error logged (LNURL failure, routing failure, timeout, etc.)
  └─> dev_fee_paid = 0 (unchanged)
  └─> dev_fee_payment_hash = NULL (unchanged)
  └─> Database: No update (fields remain unchanged)
  └─> Order remains in retry queue
  └─> Retry on next scheduler cycle (60 seconds later)
  └─> Will retry indefinitely until payment succeeds

Edge Case - Buyer Payment Fails:
  └─> Order remains in status = 'settled-hold-invoice'
  └─> failed_payment = true
  └─> Retry scheduler (job_retry_failed_payments) attempts buyer payment again
  └─> Dev fee already paid regardless (dev_fee_paid = 1)
  └─> This is correct: seller released, fee was earned

Edge Case - Dev Fee Payment Fails, Buyer Payment Succeeds (Race Condition):
  └─> Seller releases → status = 'settled-hold-invoice'
  └─> Scheduler attempts dev fee payment → FAILS (dev_fee_paid = 0)
  └─> Simultaneously, buyer payment → SUCCEEDS → status = 'success'
  └─> Order now in 'success' status with dev_fee_paid = 0
  └─> Query includes both statuses, so order is still picked up on next cycle
  └─> Dev fee payment eventually succeeds and updates dev_fee_paid = 1
```

#### Actual Implementation

The actual implementation in `src/scheduler.rs::job_process_dev_fee_payment()` includes enhanced logging:

```rust
/// Process unpaid development fees for successful orders
/// Called every 60 seconds by scheduler
async fn job_process_dev_fee_payment(ctx: AppContext) {
    let interval = 60u64; // Every 60 seconds

    tokio::spawn(async move {
        let pool = ctx.pool(); // Get pool from AppContext
        loop {
            info!("Checking for unpaid development fees");

            // Query unpaid orders using find_unpaid_dev_fees()
            // Query: WHERE (status = 'settled-hold-invoice' OR status = 'success')
            //        AND dev_fee > 0 AND dev_fee_paid = 0
            if let Ok(unpaid_orders) = find_unpaid_dev_fees(&pool).await {
                info!("Found {} orders with unpaid dev fees", unpaid_orders.len());

                for mut order in unpaid_orders {
                    // Attempt payment with 50-second timeout (under 60s cycle)
                    match tokio::time::timeout(
                        std::time::Duration::from_secs(50),
                        send_dev_fee_payment(&order),
                    )
                    .await
                    {
                        Ok(Ok(payment_hash)) => {
                            // SUCCESS: Update both fields atomically
                            let order_id = order.id;
                            let dev_fee_amount = order.dev_fee;

                            // Enhanced logging - BEFORE state
                            info!(
                                "BEFORE UPDATE: order_id={}, dev_fee_paid={}, dev_fee_payment_hash={:?}",
                                order_id, order.dev_fee_paid, order.dev_fee_payment_hash
                            );

                            order.dev_fee_paid = true;
                            order.dev_fee_payment_hash = Some(payment_hash.clone());

                            // Enhanced logging - AFTER modification
                            info!(
                                "AFTER MODIFY: order_id={}, dev_fee_paid={}, dev_fee_payment_hash={:?}",
                                order_id, order.dev_fee_paid, order.dev_fee_payment_hash
                            );

                            match order.update(&pool).await {
                                Err(e) => {
                                    error!(
                                        "❌ DATABASE UPDATE FAILED for order {}: {:?}",
                                        order_id, e
                                    );
                                }
                                Ok(_) => {
                                    info!("✅ DATABASE UPDATE SUCCEEDED for order {}", order_id);

                                    // Verification query - confirm database persistence
                                    if let Ok(verified_order) = sqlx::query_as::<_, Order>(
                                        "SELECT * FROM orders WHERE id = ?",
                                    )
                                    .bind(order_id)
                                    .fetch_one(&*pool)
                                    .await
                                    {
                                        info!(
                                            "VERIFICATION: order_id={}, dev_fee_paid={}, dev_fee_payment_hash={:?}",
                                            verified_order.id,
                                            verified_order.dev_fee_paid,
                                            verified_order.dev_fee_payment_hash
                                        );
                                    }

                                    info!(
                                        "Dev fee payment succeeded for order {} - amount: {} sats, hash: {}",
                                        order_id, dev_fee_amount, payment_hash
                                    );
                                }
                            }
                        }
                        Ok(Err(e)) => {
                            // FAILURE: Leave fields unchanged for retry
                            error!(
                                "Dev fee payment failed for order {} ({} sats) - error: {:?}, will retry",
                                order.id, order.dev_fee, e
                            );
                        }
                        Err(_) => {
                            // TIMEOUT: Leave fields unchanged for retry
                            error!(
                                "Dev fee payment timeout (50s) for order {} ({} sats) - will retry",
                                order.id, order.dev_fee
                            );
                        }
                    }
                }
            }

            tokio::time::sleep(tokio::time::Duration::from_secs(interval)).await;
        }
    });
}
```

**Key Implementation Points:**

1. **Atomic Updates:** Always update `dev_fee_paid` and `dev_fee_payment_hash` together in a single database transaction
2. **Only Update on Success:** Never update these fields on payment failure - leave them unchanged for retry
3. **Dual Status Query:** Query checks BOTH `'settled-hold-invoice'` AND `'success'` statuses to handle race conditions
4. **Enhanced Logging:** BEFORE/AFTER/VERIFY pattern provides complete diagnostic trail
5. **Database Verification:** After update, re-query database to confirm fields were persisted correctly
6. **Automatic Retry:** Failed payments remain with `dev_fee_paid = 0`, causing them to be retried on the next cycle (60s)
7. **Non-Blocking:** Order completion is never blocked by dev fee payment attempts
8. **Error Categorization:** Separate handling for payment failures vs timeouts with specific error messages

### Error Handling

**Payment Failures:**
- LNURL resolution failure (timeout: 15 seconds)
- LND send_payment hanging (timeout: 5 seconds)
- LND connection error
- Payment routing failure
- Payment result timeout (25 seconds)
- Scheduler timeout (50 seconds total)

**Response:** All errors logged but order completes successfully. Failed payments are automatically retried on next scheduler cycle (60 seconds).

**Common Error Scenarios:**

1. **Self-Payment Attempts:** When dev fee destination uses same Lightning node as Mostro, LND may hang trying to route payment. The 5-second timeout on `send_payment()` prevents indefinite blocking.

2. **LNURL Resolution Failures:** Network issues or DNS problems resolving `DEV_FEE_LIGHTNING_ADDRESS`. 15-second timeout ensures fast failure.

3. **Routing Failures:** No route found to destination or insufficient liquidity. Payment fails after attempting routing for up to 25 seconds.

## Database Schema

### Migration: `migrations/20251126120000_dev_fee.sql`

```sql
ALTER TABLE orders ADD COLUMN dev_fee INTEGER DEFAULT 0;
ALTER TABLE orders ADD COLUMN dev_fee_paid INTEGER NOT NULL DEFAULT 0;
ALTER TABLE orders ADD COLUMN dev_fee_payment_hash CHAR(64);
```

### Field Descriptions

| Column | Type | Default | Description |
|--------|------|---------|-------------|
| `dev_fee` | INTEGER | 0 | Development fee amount in satoshis |
| `dev_fee_paid` | INTEGER | 0 | Boolean: 0 = failed/not paid, 1 = paid |
| `dev_fee_payment_hash` | CHAR(64) | NULL | Lightning payment hash for reconciliation |

### Backward Compatibility

- Existing orders: dev_fee = 0, dev_fee_paid = 0
- No migration required for existing data
- Daemon handles NULL/zero values gracefully

## Implementation Roadmap

This section provides a checklist for implementing the remaining phases of the development fee feature.

### Phase 2: Fee Calculation ✅ COMPLETE

**Prerequisites:** Phase 1 complete ✅

**Implementation Tasks:**
- [x] Implement `calculate_dev_fee()` pure function in `src/util.rs`
  - Input: `total_mostro_fee: i64, percentage: f64`
  - Output: `i64` (rounded dev fee amount)
  - Logic: `(total_mostro_fee as f64) * percentage`, rounded
- [x] Implement `get_dev_fee()` wrapper function in `src/util.rs`
  - Input: `total_mostro_fee: i64`
  - Output: `i64` (calls calculate_dev_fee with Settings percentage)
- [x] Implement dev_fee calculation (for tracking mostrod's donation amount)
  - Formula: `dev_fee = round(total_mostro_fee × dev_fee_percentage)`
  - Simple rounding for whole satoshi amounts
- [x] Update message creation in `src/flow.rs::hold_invoice_paid()`
  - Seller amount: `order.amount + order.fee`
  - Buyer amount: `order.amount - order.fee`
- [x] Update message creation in `src/app/add_invoice.rs::add_invoice_action()`
  - No dev_fee in user-facing amounts
- [x] Update payment calculation in `src/app/release.rs::check_failure_retries()`
- [x] Update Lightning payment in `src/app/release.rs::do_payment()` ⚠️ CRITICAL
  - Dev fee paid separately by mostrod
- [x] Add unit tests for `calculate_dev_fee()` in `src/util.rs::tests`
  - Test `test_get_dev_fee_basic`: Standard calculation (1000 @ 30% = 300)
  - Test `test_get_dev_fee_rounding`: Rounding (333 @ 30% = 100)
  - Test `test_get_dev_fee_zero`: Zero fee (0 → 0)
  - Test `test_get_dev_fee_tiny_amounts`: Tiny amounts (1 @ 30% = 0)
  - All tests passing ✓
- [x] Integration testing with various order amounts
  - Verified correct amounts in all message flows
  - Verified correct Lightning payment amounts
  - Verified users only pay Mostro fee (not dev_fee)

**Deliverables:** ✅ All fee calculations implemented, tested, and integrated across entire order flow

### Phase 3: Payment Execution ✅ COMPLETE

**Prerequisites:** Phase 2 complete ✅

**Completed Implementation (Commits: f508669, 102cfed, eaf3319, 42253f1, 2655943):**
- [x] Implement `send_dev_fee_payment()` in `src/app/release.rs`
  - Step 0: Dev fee amount validation (`dev_fee > 0` check)
  - Step 1: LNURL resolution with 15-second timeout
    - Call: `resolv_ln_address(DEV_FEE_LIGHTNING_ADDRESS, amount)`
    - Error handling: Log and return error on timeout/failure
  - Step 2: Create LND connector
    - Call: `LndConnector::new().await`
  - Step 3: Send payment with 5-second timeout
    - Call: `ln_client.send_payment(&payment_request, amount, tx)`
    - Error handling: Timeout prevents hanging on self-payments
  - Step 4: Wait for payment result with 25-second timeout
    - Call: Loop receiving messages until terminal status
    - Success: Return payment hash
    - Failure: Return error with details
- [x] Create scheduler job `job_process_dev_fee_payment()` in `src/scheduler.rs`
  - Uses `find_unpaid_dev_fees()` database function (`src/db.rs`)
  - Query: `SELECT * FROM orders WHERE (status = 'settled-hold-invoice' OR status = 'success') AND dev_fee > 0 AND dev_fee_paid = 0`
  - **Key improvement:** Query checks BOTH statuses to handle race conditions (commit 102cfed)
  - For each unpaid order:
    - Call `send_dev_fee_payment()` with 50-second timeout
    - On success: Update `dev_fee_paid = 1`, `dev_fee_payment_hash = hash`
    - On failure: Log error, leave `dev_fee_paid = 0` for retry
  - Schedule: Run every 60 seconds
- [x] Enhanced logging (commit 2655943)
  - Logs BEFORE/AFTER state for dev_fee_paid and dev_fee_payment_hash
  - Database update success/failure logging
  - Verification queries to confirm database persistence
  - Info: Payment initiation, success with order_id, amount, hash
  - Error: Resolution failures, payment failures, timeouts with details
- [x] Error handling and retry logic
  - Dev fee validation before payment attempt (commit eaf3319)
  - Self-payment detection (5s timeout prevents hanging)
  - LNURL resolution failures (15s timeout)
  - Routing failures (25s timeout)
  - Scheduler timeout (50s total)
  - All errors: Log and allow automatic retry on next cycle (60s)
- [x] Market price order dev_fee calculation (commit 2655943)
  - Implemented in `take_buy.rs` and `take_sell.rs`
  - Calculates `dev_fee = get_dev_fee(fee * 2)` when order amount is determined
  - Fixes bug where market price orders had `dev_fee = 0` permanently
- [x] Integration testing
  - Tested successful dev fee payment flow
  - Tested LNURL resolution failure handling
  - Tested payment timeout scenarios
  - Tested scheduler retry mechanism
  - Verified order completes regardless of dev fee payment status
  - Fixed edge cases through commits 42253f1, 102cfed, 2655943

**Deliverables:** ✅ Automated dev fee payment system fully operational with scheduler-based processing, enhanced logging, race condition handling, and automatic retry mechanism

### Phase 4: Audit Events via Nostr ✅ COMPLETE

**Purpose:** Provide transparent, verifiable audit trail of all dev fee payments through Nostr relays.

**What Was Implemented:**
- Custom Nostr event kind (8383) for dev fee payment audits
- Event publishing in scheduler after successful payment
- Complete payment details: amount, hash, order reference, timestamp
- Public relay distribution for third-party verification
- Queryable tags for analytics and reporting

**Event Specification:**

| Property | Value |
|----------|-------|
| Event Kind | 8383 (Regular Event) |
| Replaceability | No - complete audit trail |
| Published After | Successful dev fee payment & DB update |
| Content Format | JSON with structured payment data |
| Tags | `y`, `z`, `order`, `amount`, `hash`, `t`, `currency`, `network` |

**Event Kind Rationale:**

Why kind 8383 (Regular Event)?
- ✅ **Complete History:** Every payment is a separate, permanent event
- ✅ **Third-Party Auditing:** Anyone can query all historical payments
- ✅ **Total Calculation:** Sum all `amount` tags to get total dev fund contributions
- ✅ **Immutable Record:** Events cannot be replaced or deleted
- ✅ **Standard Compliance:** Follows NIP-01 application-specific event range (1000-9999)

**Event Structure Example:**

```json
{
  "kind": 8383,
  "content": {
    "order_id": "550e8400-e29b-41d4-a716-446655440000",
    "dev_fee_sats": 100,
    "payment_hash": "abc123...",
    "payment_timestamp": 1234567890,
    "destination": "dev@getalby.com",
    "order_amount_sats": 10000,
    "order_fiat_amount": 50,
    "order_fiat_code": "USD",
    "status": "success"
  },
  "tags": [
    ["y", "mostro"],
    ["z", "dev-fee-payment"],
    ["order", "550e8400-e29b-..."],
    ["amount", "100"],
    ["hash", "abc123..."],
    ["t", "audit"],
    ["t", "dev-fund"],
    ["currency", "USD"],
    ["network", "mainnet"]
  ]
}
```

**Query Examples:**

```javascript
// Get all dev fee payments
const filter = {
  kinds: [8383],
  "#y": ["mostro"],
  "#z": ["dev-fee-payment"]
};

// Calculate total dev fund contributions
let total = 0;
events.forEach(event => {
  const amountTag = event.tags.find(t => t[0] === "amount");
  if (amountTag) total += parseInt(amountTag[1]);
});

// Filter by currency
const usdPayments = {
  kinds: [8383],
  "#currency": ["USD"]
};

// Find payments for specific order
const orderPayments = {
  kinds: [8383],
  "#order": ["550e8400-e29b-41d4-a716-446655440000"]
};
```

**Implementation Details:**

**Location:** `src/scheduler.rs::job_process_dev_fee_payment()` (after payment success)

**Function:** `publish_dev_fee_audit_event(order: &Order, payment_hash: &str)`

**Error Handling:** Audit event failures are logged but don't fail the payment transaction

**Retry Logic:** None - if event publish fails, payment still succeeds (prioritize financial reliability)

**Privacy Considerations:**
- Order ID included for transparency
- Buyer/seller pubkeys NOT included (privacy)
- Only aggregate payment data published

**Benefits:**

1. **Transparency:** Anyone can verify dev fund contributions
2. **Accountability:** Public record of all fee payments
3. **Analytics:** Query by currency, date range, network
4. **Trust:** Third-party auditing without Mostro access
5. **Compliance:** Verifiable fee collection for reporting

**Testing:**

```bash
# Query dev fee events from relay
nostr-cli -k 8383 --tag y=mostro --tag z=dev-fee-payment

# Calculate total contributions
nostr-cli -k 8383 --tag y=mostro | jq '[.[] | .tags[] | select(.[0]=="amount") | .[1] | tonumber] | add'
```

**Status:** ✅ Complete - Dev fee audit events are now published to Nostr relays

**Implementation:**
1. ✅ Added `DEV_FEE_AUDIT_EVENT_KIND` constant to `src/config/constants.rs`
2. ✅ Created `publish_dev_fee_audit_event()` function in `src/util.rs`
3. ✅ Integrated event publishing in `src/scheduler.rs` after successful payment
4. ✅ Non-blocking implementation - event failures don't affect payment
5. ✅ Events are queryable via standard Nostr clients and relays

**Future Enhancements:**
- Aggregate statistics event (kind 38100) updated monthly with totals
- Dashboard for visualizing dev fund contributions
- NIP-05 verification for Mostro's audit event pubkey

## Monitoring and Operations

### Statistics Queries

**Unpaid Development Fees:**
```sql
-- Same query used by find_unpaid_dev_fees() in src/db.rs:895-908
SELECT id, dev_fee, created_at, status
FROM orders
WHERE (status = 'settled-hold-invoice' OR status = 'success')
  AND dev_fee > 0
  AND dev_fee_paid = 0
ORDER BY created_at DESC;

-- IMPORTANT: Query checks BOTH statuses (settled-hold-invoice AND success)
-- Reason: Handles race condition where buyer payment completes while dev fee payment is processing
-- Without both statuses: Orders could get stuck with unpaid dev fees if buyer pays before dev fee completes
-- Result: Ensures all dev fees are eventually collected regardless of payment timing
```

**Development Fee Summary:**
```sql
SELECT
  COUNT(*) as total_orders,
  SUM(dev_fee) as total_dev_fees_sats,
  SUM(CASE WHEN dev_fee_paid = 1 THEN dev_fee ELSE 0 END) as paid_sats,
  SUM(CASE WHEN dev_fee_paid = 0 THEN dev_fee ELSE 0 END) as unpaid_sats,
  ROUND(100.0 * SUM(CASE WHEN dev_fee_paid = 1 THEN 1 ELSE 0 END) / COUNT(*), 2) as success_rate
FROM orders
WHERE status = 'success' AND dev_fee > 0;
```

**Recent Failures:**
```sql
-- Orders with unpaid dev fees older than 5 minutes (potential issues):
-- IMPORTANT: Uses dual-status query like find_unpaid_dev_fees()
SELECT id, dev_fee, dev_fee_paid, status, failed_payment, payment_attempts, created_at
FROM orders
WHERE (status = 'settled-hold-invoice' OR status = 'success')
  AND dev_fee > 0
  AND dev_fee_paid = 0
  AND created_at < strftime('%s', 'now', '-5 minutes')
ORDER BY created_at DESC;

-- Orders with unpaid dev fees in last 24 hours:
-- Useful for monitoring recent payment failures
SELECT id, dev_fee, created_at, status, dev_fee_paid
FROM orders
WHERE (status = 'settled-hold-invoice' OR status = 'success')
  AND dev_fee > 0
  AND dev_fee_paid = 0
  AND created_at > strftime('%s', 'now', '-24 hours')
ORDER BY created_at DESC;

-- Check for orders stuck in settled-hold-invoice with unpaid dev fees:
-- These should be picked up by scheduler every 60 seconds
SELECT id, dev_fee, status, dev_fee_paid, created_at,
       (strftime('%s', 'now') - created_at) / 60 as minutes_since_creation
FROM orders
WHERE status = 'settled-hold-invoice'
  AND dev_fee > 0
  AND dev_fee_paid = 0
ORDER BY created_at DESC;
```

### Log Filtering

**View all dev fee logs:**
```bash
RUST_LOG="dev_fee=debug" mostrod
```

**View only errors:**
```bash
RUST_LOG="dev_fee=error" mostrod
```

**Log Examples:**

Success:
```
[INFO dev_fee] order_id=550e8400-e29b-41d4-a716-446655440000 amount_sats=300 destination=<dev@lightning.address> Initiating development fee payment
[INFO dev_fee] order_id=550e8400-e29b-41d4-a716-446655440000 payment_hash=abcd1234... Development fee payment succeeded
```

Failure:
```
[ERROR dev_fee] order_id=550e8400-e29b-41d4-a716-446655440000 error=LnAddressParseError stage=address_resolution Failed to resolve development Lightning Address
[ERROR dev_fee] order_id=550e8400-e29b-41d4-a716-446655440000 dev_fee=300 Development fee payment failed - order completing anyway
```

## Troubleshooting

### Common Issues

**1. Daemon Won't Start - Invalid Configuration**
```
Error: Configuration error: dev_fee_percentage (0.05) is below minimum (0.10)
```
**Solution:** Set `dev_fee_percentage` to at least 0.10 in settings.toml

**2. High Failure Rate**
- Check Lightning node connectivity
- Verify `<dev@lightning.address>` is reachable
- Check routing capacity to destination
- Review error logs: `RUST_LOG="dev_fee=error" mostrod`

**3. Payment Timeouts**
- LNURL resolution timeout: 15 seconds (indicates DNS/network issues)
- send_payment timeout: 5 seconds (indicates LND hanging, often self-payment attempts)
- Payment result timeout: 25 seconds (indicates routing issues or network congestion)
- Total scheduler timeout: 50 seconds
- Orders still complete successfully regardless of dev fee payment failures
- Failed payments automatically retry every 60 seconds via scheduler

**4. Market Price Orders With Zero Dev Fee**

**Symptom:**
```sql
-- Orders with fee but no dev_fee (indicates bug in market price flow)
SELECT id, amount, fee, dev_fee, price_from_api, status
FROM orders
WHERE fee > 0
  AND dev_fee = 0
  AND price_from_api = 1;
```

**Cause:** Market price order was taken but `dev_fee` was not calculated

**Impact:** Dev fee not collected from these orders (revenue loss)

**Fix:** Ensure both `take_buy.rs` and `take_sell.rs` calculate `dev_fee` when updating `amount` and `fee` for market price orders (see Market Price Order Dev Fee Calculation section)

**Verification:**
```sql
-- All market price orders should have consistent fees
SELECT
  COUNT(*) as total_market_orders,
  SUM(CASE WHEN fee > 0 AND dev_fee = 0 THEN 1 ELSE 0 END) as broken_orders,
  SUM(CASE WHEN fee > 0 AND dev_fee > 0 THEN 1 ELSE 0 END) as correct_orders
FROM orders
WHERE price_from_api = 1
  AND amount > 0;  -- Only count taken orders
```

**5. Market Price Orders with Stale dev_fee After Timeout** ✅ FIXED

**Status:** This bug was fixed in commit `c803471`. The `update_order_to_initial_state()`
function now properly persists `dev_fee = 0` to the database.

**Historical Issue (Pre-Fix):**
The function set `dev_fee = 0` in memory but didn't include it in the SQL UPDATE statement,
causing stale values to remain in the database. When `edit_pubkeys_order()` fetched the
order from the database, it would return the old dev_fee value.

**Symptom (Before Fix):**
```sql
-- Orders that timed out but dev_fee wasn't reset in database
SELECT id, amount, fee, dev_fee, price_from_api, status
FROM orders
WHERE status = 'pending'
  AND price_from_api = 1
  AND amount = 0
  AND fee = 0
  AND dev_fee != 0;  -- BUG: Should be 0
```

**Cause:** `update_order_to_initial_state()` didn't persist dev_fee to database

**Impact:** Next taker would be charged incorrect dev_fee from previous attempt

**Fix Applied:** Added `dev_fee` parameter to `update_order_to_initial_state()` and
included it in the SQL UPDATE statement:
- `src/db.rs`: Function signature and SQL UPDATE modified
- `src/app/cancel.rs`: Explicit cancellation handler

**Prevention:** Both paths now include `order.dev_fee = 0` for market price orders. See "Taker Abandonment and Order Reset" section for details.

**Verification:**
```sql
-- All pending market price orders should have dev_fee = 0
SELECT COUNT(*) as stale_dev_fee_orders
FROM orders
WHERE status = 'pending'
  AND price_from_api = 1
  AND amount = 0
  AND fee = 0
  AND dev_fee != 0;
-- Should return 0 if fix is working correctly
```

### Manual Retry Procedure

For orders with unpaid dev fees:

1. Identify unpaid fees:
```sql
SELECT id, dev_fee FROM orders WHERE dev_fee_paid = 0 AND dev_fee > 0;
```

2. Use Lightning CLI to manually pay:
```bash
lncli payinvoice <invoice_from_lnurl>
```

3. Update database:
```sql
UPDATE orders
SET dev_fee_paid = 1, dev_fee_payment_hash = '<payment_hash>'
WHERE id = '<order_id>';
```

## Security Considerations

### Hardcoded Values

- Lightning Address: `<dev@lightning.address>` (cannot be changed without recompiling)
- Minimum fee: 10% (enforced at startup)
- Prevents misconfiguration or malicious changes

### Payment Isolation

- Dev fee payment errors don't affect core order functionality
- Failed payments logged for audit but don't halt operations
- Ensures platform reliability while maintaining transparency

### Audit Trail

- All fees recorded in database
- Payment hashes enable verification
- Logs provide forensic evidence
- Operators can reconcile payments independently

## Performance Impact

### Latency

- LNURL resolution: ~1-3 seconds (15s timeout)
- LND send_payment call: ~100-500ms (5s timeout)
- Payment execution: ~2-5 seconds (25s timeout)
- Total payment time: ~3-8 seconds typical, 45s maximum
- Scheduler processing interval: 60 seconds
- **Total order delay:** None (payment runs asynchronously via scheduler after buyer receives sats)

### Resource Usage

- Minimal CPU overhead (single calculation per order)
- Negligible memory impact
- Database: 3 additional columns per order (~76 bytes)

## Testing Specification

### Unit Tests

**Status:** ✅ IMPLEMENTED

**Location:** `src/util.rs::tests` module

**Tests Implemented:**

1. **`test_get_dev_fee_basic`**
   - Purpose: Standard percentage calculation
   - Test: 1,000 sats @ 30% = 300 sats
   - Status: ✓ Passing

2. **`test_get_dev_fee_rounding`**
   - Purpose: Rounding behavior
   - Test: 333 sats @ 30% = 99.9 → rounds to 100 sats
   - Status: ✓ Passing

3. **`test_get_dev_fee_zero`**
   - Purpose: Zero fee handling
   - Test: 0 sats @ 30% = 0 sats
   - Status: ✓ Passing

4. **`test_get_dev_fee_tiny_amounts`**
   - Purpose: Small amount edge cases
   - Test: 1 sat @ 30% = 0.3 → rounds to 0 sats
   - Status: ✓ Passing

**All tests use `calculate_dev_fee()` directly with explicit percentage (0.30) to avoid dependency on global Settings.**

**Run tests:**
```bash
cargo test test_get_dev_fee
# Output: test result: ok. 4 passed; 0 failed
```

**Test Coverage:**
- ✅ Standard calculations
- ✅ Rounding behavior (both up and down)
- ✅ Zero fee edge case
- ✅ Tiny amounts (rounds to zero)
- ✅ All tests passing in CI

### Integration Testing

**Manual Test Checklist:**

1. **Configuration Validation:**
   - Set `dev_fee_percentage = 0.05` → Daemon refuses to start ✓
   - Set `dev_fee_percentage = 1.5` → Daemon refuses to start ✓
   - Set `dev_fee_percentage = 0.30` → Daemon starts ✓

2. **Fee Calculation:**
   - Create 100,000 sat order with 1% Mostro fee
   - Verify seller hold invoice: 100,500 sats (100k + 500)
   - Verify buyer receives: 99,500 sats (100k - 500)
   - Verify total dev fee: 300 sats (paid by mostrod from its earnings)

3. **Payment Flow:**
   - Complete order successfully
   - Check database: `dev_fee_paid = 1`, `dev_fee_payment_hash != NULL`
   - Verify logs show successful payment

4. **Error Handling:**
   - Simulate payment failure (disconnect Lightning node)
   - Verify order still completes with `status = 'success'`
   - Check `dev_fee_paid = 0` in database

## Migration Guide

### For Existing Installations

1. **Backup database:**
```bash
cp ~/.mostro/mostro.db ~/.mostro/mostro.db.backup
```

2. **Update Mostro:**
```bash
git pull origin main
cargo build --release
```

3. **Update settings.toml:**
```toml
[mostro]
dev_fee_percentage = 0.30  # Add this line
```

4. **Restart daemon:**
```bash
mostrod
```

5. **Verify:**
```bash
# Check settings loaded
grep "Settings correctly loaded" mostrod.log

# Check migration applied
sqlite3 ~/.mostro/mostro.db "PRAGMA table_info(orders);" | grep dev_fee
```

### Rollback Procedure

If issues arise:

1. Stop daemon
2. Restore backup: `cp ~/.mostro/mostro.db.backup ~/.mostro/mostro.db`
3. Checkout previous version: `git checkout <previous_commit>`
4. Rebuild: `cargo build --release`
5. Restart daemon

