mostro 0.17.4

Lightning Network peer-to-peer nostr platform
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
# 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