-
Notifications
You must be signed in to change notification settings - Fork 24
feat(bond): Phase 3 claim slashed bond payout from Mostro #596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
577c485
fcdc91a
ac75aa9
727410b
0a52114
d2dc620
fef4c54
49b587e
8ccc838
4e79a05
f57c075
794c12d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -291,6 +291,8 @@ class FakeMostroService implements MostroService { | |
|
|
||
| @override | ||
| Future<void> sendInvoice(String orderId, String invoice, int? amount) async {} | ||
| @override | ||
| Future<void> sendBondPayoutInvoice(String orderId, String invoice) async {} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Suggestion: the fake is still a no-op, so the new bond-payout flow is not exercised end-to-end in integration tests. If you want the new coverage to prove the full path, mirror the production side effect here (store the outbound |
||
|
|
||
| @override | ||
| Future<void> cancelOrder(String orderId) async {} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| import 'package:mostro_mobile/data/models/enums/order_type.dart'; | ||
| import 'package:mostro_mobile/data/models/payload.dart'; | ||
|
|
||
| class BondPayoutRequest implements Payload { | ||
| final BondPayoutOrder order; | ||
| final int slashedAt; | ||
|
|
||
| const BondPayoutRequest({ | ||
| required this.order, | ||
| required this.slashedAt, | ||
| }); | ||
|
|
||
| @override | ||
| String get type => 'bond_payout_request'; | ||
|
|
||
| @override | ||
| Map<String, dynamic> toJson() => { | ||
| type: { | ||
| 'order': order.toJson(), | ||
| 'slashed_at': slashedAt, | ||
| }, | ||
| }; | ||
|
|
||
| factory BondPayoutRequest.fromJson(Map<String, dynamic> json) { | ||
| final orderJson = json['order']; | ||
| if (orderJson is! Map<String, dynamic>) { | ||
| throw const FormatException( | ||
| 'BondPayoutRequest: missing or invalid order field'); | ||
| } | ||
| final slashedAt = json['slashed_at']; | ||
| if (slashedAt is! int) { | ||
| throw const FormatException( | ||
| 'BondPayoutRequest: missing or invalid slashed_at field'); | ||
| } | ||
| return BondPayoutRequest( | ||
| order: BondPayoutOrder.fromJson(orderJson), | ||
| slashedAt: slashedAt, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| class BondPayoutOrder { | ||
| final String? id; | ||
| final OrderType kind; | ||
| final int amount; | ||
| final String fiatCode; | ||
| final int? minAmount; | ||
| final int? maxAmount; | ||
| final int fiatAmount; | ||
| final String paymentMethod; | ||
| final int premium; | ||
|
|
||
| const BondPayoutOrder({ | ||
| this.id, | ||
| required this.kind, | ||
| required this.amount, | ||
| required this.fiatCode, | ||
| this.minAmount, | ||
| this.maxAmount, | ||
| required this.fiatAmount, | ||
| required this.paymentMethod, | ||
| this.premium = 0, | ||
| }); | ||
|
|
||
| Map<String, dynamic> toJson() => { | ||
| 'id': id, | ||
| 'kind': kind.value, | ||
| 'status': null, | ||
| 'amount': amount, | ||
| 'fiat_code': fiatCode, | ||
| 'min_amount': minAmount, | ||
| 'max_amount': maxAmount, | ||
| 'fiat_amount': fiatAmount, | ||
| 'payment_method': paymentMethod, | ||
| 'premium': premium, | ||
| 'created_at': null, | ||
| 'expires_at': null, | ||
| }; | ||
|
|
||
| factory BondPayoutOrder.fromJson(Map<String, dynamic> json) { | ||
| int parseInt(String field) { | ||
| final value = json[field]; | ||
| if (value == null) { | ||
| throw FormatException('BondPayoutOrder: missing $field'); | ||
| } | ||
| if (value is int) return value; | ||
| if (value is String) { | ||
| final parsed = int.tryParse(value); | ||
| if (parsed != null) return parsed; | ||
| } | ||
| throw FormatException('BondPayoutOrder: invalid $field: $value'); | ||
| } | ||
|
|
||
| int? parseOptionalInt(String field) { | ||
| final value = json[field]; | ||
| if (value == null) return null; | ||
| if (value is int) return value; | ||
| if (value is String) return int.tryParse(value); | ||
| return null; | ||
| } | ||
|
|
||
| String parseString(String field) { | ||
| final value = json[field]; | ||
| if (value == null) { | ||
| throw FormatException('BondPayoutOrder: missing $field'); | ||
| } | ||
| return value.toString(); | ||
| } | ||
|
|
||
| return BondPayoutOrder( | ||
| id: json['id']?.toString(), | ||
| kind: OrderType.fromString(parseString('kind')), | ||
| amount: parseInt('amount'), | ||
| fiatCode: parseString('fiat_code'), | ||
| minAmount: parseOptionalInt('min_amount'), | ||
| maxAmount: parseOptionalInt('max_amount'), | ||
| fiatAmount: parseInt('fiat_amount'), | ||
| paymentMethod: parseString('payment_method'), | ||
| premium: parseOptionalInt('premium') ?? 0, | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -117,6 +117,21 @@ class OrderNotifier extends AbstractMostroNotifier { | |
| ); | ||
| } | ||
|
|
||
| Future<void> sendBondPayoutInvoice(String invoice) async { | ||
| await mostroService.sendBondPayoutInvoice(orderId, invoice); | ||
| final timestamp = DateTime.now().millisecondsSinceEpoch; | ||
| final outbound = MostroMessage( | ||
| action: Action.addBondInvoice, | ||
| id: orderId, | ||
| payload: PaymentRequest(lnInvoice: invoice), | ||
| timestamp: timestamp, | ||
| ); | ||
| await ref.read(mostroStorageProvider).addMessage( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Blocking: this still publishes the bond payout invoice first and then swallows any storage failure. If
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was already addressed in commit f57c075 (currently on the The current behavior is:
So the user is not told "success" and not navigated away when Could you point at the specific line where you think the failure is |
||
| 'outbound_addBondInvoice_${orderId}_$timestamp', | ||
| outbound, | ||
| ); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| Future<void> cancelOrder() async { | ||
| await mostroService.cancelOrder(orderId); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fake is a no-op, so the integration harness cannot exercise the new bond payout submit path. Please mirror the production behavior here (or provide a dedicated test double) so the new flow can actually be covered.