refactor: recover mint quotes by quote_id (closes #118)
This commit is contained in:
@@ -87,8 +87,8 @@ class WalletProvider extends ChangeNotifier {
|
||||
static const _mintsKey = 'wallet_mints';
|
||||
static const _activeMintKey = 'wallet_active_mint';
|
||||
static const _activeUnitKey = 'wallet_active_unit';
|
||||
static const _pendingMintInvoicesKey = 'pending_mint_invoices';
|
||||
static const _pendingNostrRequestKey = 'pending_nostr_request';
|
||||
static const _legacyPendingMintInvoicesKey = 'pending_mint_invoices';
|
||||
|
||||
/// Mint de Cuba Bitcoin - siempre aparece primero en la lista
|
||||
static const cubaBitcoinMint = 'https://mint.cubabitcoin.org';
|
||||
@@ -1330,8 +1330,6 @@ class WalletProvider extends ChangeNotifier {
|
||||
throw Exception('No hay wallet activo');
|
||||
}
|
||||
|
||||
final mintUrl = _activeMintUrl!;
|
||||
final unit = _activeUnit;
|
||||
String? invoiceBolt11;
|
||||
|
||||
// Cerrar controller anterior si existe (evitar leak)
|
||||
@@ -1355,10 +1353,8 @@ class WalletProvider extends ChangeNotifier {
|
||||
description: description,
|
||||
).listen(
|
||||
(quote) {
|
||||
// Guardar invoice temprano en SharedPreferences
|
||||
if (quote.state == MintQuoteState.unpaid) {
|
||||
invoiceBolt11 = quote.request;
|
||||
_savePendingMintInvoice(quote.id, quote.request, mintUrl, unit, amount);
|
||||
}
|
||||
|
||||
// Reenviar a la UI ANTES de cualquier await: Rust cierra el stream
|
||||
@@ -1372,13 +1368,7 @@ class WalletProvider extends ChangeNotifier {
|
||||
// Metadata async fire-and-forget (no bloquea el forward)
|
||||
if (quote.state == MintQuoteState.issued && invoiceBolt11 != null) {
|
||||
final invoice = invoiceBolt11!;
|
||||
unawaited(
|
||||
_saveMintMetadata(wallet, invoice, quote.transactionId).then((saved) {
|
||||
// Only remove pending invoice if metadata was saved;
|
||||
// otherwise _matchPendingMintInvoices can recover it on next startup
|
||||
if (saved) _removePendingMintInvoice(quote.id);
|
||||
}),
|
||||
);
|
||||
unawaited(_saveMintMetadata(wallet, invoice, quote.transactionId));
|
||||
}
|
||||
},
|
||||
onError: (error) {
|
||||
@@ -1397,91 +1387,63 @@ class WalletProvider extends ChangeNotifier {
|
||||
return controller.stream;
|
||||
}
|
||||
|
||||
/// Guarda un invoice de mint pendiente para recuperarlo después.
|
||||
Future<void> _savePendingMintInvoice(
|
||||
String quoteId, String invoice, String mintUrl, String unit, BigInt amount,
|
||||
) async {
|
||||
/// Exact recovery of paid-but-unissued mint quotes using `quote_id`.
|
||||
///
|
||||
/// Replaces the old heuristic that matched by (mintUrl, unit, amount) and
|
||||
/// could pair a freshly created invoice to an unrelated orphan transaction
|
||||
/// of the same amount. For every unissued quote the CDK knows about, we
|
||||
/// ask the mint for the current state and — if Paid — issue the proofs,
|
||||
/// linking the resulting tx_id to its bolt11 in TransactionMeta.
|
||||
Future<void> _recoverPendingMintQuotes() async {
|
||||
// One-time cleanup of the legacy SharedPreferences state (no-op after
|
||||
// the first run). Only writes when the key is actually present.
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final jsonStr = prefs.getString(_pendingMintInvoicesKey) ?? '{}';
|
||||
final map = Map<String, dynamic>.from(jsonDecode(jsonStr));
|
||||
map[quoteId] = {
|
||||
'invoice': invoice,
|
||||
'mintUrl': mintUrl,
|
||||
'unit': unit,
|
||||
'amount': amount.toString(),
|
||||
'createdAt': DateTime.now().toIso8601String(),
|
||||
};
|
||||
await prefs.setString(_pendingMintInvoicesKey, jsonEncode(map));
|
||||
debugPrint('Pending mint invoice guardado: $quoteId');
|
||||
if (prefs.getString(_legacyPendingMintInvoicesKey) != null) {
|
||||
await prefs.remove(_legacyPendingMintInvoicesKey);
|
||||
debugPrint('Removed legacy $_legacyPendingMintInvoicesKey from prefs');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error guardando pending mint invoice: $e');
|
||||
debugPrint('Legacy pending-invoices cleanup failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Elimina un invoice de mint pendiente (ya fue procesado).
|
||||
Future<void> _removePendingMintInvoice(String quoteId) async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final jsonStr = prefs.getString(_pendingMintInvoicesKey) ?? '{}';
|
||||
final map = Map<String, dynamic>.from(jsonDecode(jsonStr));
|
||||
map.remove(quoteId);
|
||||
await prefs.setString(_pendingMintInvoicesKey, jsonEncode(map));
|
||||
debugPrint('Pending mint invoice eliminado: $quoteId');
|
||||
} catch (e) {
|
||||
debugPrint('Error eliminando pending mint invoice: $e');
|
||||
}
|
||||
}
|
||||
for (final entry in _mintUnits.entries) {
|
||||
for (final unit in entry.value) {
|
||||
try {
|
||||
final wallet = await getWallet(entry.key, unit);
|
||||
final pending = await wallet.getUnissuedMintQuotes();
|
||||
for (final quote in pending) {
|
||||
try {
|
||||
final updated = await wallet.checkMintQuoteStatus(quoteId: quote.id);
|
||||
if (updated.state != MintQuoteState.paid) continue;
|
||||
|
||||
/// TTL para invoices de mint pendientes (24 horas).
|
||||
static const _pendingMintInvoiceTtl = Duration(hours: 24);
|
||||
|
||||
/// Obtiene todos los invoices de mint pendientes, limpiando expirados.
|
||||
Future<Map<String, dynamic>> _getPendingMintInvoices() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final jsonStr = prefs.getString(_pendingMintInvoicesKey) ?? '{}';
|
||||
final map = Map<String, dynamic>.from(jsonDecode(jsonStr));
|
||||
|
||||
// Filtrar expirados
|
||||
final now = DateTime.now();
|
||||
final expired = <String>[];
|
||||
for (final entry in map.entries) {
|
||||
final data = Map<String, dynamic>.from(entry.value);
|
||||
final createdAt = DateTime.tryParse(data['createdAt'] ?? '');
|
||||
if (createdAt == null || now.difference(createdAt) > _pendingMintInvoiceTtl) {
|
||||
expired.add(entry.key);
|
||||
final minted = await wallet.mintByQuoteId(quoteId: quote.id);
|
||||
final txId = minted.transactionId;
|
||||
if (txId != null && txId.isNotEmpty) {
|
||||
await _txMetaStorage.save(
|
||||
txId,
|
||||
TransactionMeta(
|
||||
type: TransactionType.lightning,
|
||||
invoice: updated.request,
|
||||
),
|
||||
);
|
||||
debugPrint('Recovered mint quote ${quote.id} → tx $txId');
|
||||
} else {
|
||||
debugPrint('Recovered mint quote ${quote.id} but no tx_id');
|
||||
}
|
||||
confettiController.fire();
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Recover quote ${quote.id} failed: $e');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Recover quotes for ${entry.key}/$unit failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Limpiar expirados si hay
|
||||
if (expired.isNotEmpty) {
|
||||
for (final key in expired) {
|
||||
map.remove(key);
|
||||
}
|
||||
await prefs.setString(_pendingMintInvoicesKey, jsonEncode(map));
|
||||
debugPrint('Pending mint invoices expirados eliminados: ${expired.length}');
|
||||
}
|
||||
|
||||
return map;
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/// Busca un invoice pendiente que coincida con un mintUrl y unit.
|
||||
/// Retorna el invoice string o null.
|
||||
Future<String?> findPendingMintInvoice(String mintUrl, String unit) async {
|
||||
final pending = await _getPendingMintInvoices();
|
||||
for (final entry in pending.values) {
|
||||
final data = Map<String, dynamic>.from(entry);
|
||||
if (data['mintUrl'] == mintUrl && data['unit'] == unit) {
|
||||
return data['invoice'] as String?;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PENDING NOSTR PAYMENT REQUESTS
|
||||
// ============================================================
|
||||
@@ -1916,63 +1878,13 @@ class WalletProvider extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Vincular transacciones incoming sin metadata con pending invoices
|
||||
await _matchPendingMintInvoices();
|
||||
// Recover paid-but-unissued quotes by quote_id (exact, no heuristics)
|
||||
await _recoverPendingMintQuotes();
|
||||
|
||||
// Resume pending Nostr payment request if app was killed mid-wait
|
||||
await resumePendingNostrRequest();
|
||||
}
|
||||
|
||||
/// Busca transacciones incoming sin metadata y las vincula con
|
||||
/// invoices de mint pendientes guardados en SharedPreferences.
|
||||
Future<void> _matchPendingMintInvoices() async {
|
||||
try {
|
||||
final pending = await _getPendingMintInvoices();
|
||||
if (pending.isEmpty) return;
|
||||
|
||||
// Obtener todas las transacciones incoming
|
||||
final allTxs = await getAllTransactions(
|
||||
direction: TransactionDirection.incoming,
|
||||
);
|
||||
|
||||
for (final tx in allTxs) {
|
||||
// Solo procesar transacciones sin metadata
|
||||
if (_txMetaStorage.has(tx.id)) continue;
|
||||
|
||||
// Buscar un pending invoice que coincida con mintUrl, unit y amount
|
||||
String? matchedQuoteId;
|
||||
String? matchedInvoice;
|
||||
|
||||
for (final entry in pending.entries) {
|
||||
final data = Map<String, dynamic>.from(entry.value);
|
||||
if (data['mintUrl'] == tx.mintUrl && data['unit'] == tx.unit && data['amount'] == tx.amount.toString()) {
|
||||
matchedQuoteId = entry.key;
|
||||
matchedInvoice = data['invoice'] as String?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedQuoteId != null && matchedInvoice != null) {
|
||||
await _txMetaStorage.save(
|
||||
tx.id,
|
||||
TransactionMeta(
|
||||
type: TransactionType.lightning,
|
||||
invoice: matchedInvoice,
|
||||
),
|
||||
);
|
||||
// Limpiar el pending invoice ya vinculado
|
||||
await _removePendingMintInvoice(matchedQuoteId);
|
||||
pending.remove(matchedQuoteId);
|
||||
debugPrint('Matched pending mint invoice → tx ${tx.id}');
|
||||
confettiController.fire();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error matching pending mint invoices: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifica si un token específico fue gastado.
|
||||
Future<bool> isTokenSpent(String encodedToken) async {
|
||||
final wallet = activeWallet;
|
||||
|
||||
@@ -782,7 +782,6 @@ class _TransactionDetailScreenState extends State<_TransactionDetailScreen> {
|
||||
late final bool _isLightning;
|
||||
String? _tokenOrInvoice;
|
||||
late final bool _shouldShowQR;
|
||||
bool _isLoadingPendingInvoice = false;
|
||||
bool _isReclaiming = false;
|
||||
|
||||
@override
|
||||
@@ -795,11 +794,6 @@ class _TransactionDetailScreenState extends State<_TransactionDetailScreen> {
|
||||
final meta = widget.walletProvider.getTransactionMeta(widget.transaction.id);
|
||||
_tokenOrInvoice = meta?.token ?? meta?.invoice;
|
||||
|
||||
// Si es Lightning incoming sin invoice, buscar en pending mint invoices
|
||||
if (_tokenOrInvoice == null && _isLightning && _isIncoming) {
|
||||
_loadPendingMintInvoice();
|
||||
}
|
||||
|
||||
// Mostrar QR para todo EXCEPTO Lightning saliente
|
||||
_shouldShowQR = !_isLightning || _isIncoming;
|
||||
|
||||
@@ -809,26 +803,6 @@ class _TransactionDetailScreenState extends State<_TransactionDetailScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Busca el invoice en pending mint invoices si no hay metadata.
|
||||
Future<void> _loadPendingMintInvoice() async {
|
||||
setState(() => _isLoadingPendingInvoice = true);
|
||||
String? invoice;
|
||||
try {
|
||||
invoice = await widget.walletProvider.findPendingMintInvoice(
|
||||
widget.transaction.mintUrl,
|
||||
widget.transaction.unit,
|
||||
);
|
||||
} catch (e, st) {
|
||||
debugPrint('findPendingMintInvoice failed: $e\n$st');
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoadingPendingInvoice = false;
|
||||
if (invoice != null) _tokenOrInvoice = invoice;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationTimer?.cancel();
|
||||
@@ -968,7 +942,7 @@ class _TransactionDetailScreenState extends State<_TransactionDetailScreen> {
|
||||
],
|
||||
|
||||
// Mensaje si no hay token/invoice (ocultar mientras carga)
|
||||
if (_tokenOrInvoice == null && !_isLoadingPendingInvoice) ...[
|
||||
if (_tokenOrInvoice == null) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
|
||||
@@ -70,6 +70,11 @@ abstract class Wallet implements RustOpaqueInterface {
|
||||
|
||||
Future<void> checkAllMintQuotes();
|
||||
|
||||
/// Ask the mint for the current state of a single quote by id and
|
||||
/// return the refreshed MintQuote. Local store is updated as a side
|
||||
/// effect inside CDK.
|
||||
Future<MintQuote> checkMintQuoteStatus({required String quoteId});
|
||||
|
||||
Future<void> checkPendingTransactions();
|
||||
|
||||
/// Observe-only counterpart of `reclaim_proofs_by_ys`. Queries the mint
|
||||
@@ -105,6 +110,10 @@ abstract class Wallet implements RustOpaqueInterface {
|
||||
|
||||
Future<MintInfo?> getMint();
|
||||
|
||||
/// Return pending (unissued) mint quotes known to the local store,
|
||||
/// filtered to this wallet's mint/unit.
|
||||
Future<List<MintQuote>> getUnissuedMintQuotes();
|
||||
|
||||
Future<bool> isTokenSpent({required Token token});
|
||||
|
||||
Future<List<Transaction>> listTransactions({TransactionDirection? direction});
|
||||
@@ -113,6 +122,12 @@ abstract class Wallet implements RustOpaqueInterface {
|
||||
|
||||
Stream<MintQuote> mint({required BigInt amount, String? description});
|
||||
|
||||
/// Mint proofs for a paid quote and return an Issued MintQuote with
|
||||
/// `transaction_id` and `token` populated — same shape the `mint()`
|
||||
/// stream emits on Issued. Use this to recover metadata for quotes
|
||||
/// that were paid while the app was killed.
|
||||
Future<MintQuote> mintByQuoteId({required String quoteId});
|
||||
|
||||
factory Wallet({
|
||||
required String mintUrl,
|
||||
required String unit,
|
||||
|
||||
+215
-37
@@ -69,7 +69,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
String get codegenVersion => '2.11.1';
|
||||
|
||||
@override
|
||||
int get rustContentHash => 1593022460;
|
||||
int get rustContentHash => 884570592;
|
||||
|
||||
static const kDefaultExternalLibraryLoaderConfig =
|
||||
ExternalLibraryLoaderConfig(
|
||||
@@ -246,6 +246,11 @@ abstract class RustLibApi extends BaseApi {
|
||||
|
||||
Future<void> crateApiWalletWalletCheckAllMintQuotes({required Wallet that});
|
||||
|
||||
Future<MintQuote> crateApiWalletWalletCheckMintQuoteStatus({
|
||||
required Wallet that,
|
||||
required String quoteId,
|
||||
});
|
||||
|
||||
Future<void> crateApiWalletWalletCheckPendingTransactions({
|
||||
required Wallet that,
|
||||
});
|
||||
@@ -269,6 +274,10 @@ abstract class RustLibApi extends BaseApi {
|
||||
|
||||
Future<MintInfo?> crateApiWalletWalletGetMint({required Wallet that});
|
||||
|
||||
Future<List<MintQuote>> crateApiWalletWalletGetUnissuedMintQuotes({
|
||||
required Wallet that,
|
||||
});
|
||||
|
||||
Future<bool> crateApiWalletWalletIsTokenSpent({
|
||||
required Wallet that,
|
||||
required Token token,
|
||||
@@ -290,6 +299,11 @@ abstract class RustLibApi extends BaseApi {
|
||||
String? description,
|
||||
});
|
||||
|
||||
Future<MintQuote> crateApiWalletWalletMintByQuoteId({
|
||||
required Wallet that,
|
||||
required String quoteId,
|
||||
});
|
||||
|
||||
Wallet crateApiWalletWalletNew({
|
||||
required String mintUrl,
|
||||
required String unit,
|
||||
@@ -1772,6 +1786,44 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
argNames: ["that"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<MintQuote> crateApiWalletWalletCheckMintQuoteStatus({
|
||||
required Wallet that,
|
||||
required String quoteId,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerWallet(
|
||||
that,
|
||||
serializer,
|
||||
);
|
||||
sse_encode_String(quoteId, serializer);
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 41,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_mint_quote,
|
||||
decodeErrorData: sse_decode_error,
|
||||
),
|
||||
constMeta: kCrateApiWalletWalletCheckMintQuoteStatusConstMeta,
|
||||
argValues: [that, quoteId],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiWalletWalletCheckMintQuoteStatusConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "Wallet_check_mint_quote_status",
|
||||
argNames: ["that", "quoteId"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> crateApiWalletWalletCheckPendingTransactions({
|
||||
required Wallet that,
|
||||
@@ -1787,7 +1839,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 41,
|
||||
funcId: 42,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -1825,7 +1877,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 42,
|
||||
funcId: 43,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -1866,7 +1918,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 43,
|
||||
funcId: 44,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -1904,7 +1956,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 44,
|
||||
funcId: 45,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -1941,7 +1993,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 45,
|
||||
funcId: 46,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -1975,7 +2027,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 46,
|
||||
funcId: 47,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -1993,6 +2045,42 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
TaskConstMeta get kCrateApiWalletWalletGetMintConstMeta =>
|
||||
const TaskConstMeta(debugName: "Wallet_get_mint", argNames: ["that"]);
|
||||
|
||||
@override
|
||||
Future<List<MintQuote>> crateApiWalletWalletGetUnissuedMintQuotes({
|
||||
required Wallet that,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerWallet(
|
||||
that,
|
||||
serializer,
|
||||
);
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 48,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_list_mint_quote,
|
||||
decodeErrorData: sse_decode_error,
|
||||
),
|
||||
constMeta: kCrateApiWalletWalletGetUnissuedMintQuotesConstMeta,
|
||||
argValues: [that],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiWalletWalletGetUnissuedMintQuotesConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "Wallet_get_unissued_mint_quotes",
|
||||
argNames: ["that"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<bool> crateApiWalletWalletIsTokenSpent({
|
||||
required Wallet that,
|
||||
@@ -2010,7 +2098,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 47,
|
||||
funcId: 49,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2051,7 +2139,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 48,
|
||||
funcId: 50,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2089,7 +2177,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 49,
|
||||
funcId: 51,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2132,7 +2220,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 50,
|
||||
funcId: 52,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2154,6 +2242,44 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
argNames: ["that", "amount", "description", "sink"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<MintQuote> crateApiWalletWalletMintByQuoteId({
|
||||
required Wallet that,
|
||||
required String quoteId,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerWallet(
|
||||
that,
|
||||
serializer,
|
||||
);
|
||||
sse_encode_String(quoteId, serializer);
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 53,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_mint_quote,
|
||||
decodeErrorData: sse_decode_error,
|
||||
),
|
||||
constMeta: kCrateApiWalletWalletMintByQuoteIdConstMeta,
|
||||
argValues: [that, quoteId],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiWalletWalletMintByQuoteIdConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "Wallet_mint_by_quote_id",
|
||||
argNames: ["that", "quoteId"],
|
||||
);
|
||||
|
||||
@override
|
||||
Wallet crateApiWalletWalletNew({
|
||||
required String mintUrl,
|
||||
@@ -2174,7 +2300,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
db,
|
||||
serializer,
|
||||
);
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 51)!;
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 54)!;
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData:
|
||||
@@ -2212,7 +2338,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 52,
|
||||
funcId: 55,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2250,7 +2376,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 53,
|
||||
funcId: 56,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2291,7 +2417,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 54,
|
||||
funcId: 57,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2332,7 +2458,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 55,
|
||||
funcId: 58,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2368,7 +2494,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 56,
|
||||
funcId: 59,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2406,7 +2532,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 57,
|
||||
funcId: 60,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2442,7 +2568,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 58,
|
||||
funcId: 61,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2476,7 +2602,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 59,
|
||||
funcId: 62,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2510,7 +2636,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 60,
|
||||
funcId: 63,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2552,7 +2678,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 61,
|
||||
funcId: 64,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2588,7 +2714,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 62,
|
||||
funcId: 65,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2634,7 +2760,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 63,
|
||||
funcId: 66,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2672,7 +2798,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_String(proofsJson, serializer);
|
||||
sse_encode_opt_String(memo, serializer);
|
||||
sse_encode_opt_String(unit, serializer);
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 64)!;
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 67)!;
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_token,
|
||||
@@ -2702,7 +2828,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_box_autoadd_token(token, serializer);
|
||||
sse_encode_opt_box_autoadd_usize(maxFragmentLength, serializer);
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 65)!;
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 68)!;
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_list_String,
|
||||
@@ -2732,7 +2858,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 66,
|
||||
funcId: 69,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2756,7 +2882,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
SyncTask(
|
||||
callFfi: () {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 67)!;
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 70)!;
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_String,
|
||||
@@ -2782,7 +2908,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 68,
|
||||
funcId: 71,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2807,7 +2933,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
callFfi: () {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_String(secret, serializer);
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 69)!;
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 72)!;
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_String,
|
||||
@@ -2830,7 +2956,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
callFfi: () {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_String(mnemonic, serializer);
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 70)!;
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 73)!;
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_list_prim_u_8_strict,
|
||||
@@ -2857,7 +2983,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
callFfi: () {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_String(encoded, serializer);
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 71)!;
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 74)!;
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_payment_request_info,
|
||||
@@ -2886,7 +3012,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 72,
|
||||
funcId: 75,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2913,7 +3039,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 73,
|
||||
funcId: 76,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2940,7 +3066,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
pdeCallFfi(
|
||||
generalizedFrbRustBinding,
|
||||
serializer,
|
||||
funcId: 74,
|
||||
funcId: 77,
|
||||
port: port_,
|
||||
);
|
||||
},
|
||||
@@ -2965,7 +3091,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
callFfi: () {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_list_prim_u_8_loose(raw, serializer);
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 75)!;
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 78)!;
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_token,
|
||||
@@ -2988,7 +3114,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
callFfi: () {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_String(encoded, serializer);
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 76)!;
|
||||
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 79)!;
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_token,
|
||||
@@ -3513,6 +3639,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return (raw as List<dynamic>).map(dco_decode_mint_method_settings).toList();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<MintQuote> dco_decode_list_mint_quote(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return (raw as List<dynamic>).map(dco_decode_mint_quote).toList();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<int> dco_decode_list_prim_u_8_loose(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -4553,6 +4685,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return ans_;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<MintQuote> sse_decode_list_mint_quote(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
var len_ = sse_decode_i_32(deserializer);
|
||||
var ans_ = <MintQuote>[];
|
||||
for (var idx_ = 0; idx_ < len_; ++idx_) {
|
||||
ans_.add(sse_decode_mint_quote(deserializer));
|
||||
}
|
||||
return ans_;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -5778,6 +5922,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_mint_quote(
|
||||
List<MintQuote> self,
|
||||
SseSerializer serializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_i_32(self.length, serializer);
|
||||
for (final item in self) {
|
||||
sse_encode_mint_quote(item, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_loose(
|
||||
List<int> self,
|
||||
@@ -6592,6 +6748,14 @@ class WalletImpl extends RustOpaque implements Wallet {
|
||||
Future<void> checkAllMintQuotes() =>
|
||||
RustLib.instance.api.crateApiWalletWalletCheckAllMintQuotes(that: this);
|
||||
|
||||
/// Ask the mint for the current state of a single quote by id and
|
||||
/// return the refreshed MintQuote. Local store is updated as a side
|
||||
/// effect inside CDK.
|
||||
Future<MintQuote> checkMintQuoteStatus({required String quoteId}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiWalletWalletCheckMintQuoteStatus(that: this, quoteId: quoteId);
|
||||
|
||||
Future<void> checkPendingTransactions() => RustLib.instance.api
|
||||
.crateApiWalletWalletCheckPendingTransactions(that: this);
|
||||
|
||||
@@ -6639,6 +6803,11 @@ class WalletImpl extends RustOpaque implements Wallet {
|
||||
Future<MintInfo?> getMint() =>
|
||||
RustLib.instance.api.crateApiWalletWalletGetMint(that: this);
|
||||
|
||||
/// Return pending (unissued) mint quotes known to the local store,
|
||||
/// filtered to this wallet's mint/unit.
|
||||
Future<List<MintQuote>> getUnissuedMintQuotes() => RustLib.instance.api
|
||||
.crateApiWalletWalletGetUnissuedMintQuotes(that: this);
|
||||
|
||||
Future<bool> isTokenSpent({required Token token}) => RustLib.instance.api
|
||||
.crateApiWalletWalletIsTokenSpent(that: this, token: token);
|
||||
|
||||
@@ -6659,6 +6828,15 @@ class WalletImpl extends RustOpaque implements Wallet {
|
||||
description: description,
|
||||
);
|
||||
|
||||
/// Mint proofs for a paid quote and return an Issued MintQuote with
|
||||
/// `transaction_id` and `token` populated — same shape the `mint()`
|
||||
/// stream emits on Issued. Use this to recover metadata for quotes
|
||||
/// that were paid while the app was killed.
|
||||
Future<MintQuote> mintByQuoteId({required String quoteId}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiWalletWalletMintByQuoteId(that: this, quoteId: quoteId);
|
||||
|
||||
/// Pay a NUT-18 payment request.
|
||||
/// Uses CDK's pay_request() which handles:
|
||||
/// - NUT-10 spending conditions
|
||||
|
||||
@@ -299,6 +299,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
List<MintMethodSettings> dco_decode_list_mint_method_settings(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<MintQuote> dco_decode_list_mint_quote(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<int> dco_decode_list_prim_u_8_loose(dynamic raw);
|
||||
|
||||
@@ -706,6 +709,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
List<MintQuote> sse_decode_list_mint_quote(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
|
||||
|
||||
@@ -1200,6 +1206,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_mint_quote(
|
||||
List<MintQuote> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
|
||||
|
||||
|
||||
@@ -301,6 +301,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
List<MintMethodSettings> dco_decode_list_mint_method_settings(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<MintQuote> dco_decode_list_mint_quote(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<int> dco_decode_list_prim_u_8_loose(dynamic raw);
|
||||
|
||||
@@ -708,6 +711,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
List<MintQuote> sse_decode_list_mint_quote(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
|
||||
|
||||
@@ -1202,6 +1208,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_mint_quote(
|
||||
List<MintQuote> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
|
||||
|
||||
|
||||
@@ -411,6 +411,62 @@ impl Wallet {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return pending (unissued) mint quotes known to the local store,
|
||||
/// filtered to this wallet's mint/unit.
|
||||
pub async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, Error> {
|
||||
let quotes = self.inner.get_unissued_mint_quotes().await?;
|
||||
Ok(quotes.into_iter().map(MintQuote::from).collect())
|
||||
}
|
||||
|
||||
/// Ask the mint for the current state of a single quote by id and
|
||||
/// return the refreshed MintQuote. Local store is updated as a side
|
||||
/// effect inside CDK.
|
||||
pub async fn check_mint_quote_status(&self, quote_id: String) -> Result<MintQuote, Error> {
|
||||
let quote = self.inner.check_mint_quote_status("e_id).await?;
|
||||
Ok(MintQuote::from(quote))
|
||||
}
|
||||
|
||||
/// Mint proofs for a paid quote and return an Issued MintQuote with
|
||||
/// `transaction_id` and `token` populated — same shape the `mint()`
|
||||
/// stream emits on Issued. Use this to recover metadata for quotes
|
||||
/// that were paid while the app was killed.
|
||||
pub async fn mint_by_quote_id(&self, quote_id: String) -> Result<MintQuote, Error> {
|
||||
let mint_url = self.mint_url()?;
|
||||
let unit = self.unit();
|
||||
let cdk_quote = self.inner.check_mint_quote_status("e_id).await?;
|
||||
let expiry = cdk_quote.expiry;
|
||||
let request = cdk_quote.request.clone();
|
||||
|
||||
let mint_proofs = self
|
||||
.inner
|
||||
.mint("e_id, SplitTarget::None, None)
|
||||
.await?;
|
||||
|
||||
let tx_id = match TransactionId::try_from(mint_proofs.clone()) {
|
||||
Ok(id) => Some(id.to_string()),
|
||||
Err(e) => {
|
||||
info!("Failed to compute mint tx ID: {e}");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let mint_amount = mint_proofs.total_amount().unwrap_or_default();
|
||||
let token = Token::try_from(CdkToken::new(mint_url, mint_proofs, None, unit)).ok();
|
||||
|
||||
self.update_balance_streams().await;
|
||||
|
||||
Ok(MintQuote {
|
||||
id: quote_id,
|
||||
request,
|
||||
amount: Some(mint_amount.into()),
|
||||
expiry: Some(expiry),
|
||||
state: CdkMintQuoteState::Issued.into(),
|
||||
token,
|
||||
error: None,
|
||||
transaction_id: tx_id,
|
||||
})
|
||||
}
|
||||
|
||||
// === Lightning Withdrawal (NUT-05) ===
|
||||
|
||||
pub async fn melt_quote(&self, request: String) -> Result<MeltQuote, Error> {
|
||||
|
||||
+254
-37
@@ -40,7 +40,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
|
||||
default_rust_auto_opaque = RustAutoOpaqueMoi,
|
||||
);
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1";
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1593022460;
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 884570592;
|
||||
|
||||
// Section: executor
|
||||
|
||||
@@ -1985,6 +1985,66 @@ fn wire__crate__api__wallet__Wallet_check_all_mint_quotes_impl(
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__wallet__Wallet_check_mint_quote_status_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
rust_vec_len_: i32,
|
||||
data_len_: i32,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "Wallet_check_mint_quote_status",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let message = unsafe {
|
||||
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
|
||||
ptr_,
|
||||
rust_vec_len_,
|
||||
data_len_,
|
||||
)
|
||||
};
|
||||
let mut deserializer =
|
||||
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
|
||||
let api_that = <RustOpaqueMoi<
|
||||
flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Wallet>,
|
||||
>>::sse_decode(&mut deserializer);
|
||||
let api_quote_id = <String>::sse_decode(&mut deserializer);
|
||||
deserializer.end();
|
||||
move |context| async move {
|
||||
transform_result_sse::<_, crate::api::error::Error>(
|
||||
(move || async move {
|
||||
let mut api_that_guard = None;
|
||||
let decode_indices_ =
|
||||
flutter_rust_bridge::for_generated::lockable_compute_decode_order(
|
||||
vec![flutter_rust_bridge::for_generated::LockableOrderInfo::new(
|
||||
&api_that, 0, false,
|
||||
)],
|
||||
);
|
||||
for i in decode_indices_ {
|
||||
match i {
|
||||
0 => {
|
||||
api_that_guard =
|
||||
Some(api_that.lockable_decode_async_ref().await)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
let api_that_guard = api_that_guard.unwrap();
|
||||
let output_ok = crate::api::wallet::Wallet::check_mint_quote_status(
|
||||
&*api_that_guard,
|
||||
api_quote_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__wallet__Wallet_check_pending_transactions_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
@@ -2335,6 +2395,63 @@ fn wire__crate__api__wallet__Wallet_get_mint_impl(
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__wallet__Wallet_get_unissued_mint_quotes_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
rust_vec_len_: i32,
|
||||
data_len_: i32,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "Wallet_get_unissued_mint_quotes",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let message = unsafe {
|
||||
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
|
||||
ptr_,
|
||||
rust_vec_len_,
|
||||
data_len_,
|
||||
)
|
||||
};
|
||||
let mut deserializer =
|
||||
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
|
||||
let api_that = <RustOpaqueMoi<
|
||||
flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Wallet>,
|
||||
>>::sse_decode(&mut deserializer);
|
||||
deserializer.end();
|
||||
move |context| async move {
|
||||
transform_result_sse::<_, crate::api::error::Error>(
|
||||
(move || async move {
|
||||
let mut api_that_guard = None;
|
||||
let decode_indices_ =
|
||||
flutter_rust_bridge::for_generated::lockable_compute_decode_order(
|
||||
vec![flutter_rust_bridge::for_generated::LockableOrderInfo::new(
|
||||
&api_that, 0, false,
|
||||
)],
|
||||
);
|
||||
for i in decode_indices_ {
|
||||
match i {
|
||||
0 => {
|
||||
api_that_guard =
|
||||
Some(api_that.lockable_decode_async_ref().await)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
let api_that_guard = api_that_guard.unwrap();
|
||||
let output_ok =
|
||||
crate::api::wallet::Wallet::get_unissued_mint_quotes(&*api_that_guard)
|
||||
.await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__wallet__Wallet_is_token_spent_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
@@ -2579,6 +2696,66 @@ fn wire__crate__api__wallet__Wallet_mint_impl(
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__wallet__Wallet_mint_by_quote_id_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
rust_vec_len_: i32,
|
||||
data_len_: i32,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "Wallet_mint_by_quote_id",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let message = unsafe {
|
||||
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
|
||||
ptr_,
|
||||
rust_vec_len_,
|
||||
data_len_,
|
||||
)
|
||||
};
|
||||
let mut deserializer =
|
||||
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
|
||||
let api_that = <RustOpaqueMoi<
|
||||
flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Wallet>,
|
||||
>>::sse_decode(&mut deserializer);
|
||||
let api_quote_id = <String>::sse_decode(&mut deserializer);
|
||||
deserializer.end();
|
||||
move |context| async move {
|
||||
transform_result_sse::<_, crate::api::error::Error>(
|
||||
(move || async move {
|
||||
let mut api_that_guard = None;
|
||||
let decode_indices_ =
|
||||
flutter_rust_bridge::for_generated::lockable_compute_decode_order(
|
||||
vec![flutter_rust_bridge::for_generated::LockableOrderInfo::new(
|
||||
&api_that, 0, false,
|
||||
)],
|
||||
);
|
||||
for i in decode_indices_ {
|
||||
match i {
|
||||
0 => {
|
||||
api_that_guard =
|
||||
Some(api_that.lockable_decode_async_ref().await)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
let api_that_guard = api_that_guard.unwrap();
|
||||
let output_ok = crate::api::wallet::Wallet::mint_by_quote_id(
|
||||
&*api_that_guard,
|
||||
api_quote_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__wallet__Wallet_new_impl(
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
rust_vec_len_: i32,
|
||||
@@ -4158,6 +4335,18 @@ impl SseDecode for Vec<crate::api::mint_info::MintMethodSettings> {
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for Vec<crate::api::wallet::MintQuote> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
let mut len_ = <i32>::sse_decode(deserializer);
|
||||
let mut ans_ = vec![];
|
||||
for idx_ in 0..len_ {
|
||||
ans_.push(<crate::api::wallet::MintQuote>::sse_decode(deserializer));
|
||||
}
|
||||
return ans_;
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for Vec<u8> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
@@ -4833,94 +5022,112 @@ fn pde_ffi_dispatcher_primary_impl(
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
41 => wire__crate__api__wallet__Wallet_check_pending_transactions_impl(
|
||||
41 => wire__crate__api__wallet__Wallet_check_mint_quote_status_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
42 => wire__crate__api__wallet__Wallet_check_proofs_by_ys_impl(
|
||||
42 => wire__crate__api__wallet__Wallet_check_pending_transactions_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
43 => wire__crate__api__wallet__Wallet_confirm_melt_impl(port, ptr, rust_vec_len, data_len),
|
||||
44 => wire__crate__api__wallet__Wallet_create_payment_request_impl(
|
||||
43 => wire__crate__api__wallet__Wallet_check_proofs_by_ys_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
45 => wire__crate__api__wallet__Wallet_finalize_pending_melts_impl(
|
||||
44 => wire__crate__api__wallet__Wallet_confirm_melt_impl(port, ptr, rust_vec_len, data_len),
|
||||
45 => wire__crate__api__wallet__Wallet_create_payment_request_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
46 => wire__crate__api__wallet__Wallet_get_mint_impl(port, ptr, rust_vec_len, data_len),
|
||||
47 => {
|
||||
46 => wire__crate__api__wallet__Wallet_finalize_pending_melts_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
47 => wire__crate__api__wallet__Wallet_get_mint_impl(port, ptr, rust_vec_len, data_len),
|
||||
48 => wire__crate__api__wallet__Wallet_get_unissued_mint_quotes_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
49 => {
|
||||
wire__crate__api__wallet__Wallet_is_token_spent_impl(port, ptr, rust_vec_len, data_len)
|
||||
}
|
||||
48 => wire__crate__api__wallet__Wallet_list_transactions_impl(
|
||||
50 => wire__crate__api__wallet__Wallet_list_transactions_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
49 => wire__crate__api__wallet__Wallet_melt_quote_impl(port, ptr, rust_vec_len, data_len),
|
||||
50 => wire__crate__api__wallet__Wallet_mint_impl(port, ptr, rust_vec_len, data_len),
|
||||
52 => wire__crate__api__wallet__Wallet_pay_payment_request_impl(
|
||||
51 => wire__crate__api__wallet__Wallet_melt_quote_impl(port, ptr, rust_vec_len, data_len),
|
||||
52 => wire__crate__api__wallet__Wallet_mint_impl(port, ptr, rust_vec_len, data_len),
|
||||
53 => wire__crate__api__wallet__Wallet_mint_by_quote_id_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
53 => wire__crate__api__wallet__Wallet_prepare_melt_impl(port, ptr, rust_vec_len, data_len),
|
||||
54 => wire__crate__api__wallet__Wallet_prepare_send_impl(port, ptr, rust_vec_len, data_len),
|
||||
55 => wire__crate__api__wallet__Wallet_receive_impl(port, ptr, rust_vec_len, data_len),
|
||||
56 => wire__crate__api__wallet__Wallet_reclaim_pending_proofs_impl(
|
||||
55 => wire__crate__api__wallet__Wallet_pay_payment_request_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
57 => wire__crate__api__wallet__Wallet_reclaim_proofs_by_ys_impl(
|
||||
56 => wire__crate__api__wallet__Wallet_prepare_melt_impl(port, ptr, rust_vec_len, data_len),
|
||||
57 => wire__crate__api__wallet__Wallet_prepare_send_impl(port, ptr, rust_vec_len, data_len),
|
||||
58 => wire__crate__api__wallet__Wallet_receive_impl(port, ptr, rust_vec_len, data_len),
|
||||
59 => wire__crate__api__wallet__Wallet_reclaim_pending_proofs_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
58 => wire__crate__api__wallet__Wallet_recover_incomplete_sagas_impl(
|
||||
60 => wire__crate__api__wallet__Wallet_reclaim_proofs_by_ys_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
59 => {
|
||||
61 => wire__crate__api__wallet__Wallet_recover_incomplete_sagas_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
62 => {
|
||||
wire__crate__api__wallet__Wallet_refresh_balance_impl(port, ptr, rust_vec_len, data_len)
|
||||
}
|
||||
60 => wire__crate__api__wallet__Wallet_restore_impl(port, ptr, rust_vec_len, data_len),
|
||||
61 => wire__crate__api__wallet__Wallet_send_impl(port, ptr, rust_vec_len, data_len),
|
||||
62 => {
|
||||
63 => wire__crate__api__wallet__Wallet_restore_impl(port, ptr, rust_vec_len, data_len),
|
||||
64 => wire__crate__api__wallet__Wallet_send_impl(port, ptr, rust_vec_len, data_len),
|
||||
65 => {
|
||||
wire__crate__api__wallet__Wallet_stream_balance_impl(port, ptr, rust_vec_len, data_len)
|
||||
}
|
||||
63 => wire__crate__api__wallet__Wallet_wait_for_nostr_payment_impl(
|
||||
66 => wire__crate__api__wallet__Wallet_wait_for_nostr_payment_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
66 => wire__crate__api__mint_info__fetch_keysets_impl(port, ptr, rust_vec_len, data_len),
|
||||
68 => wire__crate__api__mint_info__get_mint_info_impl(port, ptr, rust_vec_len, data_len),
|
||||
72 => wire__crate__api__mint_info__ping_mint_impl(port, ptr, rust_vec_len, data_len),
|
||||
73 => wire__crate__api__wallet__receive_options_default_impl(
|
||||
69 => wire__crate__api__mint_info__fetch_keysets_impl(port, ptr, rust_vec_len, data_len),
|
||||
71 => wire__crate__api__mint_info__get_mint_info_impl(port, ptr, rust_vec_len, data_len),
|
||||
75 => wire__crate__api__mint_info__ping_mint_impl(port, ptr, rust_vec_len, data_len),
|
||||
76 => wire__crate__api__wallet__receive_options_default_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
74 => {
|
||||
77 => {
|
||||
wire__crate__api__wallet__send_options_default_impl(port, ptr, rust_vec_len, data_len)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@@ -4969,15 +5176,15 @@ fn pde_ffi_dispatcher_sync_impl(
|
||||
34 => wire__crate__api__wallet__Wallet_auto_accessor_get_unit_impl(ptr, rust_vec_len, data_len),
|
||||
35 => wire__crate__api__wallet__Wallet_auto_accessor_set_mint_url_impl(ptr, rust_vec_len, data_len),
|
||||
36 => wire__crate__api__wallet__Wallet_auto_accessor_set_unit_impl(ptr, rust_vec_len, data_len),
|
||||
51 => wire__crate__api__wallet__Wallet_new_impl(ptr, rust_vec_len, data_len),
|
||||
64 => wire__crate__api__token__create_offline_token_impl(ptr, rust_vec_len, data_len),
|
||||
65 => wire__crate__api__token__encode_qr_token_impl(ptr, rust_vec_len, data_len),
|
||||
67 => wire__crate__api__keys__generate_mnemonic_impl(ptr, rust_vec_len, data_len),
|
||||
69 => wire__crate__api__keys__get_pub_key_impl(ptr, rust_vec_len, data_len),
|
||||
70 => wire__crate__api__keys__mnemonic_to_seed_impl(ptr, rust_vec_len, data_len),
|
||||
71 => wire__crate__api__payment_request__payment_request_info_parse_impl(ptr, rust_vec_len, data_len),
|
||||
75 => wire__crate__api__token__token_from_raw_bytes_impl(ptr, rust_vec_len, data_len),
|
||||
76 => wire__crate__api__token__token_parse_impl(ptr, rust_vec_len, data_len),
|
||||
54 => wire__crate__api__wallet__Wallet_new_impl(ptr, rust_vec_len, data_len),
|
||||
67 => wire__crate__api__token__create_offline_token_impl(ptr, rust_vec_len, data_len),
|
||||
68 => wire__crate__api__token__encode_qr_token_impl(ptr, rust_vec_len, data_len),
|
||||
70 => wire__crate__api__keys__generate_mnemonic_impl(ptr, rust_vec_len, data_len),
|
||||
72 => wire__crate__api__keys__get_pub_key_impl(ptr, rust_vec_len, data_len),
|
||||
73 => wire__crate__api__keys__mnemonic_to_seed_impl(ptr, rust_vec_len, data_len),
|
||||
74 => wire__crate__api__payment_request__payment_request_info_parse_impl(ptr, rust_vec_len, data_len),
|
||||
78 => wire__crate__api__token__token_from_raw_bytes_impl(ptr, rust_vec_len, data_len),
|
||||
79 => wire__crate__api__token__token_parse_impl(ptr, rust_vec_len, data_len),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -6039,6 +6246,16 @@ impl SseEncode for Vec<crate::api::mint_info::MintMethodSettings> {
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for Vec<crate::api::wallet::MintQuote> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<i32>::sse_encode(self.len() as _, serializer);
|
||||
for item in self {
|
||||
<crate::api::wallet::MintQuote>::sse_encode(item, serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for Vec<u8> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
|
||||
Reference in New Issue
Block a user