Compare commits
29
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aafa9f97eb | ||
|
|
7d3fe5ac58 | ||
|
|
b9c01ce865 | ||
|
|
679aa6be5f | ||
|
|
c44a17efbd | ||
|
|
27a2baefcc | ||
|
|
e23c5748ce | ||
|
|
c9781b2ff2 | ||
|
|
b9485d3296 | ||
|
|
c222fff82e | ||
|
|
f839031340 | ||
|
|
41b8743b29 | ||
|
|
056f4c1286 | ||
|
|
cfed450521 | ||
|
|
438603c97b | ||
|
|
c92c438efe | ||
|
|
7d5efcfb75 | ||
|
|
bb942ef72c | ||
|
|
b28a529ffc | ||
|
|
1ff3a6ec16 | ||
|
|
f1b679d912 | ||
|
|
8b514337a6 | ||
|
|
d7f6803f1f | ||
|
|
d91a40c41d | ||
|
|
0ed49789d0 | ||
|
|
a41b99644c | ||
|
|
286eae3812 | ||
|
|
e1e8057be5 | ||
|
|
a1ce50bcd4 |
@@ -22,6 +22,7 @@ class AppColors {
|
||||
// === ACCENT COLORS ===
|
||||
static const Color primaryAction = Color(0xFFE35D33); // Naranja vibrante
|
||||
static const Color secondaryAction = Color(0xFFFFB74D); // Amarillo pulpa/Bitcoin
|
||||
static const Color bitcoinOrange = Color(0xFFF7931A); // Bitcoin brand orange
|
||||
static const Color success = Color(0xFF00E676); // Verde neón
|
||||
static const Color error = Color(0xFFFF5252); // Rojo suave
|
||||
static const Color warning = Color(0xFFFFB74D); // Amarillo
|
||||
|
||||
@@ -473,11 +473,13 @@ class NfcService {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Check if a string looks like a Cashu token.
|
||||
/// Check if a string looks like a Cashu token or payment request.
|
||||
static bool _isCashuToken(String text) {
|
||||
final lower = text.toLowerCase().trim();
|
||||
return lower.startsWith('cashua') ||
|
||||
lower.startsWith('cashub') ||
|
||||
lower.startsWith('creqa');
|
||||
lower.startsWith('creqa') ||
|
||||
lower.startsWith('creqb') ||
|
||||
lower.startsWith('bitcoin:');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/// Builds a BIP-321 unified payment URI.
|
||||
///
|
||||
/// Format: BITCOIN:?LIGHTNING=LNBC...&CREQ=CREQB1...
|
||||
/// All uppercase for optimal QR encoding (alphanumeric mode).
|
||||
///
|
||||
/// Ref: NUT-26 "BIP-321 Integration" section.
|
||||
String buildUnifiedUri({
|
||||
required String creqB,
|
||||
String? bolt11,
|
||||
}) {
|
||||
final buffer = StringBuffer('bitcoin:?');
|
||||
if (bolt11 != null) {
|
||||
buffer.write('lightning=');
|
||||
buffer.write(bolt11.toUpperCase());
|
||||
buffer.write('&');
|
||||
}
|
||||
buffer.write('creq=');
|
||||
buffer.write(creqB.toUpperCase());
|
||||
return buffer.toString();
|
||||
}
|
||||
@@ -15,7 +15,7 @@ enum IncomingDataType {
|
||||
cashuToken, // cashuA... / cashuB...
|
||||
lightningInvoice, // lnbc... / lntb... / lnbcrt...
|
||||
mintUrl, // https://...
|
||||
paymentRequest, // creqA... (post-MVP, Cashu payment request)
|
||||
paymentRequest, // creqA/creqB/bitcoin:?creq= (NUT-18/26 payment request)
|
||||
unknown,
|
||||
}
|
||||
|
||||
@@ -103,8 +103,31 @@ class IncomingDataParser {
|
||||
);
|
||||
}
|
||||
|
||||
// Payment Request (creqA...)
|
||||
if (lower.startsWith('creqa')) {
|
||||
// BIP-321 unified URI: bitcoin:?lightning=...&creq=...
|
||||
// Detect as paymentRequest but also extract the lightning invoice
|
||||
if (lower.startsWith('bitcoin:')) {
|
||||
final bolt11 = _extractBip321Param(trimmed, 'lightning');
|
||||
final creq = _extractBip321Param(trimmed, 'creq');
|
||||
|
||||
if (creq != null) {
|
||||
return ParsedData(
|
||||
type: IncomingDataType.paymentRequest,
|
||||
raw: trimmed,
|
||||
invoiceBolt11: bolt11,
|
||||
);
|
||||
}
|
||||
// bitcoin: URI with only lightning= (no creq)
|
||||
if (bolt11 != null) {
|
||||
return ParsedData(
|
||||
type: IncomingDataType.lightningInvoice,
|
||||
raw: trimmed,
|
||||
invoiceBolt11: bolt11,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Payment Request: creqA (NUT-18), creqB/CREQB1 (NUT-26)
|
||||
if (lower.startsWith('creqa') || lower.startsWith('creqb')) {
|
||||
return ParsedData(
|
||||
type: IncomingDataType.paymentRequest,
|
||||
raw: trimmed,
|
||||
@@ -193,7 +216,35 @@ class IncomingDataParser {
|
||||
case ScanMode.cashuOnly:
|
||||
return data.type == IncomingDataType.cashuToken;
|
||||
case ScanMode.invoiceOnly:
|
||||
return data.type == IncomingDataType.lightningInvoice;
|
||||
// Accept pure invoices and BIP-321 URIs that contain a lightning invoice
|
||||
return data.type == IncomingDataType.lightningInvoice ||
|
||||
(data.type == IncomingDataType.paymentRequest &&
|
||||
data.invoiceBolt11 != null);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a query parameter value from a BIP-321 URI.
|
||||
/// Case-insensitive key matching, handles percent-encoding.
|
||||
static String? _extractBip321Param(String uri, String key) {
|
||||
final qIndex = uri.indexOf('?');
|
||||
if (qIndex < 0) return null;
|
||||
final query = uri.substring(qIndex + 1);
|
||||
for (final param in query.split('&')) {
|
||||
final eqIndex = param.indexOf('=');
|
||||
if (eqIndex < 0) continue;
|
||||
final k = param.substring(0, eqIndex);
|
||||
if (k.toLowerCase() == key.toLowerCase()) {
|
||||
final rawValue = param.substring(eqIndex + 1);
|
||||
if (rawValue.isEmpty) continue;
|
||||
try {
|
||||
final value = Uri.decodeComponent(rawValue);
|
||||
if (value.isEmpty) continue;
|
||||
return value;
|
||||
} on ArgumentError {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+27
-2
@@ -502,7 +502,22 @@
|
||||
"scanLightningInvoiceHint": "Scanne eine Lightning Rechnung (lnbc...)",
|
||||
"addMintQuestion": "Diesen Mint hinzufügen?",
|
||||
"cameraPermissionDenied": "Kamera-Berechtigung verweigert",
|
||||
"paymentRequestNotSupported": "Zahlungsanfragen werden noch nicht unterstützt",
|
||||
"paymentRequestTitle": "Zahlungsanfrage",
|
||||
"paymentRequestFrom": "Anfrage von",
|
||||
"paymentRequestAmount": "Angeforderter Betrag",
|
||||
"paymentRequestDescription": "Beschreibung",
|
||||
"paymentRequestMints": "Akzeptierte Mints",
|
||||
"paymentRequestAnyMint": "Jeder Mint",
|
||||
"paymentRequestPay": "Bezahlen",
|
||||
"paymentRequestPaying": "Bezahle...",
|
||||
"paymentRequestSuccess": "Zahlung erfolgreich gesendet",
|
||||
"paymentRequestNoTransport": "Diese Anfrage hat keine konfigurierte Zustellmethode",
|
||||
"paymentRequestTransport": "Transport",
|
||||
"paymentRequestMintNotAccepted": "Dein aktiver Mint ist nicht in der Liste der akzeptierten Mints",
|
||||
"paymentRequestUnitMismatch": "Inkompatible Einheit: Anfrage erfordert {unit}",
|
||||
"paymentRequestInsufficientBalance": "Unzureichendes Guthaben",
|
||||
"paymentRequestErrorParsing": "Fehler beim Lesen der Zahlungsanfrage",
|
||||
|
||||
"p2pkTitle": "P2PK-Schlüssel",
|
||||
"p2pkSettingsDescription": "Gesperrtes ecash empfangen",
|
||||
"p2pkExperimental": "P2PK ist experimentell. Mit Vorsicht verwenden.",
|
||||
@@ -534,5 +549,15 @@
|
||||
"p2pkErrorInvalidNsec": "Ungültiger nsec",
|
||||
"p2pkErrorKeyAlreadyExists": "Dieser Schlüssel existiert bereits",
|
||||
"p2pkErrorKeyNotFound": "Schlüssel nicht gefunden",
|
||||
"p2pkErrorCannotDeletePrimary": "Primärschlüssel kann nicht gelöscht werden"
|
||||
"p2pkErrorCannotDeletePrimary": "Primärschlüssel kann nicht gelöscht werden",
|
||||
|
||||
"request": "Anfordern",
|
||||
"requestPayment": "Zahlung anfordern",
|
||||
"requestPaymentDescription": "Einheitliche Zahlungsanforderung erstellen",
|
||||
"generateRequest": "Anforderung erstellen",
|
||||
"generatingRequest": "Anforderung wird erstellt...",
|
||||
"requestPaymentReceived": "Zahlung erhalten",
|
||||
"requestDescriptionHint": "Beschreibung (optional)",
|
||||
"universal": "Universal",
|
||||
"copiedToClipboard": "In die Zwischenablage kopiert"
|
||||
}
|
||||
|
||||
+31
-2
@@ -390,7 +390,26 @@
|
||||
"scanLightningInvoiceHint": "Scan a Lightning invoice (lnbc...)",
|
||||
"addMintQuestion": "Add this mint?",
|
||||
"cameraPermissionDenied": "Camera permission denied",
|
||||
"paymentRequestNotSupported": "Payment requests are not yet supported",
|
||||
"paymentRequestTitle": "Payment Request",
|
||||
"paymentRequestFrom": "Request from",
|
||||
"paymentRequestAmount": "Requested amount",
|
||||
"paymentRequestDescription": "Description",
|
||||
"paymentRequestMints": "Accepted mints",
|
||||
"paymentRequestAnyMint": "Any mint",
|
||||
"paymentRequestPay": "Pay",
|
||||
"paymentRequestPaying": "Paying...",
|
||||
"paymentRequestSuccess": "Payment sent successfully",
|
||||
"paymentRequestNoTransport": "This request has no delivery method configured",
|
||||
"paymentRequestTransport": "Transport",
|
||||
"paymentRequestMintNotAccepted": "Your active mint is not in the list of accepted mints",
|
||||
"paymentRequestUnitMismatch": "Incompatible unit: request requires {unit}",
|
||||
"@paymentRequestUnitMismatch": {
|
||||
"placeholders": {
|
||||
"unit": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"paymentRequestInsufficientBalance": "Insufficient balance",
|
||||
"paymentRequestErrorParsing": "Error reading payment request",
|
||||
|
||||
"p2pkTitle": "P2PK Keys",
|
||||
"p2pkSettingsDescription": "Receive locked ecash",
|
||||
@@ -423,5 +442,15 @@
|
||||
"p2pkErrorInvalidNsec": "Invalid nsec",
|
||||
"p2pkErrorKeyAlreadyExists": "This key already exists",
|
||||
"p2pkErrorKeyNotFound": "Key not found",
|
||||
"p2pkErrorCannotDeletePrimary": "Cannot delete primary key"
|
||||
"p2pkErrorCannotDeletePrimary": "Cannot delete primary key",
|
||||
|
||||
"request": "Request",
|
||||
"requestPayment": "Request Payment",
|
||||
"requestPaymentDescription": "Generate unified payment request",
|
||||
"generateRequest": "Generate Request",
|
||||
"generatingRequest": "Generating request...",
|
||||
"requestPaymentReceived": "Payment received",
|
||||
"requestDescriptionHint": "Description (optional)",
|
||||
"universal": "Universal",
|
||||
"copiedToClipboard": "Copied to clipboard"
|
||||
}
|
||||
|
||||
+31
-2
@@ -510,7 +510,26 @@
|
||||
"scanLightningInvoiceHint": "Escanea un invoice Lightning (lnbc...)",
|
||||
"addMintQuestion": "¿Agregar este mint?",
|
||||
"cameraPermissionDenied": "Permiso de cámara denegado",
|
||||
"paymentRequestNotSupported": "Los payment requests aún no están soportados",
|
||||
"paymentRequestTitle": "Solicitud de pago",
|
||||
"paymentRequestFrom": "Solicitud de",
|
||||
"paymentRequestAmount": "Monto solicitado",
|
||||
"paymentRequestDescription": "Descripción",
|
||||
"paymentRequestMints": "Mints aceptados",
|
||||
"paymentRequestAnyMint": "Cualquier mint",
|
||||
"paymentRequestPay": "Pagar",
|
||||
"paymentRequestPaying": "Pagando...",
|
||||
"paymentRequestSuccess": "Pago enviado correctamente",
|
||||
"paymentRequestNoTransport": "Esta solicitud no tiene método de entrega configurado",
|
||||
"paymentRequestTransport": "Transporte",
|
||||
"paymentRequestMintNotAccepted": "Tu mint activo no está en la lista de mints aceptados",
|
||||
"paymentRequestUnitMismatch": "Unidad incompatible: la solicitud requiere {unit}",
|
||||
"@paymentRequestUnitMismatch": {
|
||||
"placeholders": {
|
||||
"unit": { "type": "String" }
|
||||
}
|
||||
},
|
||||
"paymentRequestInsufficientBalance": "Balance insuficiente",
|
||||
"paymentRequestErrorParsing": "Error al leer la solicitud de pago",
|
||||
|
||||
"p2pkTitle": "Claves P2PK",
|
||||
"p2pkSettingsDescription": "Recibir ecash bloqueado",
|
||||
@@ -543,5 +562,15 @@
|
||||
"p2pkErrorInvalidNsec": "nsec inválido",
|
||||
"p2pkErrorKeyAlreadyExists": "Esta clave ya existe",
|
||||
"p2pkErrorKeyNotFound": "Clave no encontrada",
|
||||
"p2pkErrorCannotDeletePrimary": "No se puede eliminar la clave principal"
|
||||
"p2pkErrorCannotDeletePrimary": "No se puede eliminar la clave principal",
|
||||
|
||||
"request": "Solicitar",
|
||||
"requestPayment": "Solicitar pago",
|
||||
"requestPaymentDescription": "Generar solicitud de pago unificada",
|
||||
"generateRequest": "Generar solicitud",
|
||||
"generatingRequest": "Generando solicitud...",
|
||||
"requestPaymentReceived": "Pago recibido",
|
||||
"requestDescriptionHint": "Descripción (opcional)",
|
||||
"universal": "Universal",
|
||||
"copiedToClipboard": "Copiado al portapapeles"
|
||||
}
|
||||
|
||||
+27
-2
@@ -502,7 +502,22 @@
|
||||
"scanLightningInvoiceHint": "Scannez une facture Lightning (lnbc...)",
|
||||
"addMintQuestion": "Ajouter ce mint ?",
|
||||
"cameraPermissionDenied": "Permission de la caméra refusée",
|
||||
"paymentRequestNotSupported": "Les demandes de paiement ne sont pas encore prises en charge",
|
||||
"paymentRequestTitle": "Demande de paiement",
|
||||
"paymentRequestFrom": "Demande de",
|
||||
"paymentRequestAmount": "Montant demandé",
|
||||
"paymentRequestDescription": "Description",
|
||||
"paymentRequestMints": "Mints acceptés",
|
||||
"paymentRequestAnyMint": "N'importe quel mint",
|
||||
"paymentRequestPay": "Payer",
|
||||
"paymentRequestPaying": "Paiement en cours...",
|
||||
"paymentRequestSuccess": "Paiement envoyé avec succès",
|
||||
"paymentRequestNoTransport": "Cette demande n'a pas de méthode de livraison configurée",
|
||||
"paymentRequestTransport": "Transport",
|
||||
"paymentRequestMintNotAccepted": "Votre mint actif n'est pas dans la liste des mints acceptés",
|
||||
"paymentRequestUnitMismatch": "Unité incompatible : la demande nécessite {unit}",
|
||||
"paymentRequestInsufficientBalance": "Solde insuffisant",
|
||||
"paymentRequestErrorParsing": "Erreur lors de la lecture de la demande de paiement",
|
||||
|
||||
"p2pkTitle": "Clés P2PK",
|
||||
"p2pkSettingsDescription": "Recevoir ecash verrouillé",
|
||||
"p2pkExperimental": "P2PK est expérimental. Utiliser avec prudence.",
|
||||
@@ -534,5 +549,15 @@
|
||||
"p2pkErrorInvalidNsec": "nsec invalide",
|
||||
"p2pkErrorKeyAlreadyExists": "Cette clé existe déjà",
|
||||
"p2pkErrorKeyNotFound": "Clé non trouvée",
|
||||
"p2pkErrorCannotDeletePrimary": "Impossible de supprimer la clé principale"
|
||||
"p2pkErrorCannotDeletePrimary": "Impossible de supprimer la clé principale",
|
||||
|
||||
"request": "Demander",
|
||||
"requestPayment": "Demander un paiement",
|
||||
"requestPaymentDescription": "Générer une demande de paiement unifiée",
|
||||
"generateRequest": "Générer la demande",
|
||||
"generatingRequest": "Génération en cours...",
|
||||
"requestPaymentReceived": "Paiement reçu",
|
||||
"requestDescriptionHint": "Description (facultatif)",
|
||||
"universal": "Universel",
|
||||
"copiedToClipboard": "Copié dans le presse-papiers"
|
||||
}
|
||||
|
||||
+27
-2
@@ -502,7 +502,22 @@
|
||||
"scanLightningInvoiceHint": "Scansiona una fattura Lightning (lnbc...)",
|
||||
"addMintQuestion": "Aggiungere questo mint?",
|
||||
"cameraPermissionDenied": "Permesso fotocamera negato",
|
||||
"paymentRequestNotSupported": "Le richieste di pagamento non sono ancora supportate",
|
||||
"paymentRequestTitle": "Richiesta di pagamento",
|
||||
"paymentRequestFrom": "Richiesta da",
|
||||
"paymentRequestAmount": "Importo richiesto",
|
||||
"paymentRequestDescription": "Descrizione",
|
||||
"paymentRequestMints": "Mint accettati",
|
||||
"paymentRequestAnyMint": "Qualsiasi mint",
|
||||
"paymentRequestPay": "Paga",
|
||||
"paymentRequestPaying": "Pagamento in corso...",
|
||||
"paymentRequestSuccess": "Pagamento inviato con successo",
|
||||
"paymentRequestNoTransport": "Questa richiesta non ha un metodo di consegna configurato",
|
||||
"paymentRequestTransport": "Trasporto",
|
||||
"paymentRequestMintNotAccepted": "Il tuo mint attivo non è nella lista dei mint accettati",
|
||||
"paymentRequestUnitMismatch": "Unità incompatibile: la richiesta richiede {unit}",
|
||||
"paymentRequestInsufficientBalance": "Saldo insufficiente",
|
||||
"paymentRequestErrorParsing": "Errore nella lettura della richiesta di pagamento",
|
||||
|
||||
"p2pkTitle": "Chiavi P2PK",
|
||||
"p2pkSettingsDescription": "Ricevi ecash bloccato",
|
||||
"p2pkExperimental": "P2PK è sperimentale. Usare con cautela.",
|
||||
@@ -534,5 +549,15 @@
|
||||
"p2pkErrorInvalidNsec": "nsec non valido",
|
||||
"p2pkErrorKeyAlreadyExists": "Questa chiave esiste già",
|
||||
"p2pkErrorKeyNotFound": "Chiave non trovata",
|
||||
"p2pkErrorCannotDeletePrimary": "Impossibile eliminare la chiave principale"
|
||||
"p2pkErrorCannotDeletePrimary": "Impossibile eliminare la chiave principale",
|
||||
|
||||
"request": "Richiedi",
|
||||
"requestPayment": "Richiedi pagamento",
|
||||
"requestPaymentDescription": "Genera richiesta di pagamento unificata",
|
||||
"generateRequest": "Genera richiesta",
|
||||
"generatingRequest": "Generazione in corso...",
|
||||
"requestPaymentReceived": "Pagamento ricevuto",
|
||||
"requestDescriptionHint": "Descrizione (opzionale)",
|
||||
"universal": "Universale",
|
||||
"copiedToClipboard": "Copiato negli appunti"
|
||||
}
|
||||
|
||||
+27
-2
@@ -502,7 +502,22 @@
|
||||
"scanLightningInvoiceHint": "Lightningインボイスをスキャン(lnbc...)",
|
||||
"addMintQuestion": "このMintを追加しますか?",
|
||||
"cameraPermissionDenied": "カメラの許可が拒否されました",
|
||||
"paymentRequestNotSupported": "支払いリクエストはまだサポートされていません",
|
||||
"paymentRequestTitle": "支払いリクエスト",
|
||||
"paymentRequestFrom": "リクエスト元",
|
||||
"paymentRequestAmount": "リクエスト金額",
|
||||
"paymentRequestDescription": "説明",
|
||||
"paymentRequestMints": "対応ミント",
|
||||
"paymentRequestAnyMint": "すべてのミント",
|
||||
"paymentRequestPay": "支払う",
|
||||
"paymentRequestPaying": "支払い中...",
|
||||
"paymentRequestSuccess": "支払いが正常に送信されました",
|
||||
"paymentRequestNoTransport": "このリクエストには配信方法が設定されていません",
|
||||
"paymentRequestTransport": "トランスポート",
|
||||
"paymentRequestMintNotAccepted": "アクティブなミントは対応ミントリストにありません",
|
||||
"paymentRequestUnitMismatch": "単位が互換性がありません:リクエストには{unit}が必要です",
|
||||
"paymentRequestInsufficientBalance": "残高不足",
|
||||
"paymentRequestErrorParsing": "支払いリクエストの読み取りエラー",
|
||||
|
||||
"p2pkTitle": "P2PK鍵",
|
||||
"p2pkSettingsDescription": "ロックされたecashを受け取る",
|
||||
"p2pkExperimental": "P2PKは実験的機能です。注意してご使用ください。",
|
||||
@@ -534,5 +549,15 @@
|
||||
"p2pkErrorInvalidNsec": "無効なnsec",
|
||||
"p2pkErrorKeyAlreadyExists": "この鍵は既に存在します",
|
||||
"p2pkErrorKeyNotFound": "鍵が見つかりません",
|
||||
"p2pkErrorCannotDeletePrimary": "プライマリ鍵は削除できません"
|
||||
"p2pkErrorCannotDeletePrimary": "プライマリ鍵は削除できません",
|
||||
|
||||
"request": "リクエスト",
|
||||
"requestPayment": "支払いをリクエスト",
|
||||
"requestPaymentDescription": "統合支払いリクエストを生成",
|
||||
"generateRequest": "リクエストを生成",
|
||||
"generatingRequest": "生成中...",
|
||||
"requestPaymentReceived": "支払いを受け取りました",
|
||||
"requestDescriptionHint": "説明(任意)",
|
||||
"universal": "ユニバーサル",
|
||||
"copiedToClipboard": "クリップボードにコピーしました"
|
||||
}
|
||||
|
||||
+27
-2
@@ -502,7 +502,22 @@
|
||||
"scanLightningInvoiceHint": "Lightning 인보이스 스캔 (lnbc...)",
|
||||
"addMintQuestion": "이 mint를 추가하시겠습니까?",
|
||||
"cameraPermissionDenied": "카메라 권한이 거부되었습니다",
|
||||
"paymentRequestNotSupported": "결제 요청은 아직 지원되지 않습니다",
|
||||
"paymentRequestTitle": "결제 요청",
|
||||
"paymentRequestFrom": "요청자",
|
||||
"paymentRequestAmount": "요청 금액",
|
||||
"paymentRequestDescription": "설명",
|
||||
"paymentRequestMints": "허용된 민트",
|
||||
"paymentRequestAnyMint": "모든 민트",
|
||||
"paymentRequestPay": "결제",
|
||||
"paymentRequestPaying": "결제 중...",
|
||||
"paymentRequestSuccess": "결제가 성공적으로 전송되었습니다",
|
||||
"paymentRequestNoTransport": "이 요청에는 전달 방법이 설정되지 않았습니다",
|
||||
"paymentRequestTransport": "전송 방식",
|
||||
"paymentRequestMintNotAccepted": "활성 민트가 허용된 민트 목록에 없습니다",
|
||||
"paymentRequestUnitMismatch": "호환되지 않는 단위: 요청에 {unit}이(가) 필요합니다",
|
||||
"paymentRequestInsufficientBalance": "잔액 부족",
|
||||
"paymentRequestErrorParsing": "결제 요청을 읽는 중 오류 발생",
|
||||
|
||||
"p2pkTitle": "P2PK 키",
|
||||
"p2pkSettingsDescription": "잠긴 ecash 받기",
|
||||
"p2pkExperimental": "P2PK는 실험적 기능입니다. 주의하여 사용하세요.",
|
||||
@@ -534,5 +549,15 @@
|
||||
"p2pkErrorInvalidNsec": "유효하지 않은 nsec",
|
||||
"p2pkErrorKeyAlreadyExists": "이 키는 이미 존재합니다",
|
||||
"p2pkErrorKeyNotFound": "키를 찾을 수 없습니다",
|
||||
"p2pkErrorCannotDeletePrimary": "기본 키는 삭제할 수 없습니다"
|
||||
"p2pkErrorCannotDeletePrimary": "기본 키는 삭제할 수 없습니다",
|
||||
|
||||
"request": "요청",
|
||||
"requestPayment": "결제 요청",
|
||||
"requestPaymentDescription": "통합 결제 요청 생성",
|
||||
"generateRequest": "요청 생성",
|
||||
"generatingRequest": "생성 중...",
|
||||
"requestPaymentReceived": "결제가 수신되었습니다",
|
||||
"requestDescriptionHint": "설명 (선택사항)",
|
||||
"universal": "유니버설",
|
||||
"copiedToClipboard": "클립보드에 복사되었습니다"
|
||||
}
|
||||
|
||||
@@ -2017,11 +2017,95 @@ abstract class L10n {
|
||||
/// **'Permiso de cámara denegado'**
|
||||
String get cameraPermissionDenied;
|
||||
|
||||
/// No description provided for @paymentRequestNotSupported.
|
||||
/// No description provided for @paymentRequestTitle.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Los payment requests aún no están soportados'**
|
||||
String get paymentRequestNotSupported;
|
||||
/// **'Solicitud de pago'**
|
||||
String get paymentRequestTitle;
|
||||
|
||||
/// No description provided for @paymentRequestFrom.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Solicitud de'**
|
||||
String get paymentRequestFrom;
|
||||
|
||||
/// No description provided for @paymentRequestAmount.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Monto solicitado'**
|
||||
String get paymentRequestAmount;
|
||||
|
||||
/// No description provided for @paymentRequestDescription.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Descripción'**
|
||||
String get paymentRequestDescription;
|
||||
|
||||
/// No description provided for @paymentRequestMints.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Mints aceptados'**
|
||||
String get paymentRequestMints;
|
||||
|
||||
/// No description provided for @paymentRequestAnyMint.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Cualquier mint'**
|
||||
String get paymentRequestAnyMint;
|
||||
|
||||
/// No description provided for @paymentRequestPay.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Pagar'**
|
||||
String get paymentRequestPay;
|
||||
|
||||
/// No description provided for @paymentRequestPaying.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Pagando...'**
|
||||
String get paymentRequestPaying;
|
||||
|
||||
/// No description provided for @paymentRequestSuccess.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Pago enviado correctamente'**
|
||||
String get paymentRequestSuccess;
|
||||
|
||||
/// No description provided for @paymentRequestNoTransport.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Esta solicitud no tiene método de entrega configurado'**
|
||||
String get paymentRequestNoTransport;
|
||||
|
||||
/// No description provided for @paymentRequestTransport.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Transporte'**
|
||||
String get paymentRequestTransport;
|
||||
|
||||
/// No description provided for @paymentRequestMintNotAccepted.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Tu mint activo no está en la lista de mints aceptados'**
|
||||
String get paymentRequestMintNotAccepted;
|
||||
|
||||
/// No description provided for @paymentRequestUnitMismatch.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Unidad incompatible: la solicitud requiere {unit}'**
|
||||
String paymentRequestUnitMismatch(String unit);
|
||||
|
||||
/// No description provided for @paymentRequestInsufficientBalance.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Balance insuficiente'**
|
||||
String get paymentRequestInsufficientBalance;
|
||||
|
||||
/// No description provided for @paymentRequestErrorParsing.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Error al leer la solicitud de pago'**
|
||||
String get paymentRequestErrorParsing;
|
||||
|
||||
/// No description provided for @p2pkTitle.
|
||||
///
|
||||
@@ -2214,6 +2298,60 @@ abstract class L10n {
|
||||
/// In es, this message translates to:
|
||||
/// **'No se puede eliminar la clave principal'**
|
||||
String get p2pkErrorCannotDeletePrimary;
|
||||
|
||||
/// No description provided for @request.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Solicitar'**
|
||||
String get request;
|
||||
|
||||
/// No description provided for @requestPayment.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Solicitar pago'**
|
||||
String get requestPayment;
|
||||
|
||||
/// No description provided for @requestPaymentDescription.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Generar solicitud de pago unificada'**
|
||||
String get requestPaymentDescription;
|
||||
|
||||
/// No description provided for @generateRequest.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Generar solicitud'**
|
||||
String get generateRequest;
|
||||
|
||||
/// No description provided for @generatingRequest.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Generando solicitud...'**
|
||||
String get generatingRequest;
|
||||
|
||||
/// No description provided for @requestPaymentReceived.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Pago recibido'**
|
||||
String get requestPaymentReceived;
|
||||
|
||||
/// No description provided for @requestDescriptionHint.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Descripción (opcional)'**
|
||||
String get requestDescriptionHint;
|
||||
|
||||
/// No description provided for @universal.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Universal'**
|
||||
String get universal;
|
||||
|
||||
/// No description provided for @copiedToClipboard.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Copiado al portapapeles'**
|
||||
String get copiedToClipboard;
|
||||
}
|
||||
|
||||
class _L10nDelegate extends LocalizationsDelegate<L10n> {
|
||||
|
||||
@@ -1063,8 +1063,54 @@ class L10nDe extends L10n {
|
||||
String get cameraPermissionDenied => 'Kamera-Berechtigung verweigert';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported =>
|
||||
'Zahlungsanfragen werden noch nicht unterstützt';
|
||||
String get paymentRequestTitle => 'Zahlungsanfrage';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => 'Anfrage von';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => 'Angeforderter Betrag';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => 'Beschreibung';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => 'Akzeptierte Mints';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => 'Jeder Mint';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => 'Bezahlen';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => 'Bezahle...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => 'Zahlung erfolgreich gesendet';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport =>
|
||||
'Diese Anfrage hat keine konfigurierte Zustellmethode';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => 'Transport';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted =>
|
||||
'Dein aktiver Mint ist nicht in der Liste der akzeptierten Mints';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return 'Inkompatible Einheit: Anfrage erfordert $unit';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => 'Unzureichendes Guthaben';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing =>
|
||||
'Fehler beim Lesen der Zahlungsanfrage';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'P2PK-Schlüssel';
|
||||
@@ -1167,4 +1213,32 @@ class L10nDe extends L10n {
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary =>
|
||||
'Primärschlüssel kann nicht gelöscht werden';
|
||||
|
||||
@override
|
||||
String get request => 'Anfordern';
|
||||
|
||||
@override
|
||||
String get requestPayment => 'Zahlung anfordern';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription =>
|
||||
'Einheitliche Zahlungsanforderung erstellen';
|
||||
|
||||
@override
|
||||
String get generateRequest => 'Anforderung erstellen';
|
||||
|
||||
@override
|
||||
String get generatingRequest => 'Anforderung wird erstellt...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => 'Zahlung erhalten';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => 'Beschreibung (optional)';
|
||||
|
||||
@override
|
||||
String get universal => 'Universal';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => 'In die Zwischenablage kopiert';
|
||||
}
|
||||
|
||||
@@ -1050,8 +1050,53 @@ class L10nEn extends L10n {
|
||||
String get cameraPermissionDenied => 'Camera permission denied';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported =>
|
||||
'Payment requests are not yet supported';
|
||||
String get paymentRequestTitle => 'Payment Request';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => 'Request from';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => 'Requested amount';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => 'Description';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => 'Accepted mints';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => 'Any mint';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => 'Pay';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => 'Paying...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => 'Payment sent successfully';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport =>
|
||||
'This request has no delivery method configured';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => 'Transport';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted =>
|
||||
'Your active mint is not in the list of accepted mints';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return 'Incompatible unit: request requires $unit';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => 'Insufficient balance';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing => 'Error reading payment request';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'P2PK Keys';
|
||||
@@ -1150,4 +1195,31 @@ class L10nEn extends L10n {
|
||||
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary => 'Cannot delete primary key';
|
||||
|
||||
@override
|
||||
String get request => 'Request';
|
||||
|
||||
@override
|
||||
String get requestPayment => 'Request Payment';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription => 'Generate unified payment request';
|
||||
|
||||
@override
|
||||
String get generateRequest => 'Generate Request';
|
||||
|
||||
@override
|
||||
String get generatingRequest => 'Generating request...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => 'Payment received';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => 'Description (optional)';
|
||||
|
||||
@override
|
||||
String get universal => 'Universal';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => 'Copied to clipboard';
|
||||
}
|
||||
|
||||
@@ -1055,8 +1055,53 @@ class L10nEs extends L10n {
|
||||
String get cameraPermissionDenied => 'Permiso de cámara denegado';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported =>
|
||||
'Los payment requests aún no están soportados';
|
||||
String get paymentRequestTitle => 'Solicitud de pago';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => 'Solicitud de';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => 'Monto solicitado';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => 'Descripción';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => 'Mints aceptados';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => 'Cualquier mint';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => 'Pagar';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => 'Pagando...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => 'Pago enviado correctamente';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport =>
|
||||
'Esta solicitud no tiene método de entrega configurado';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => 'Transporte';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted =>
|
||||
'Tu mint activo no está en la lista de mints aceptados';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return 'Unidad incompatible: la solicitud requiere $unit';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => 'Balance insuficiente';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing => 'Error al leer la solicitud de pago';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'Claves P2PK';
|
||||
@@ -1158,4 +1203,31 @@ class L10nEs extends L10n {
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary =>
|
||||
'No se puede eliminar la clave principal';
|
||||
|
||||
@override
|
||||
String get request => 'Solicitar';
|
||||
|
||||
@override
|
||||
String get requestPayment => 'Solicitar pago';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription => 'Generar solicitud de pago unificada';
|
||||
|
||||
@override
|
||||
String get generateRequest => 'Generar solicitud';
|
||||
|
||||
@override
|
||||
String get generatingRequest => 'Generando solicitud...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => 'Pago recibido';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => 'Descripción (opcional)';
|
||||
|
||||
@override
|
||||
String get universal => 'Universal';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => 'Copiado al portapapeles';
|
||||
}
|
||||
|
||||
@@ -1068,8 +1068,54 @@ class L10nFr extends L10n {
|
||||
String get cameraPermissionDenied => 'Permission de la caméra refusée';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported =>
|
||||
'Les demandes de paiement ne sont pas encore prises en charge';
|
||||
String get paymentRequestTitle => 'Demande de paiement';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => 'Demande de';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => 'Montant demandé';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => 'Description';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => 'Mints acceptés';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => 'N\'importe quel mint';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => 'Payer';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => 'Paiement en cours...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => 'Paiement envoyé avec succès';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport =>
|
||||
'Cette demande n\'a pas de méthode de livraison configurée';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => 'Transport';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted =>
|
||||
'Votre mint actif n\'est pas dans la liste des mints acceptés';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return 'Unité incompatible : la demande nécessite $unit';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => 'Solde insuffisant';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing =>
|
||||
'Erreur lors de la lecture de la demande de paiement';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'Clés P2PK';
|
||||
@@ -1172,4 +1218,32 @@ class L10nFr extends L10n {
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary =>
|
||||
'Impossible de supprimer la clé principale';
|
||||
|
||||
@override
|
||||
String get request => 'Demander';
|
||||
|
||||
@override
|
||||
String get requestPayment => 'Demander un paiement';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription =>
|
||||
'Générer une demande de paiement unifiée';
|
||||
|
||||
@override
|
||||
String get generateRequest => 'Générer la demande';
|
||||
|
||||
@override
|
||||
String get generatingRequest => 'Génération en cours...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => 'Paiement reçu';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => 'Description (facultatif)';
|
||||
|
||||
@override
|
||||
String get universal => 'Universel';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => 'Copié dans le presse-papiers';
|
||||
}
|
||||
|
||||
@@ -1058,8 +1058,54 @@ class L10nIt extends L10n {
|
||||
String get cameraPermissionDenied => 'Permesso fotocamera negato';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported =>
|
||||
'Le richieste di pagamento non sono ancora supportate';
|
||||
String get paymentRequestTitle => 'Richiesta di pagamento';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => 'Richiesta da';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => 'Importo richiesto';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => 'Descrizione';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => 'Mint accettati';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => 'Qualsiasi mint';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => 'Paga';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => 'Pagamento in corso...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => 'Pagamento inviato con successo';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport =>
|
||||
'Questa richiesta non ha un metodo di consegna configurato';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => 'Trasporto';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted =>
|
||||
'Il tuo mint attivo non è nella lista dei mint accettati';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return 'Unità incompatibile: la richiesta richiede $unit';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => 'Saldo insufficiente';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing =>
|
||||
'Errore nella lettura della richiesta di pagamento';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'Chiavi P2PK';
|
||||
@@ -1160,4 +1206,32 @@ class L10nIt extends L10n {
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary =>
|
||||
'Impossibile eliminare la chiave principale';
|
||||
|
||||
@override
|
||||
String get request => 'Richiedi';
|
||||
|
||||
@override
|
||||
String get requestPayment => 'Richiedi pagamento';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription =>
|
||||
'Genera richiesta di pagamento unificata';
|
||||
|
||||
@override
|
||||
String get generateRequest => 'Genera richiesta';
|
||||
|
||||
@override
|
||||
String get generatingRequest => 'Generazione in corso...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => 'Pagamento ricevuto';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => 'Descrizione (opzionale)';
|
||||
|
||||
@override
|
||||
String get universal => 'Universale';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => 'Copiato negli appunti';
|
||||
}
|
||||
|
||||
@@ -1038,7 +1038,51 @@ class L10nJa extends L10n {
|
||||
String get cameraPermissionDenied => 'カメラの許可が拒否されました';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported => '支払いリクエストはまだサポートされていません';
|
||||
String get paymentRequestTitle => '支払いリクエスト';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => 'リクエスト元';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => 'リクエスト金額';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => '説明';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => '対応ミント';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => 'すべてのミント';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => '支払う';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => '支払い中...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => '支払いが正常に送信されました';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport => 'このリクエストには配信方法が設定されていません';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => 'トランスポート';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted => 'アクティブなミントは対応ミントリストにありません';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return '単位が互換性がありません:リクエストには$unitが必要です';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => '残高不足';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing => '支払いリクエストの読み取りエラー';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'P2PK鍵';
|
||||
@@ -1136,4 +1180,31 @@ class L10nJa extends L10n {
|
||||
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary => 'プライマリ鍵は削除できません';
|
||||
|
||||
@override
|
||||
String get request => 'リクエスト';
|
||||
|
||||
@override
|
||||
String get requestPayment => '支払いをリクエスト';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription => '統合支払いリクエストを生成';
|
||||
|
||||
@override
|
||||
String get generateRequest => 'リクエストを生成';
|
||||
|
||||
@override
|
||||
String get generatingRequest => '生成中...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => '支払いを受け取りました';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => '説明(任意)';
|
||||
|
||||
@override
|
||||
String get universal => 'ユニバーサル';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => 'クリップボードにコピーしました';
|
||||
}
|
||||
|
||||
@@ -1040,7 +1040,51 @@ class L10nKo extends L10n {
|
||||
String get cameraPermissionDenied => '카메라 권한이 거부되었습니다';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported => '결제 요청은 아직 지원되지 않습니다';
|
||||
String get paymentRequestTitle => '결제 요청';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => '요청자';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => '요청 금액';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => '설명';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => '허용된 민트';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => '모든 민트';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => '결제';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => '결제 중...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => '결제가 성공적으로 전송되었습니다';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport => '이 요청에는 전달 방법이 설정되지 않았습니다';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => '전송 방식';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted => '활성 민트가 허용된 민트 목록에 없습니다';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return '호환되지 않는 단위: 요청에 $unit이(가) 필요합니다';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => '잔액 부족';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing => '결제 요청을 읽는 중 오류 발생';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'P2PK 키';
|
||||
@@ -1138,4 +1182,31 @@ class L10nKo extends L10n {
|
||||
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary => '기본 키는 삭제할 수 없습니다';
|
||||
|
||||
@override
|
||||
String get request => '요청';
|
||||
|
||||
@override
|
||||
String get requestPayment => '결제 요청';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription => '통합 결제 요청 생성';
|
||||
|
||||
@override
|
||||
String get generateRequest => '요청 생성';
|
||||
|
||||
@override
|
||||
String get generatingRequest => '생성 중...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => '결제가 수신되었습니다';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => '설명 (선택사항)';
|
||||
|
||||
@override
|
||||
String get universal => '유니버설';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => '클립보드에 복사되었습니다';
|
||||
}
|
||||
|
||||
@@ -1057,8 +1057,54 @@ class L10nPt extends L10n {
|
||||
String get cameraPermissionDenied => 'Permissão de câmera negada';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported =>
|
||||
'Solicitações de pagamento ainda não são suportadas';
|
||||
String get paymentRequestTitle => 'Solicitação de pagamento';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => 'Solicitação de';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => 'Valor solicitado';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => 'Descrição';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => 'Mints aceitos';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => 'Qualquer mint';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => 'Pagar';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => 'Pagando...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => 'Pagamento enviado com sucesso';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport =>
|
||||
'Esta solicitação não tem método de entrega configurado';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => 'Transporte';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted =>
|
||||
'Seu mint ativo não está na lista de mints aceitos';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return 'Unidade incompatível: a solicitação requer $unit';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => 'Saldo insuficiente';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing =>
|
||||
'Erro ao ler a solicitação de pagamento';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'Chaves P2PK';
|
||||
@@ -1160,4 +1206,32 @@ class L10nPt extends L10n {
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary =>
|
||||
'Não é possível excluir a chave principal';
|
||||
|
||||
@override
|
||||
String get request => 'Solicitar';
|
||||
|
||||
@override
|
||||
String get requestPayment => 'Solicitar pagamento';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription =>
|
||||
'Gerar solicitação de pagamento unificada';
|
||||
|
||||
@override
|
||||
String get generateRequest => 'Gerar solicitação';
|
||||
|
||||
@override
|
||||
String get generatingRequest => 'Gerando solicitação...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => 'Pagamento recebido';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => 'Descrição (opcional)';
|
||||
|
||||
@override
|
||||
String get universal => 'Universal';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => 'Copiado para a área de transferência';
|
||||
}
|
||||
|
||||
@@ -1054,8 +1054,53 @@ class L10nRu extends L10n {
|
||||
String get cameraPermissionDenied => 'Доступ к камере запрещён';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported =>
|
||||
'Запросы на оплату пока не поддерживаются';
|
||||
String get paymentRequestTitle => 'Запрос на оплату';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => 'Запрос от';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => 'Запрошенная сумма';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => 'Описание';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => 'Принимаемые минты';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => 'Любой минт';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => 'Оплатить';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => 'Оплата...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => 'Платёж успешно отправлен';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport =>
|
||||
'В этом запросе не настроен метод доставки';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => 'Транспорт';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted =>
|
||||
'Ваш активный минт не в списке принимаемых';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return 'Несовместимая единица: запрос требует $unit';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => 'Недостаточный баланс';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing => 'Ошибка чтения запроса на оплату';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'Ключи P2PK';
|
||||
@@ -1157,4 +1202,32 @@ class L10nRu extends L10n {
|
||||
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary => 'Невозможно удалить основной ключ';
|
||||
|
||||
@override
|
||||
String get request => 'Запросить';
|
||||
|
||||
@override
|
||||
String get requestPayment => 'Запросить платёж';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription =>
|
||||
'Создать унифицированный запрос на оплату';
|
||||
|
||||
@override
|
||||
String get generateRequest => 'Создать запрос';
|
||||
|
||||
@override
|
||||
String get generatingRequest => 'Создание запроса...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => 'Платёж получен';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => 'Описание (необязательно)';
|
||||
|
||||
@override
|
||||
String get universal => 'Универсальный';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => 'Скопировано в буфер обмена';
|
||||
}
|
||||
|
||||
@@ -1057,7 +1057,54 @@ class L10nSw extends L10n {
|
||||
String get cameraPermissionDenied => 'Ruhusa ya kamera imekataliwa';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported => 'Maombi ya malipo bado hayatumiki';
|
||||
String get paymentRequestTitle => 'Ombi la malipo';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => 'Ombi kutoka';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => 'Kiasi kilichoombwa';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => 'Maelezo';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => 'Mint zinazokubaliwa';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => 'Mint yoyote';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => 'Lipa';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => 'Inalipa...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => 'Malipo yametumwa kwa mafanikio';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport =>
|
||||
'Ombi hili halina njia ya uwasilishaji iliyosanidiwa';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => 'Njia ya usafirishaji';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted =>
|
||||
'Mint yako hai haiko kwenye orodha ya mint zinazokubaliwa';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return 'Kitengo kisichooana: ombi linahitaji $unit';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => 'Salio haitoshi';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing =>
|
||||
'Hitilafu wakati wa kusoma ombi la malipo';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'Funguo za P2PK';
|
||||
@@ -1157,4 +1204,31 @@ class L10nSw extends L10n {
|
||||
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary => 'Haiwezekani kufuta ufunguo mkuu';
|
||||
|
||||
@override
|
||||
String get request => 'Omba';
|
||||
|
||||
@override
|
||||
String get requestPayment => 'Omba malipo';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription => 'Tengeneza ombi la malipo la pamoja';
|
||||
|
||||
@override
|
||||
String get generateRequest => 'Tengeneza ombi';
|
||||
|
||||
@override
|
||||
String get generatingRequest => 'Inatengeneza ombi...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => 'Malipo limepokelewa';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => 'Maelezo (si lazima)';
|
||||
|
||||
@override
|
||||
String get universal => 'Universal';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => 'Imenakiliwa kwenye ubao wa kunakili';
|
||||
}
|
||||
|
||||
@@ -1034,7 +1034,51 @@ class L10nZh extends L10n {
|
||||
String get cameraPermissionDenied => '相机权限被拒绝';
|
||||
|
||||
@override
|
||||
String get paymentRequestNotSupported => '付款请求尚不支持';
|
||||
String get paymentRequestTitle => '付款请求';
|
||||
|
||||
@override
|
||||
String get paymentRequestFrom => '来自';
|
||||
|
||||
@override
|
||||
String get paymentRequestAmount => '请求金额';
|
||||
|
||||
@override
|
||||
String get paymentRequestDescription => '描述';
|
||||
|
||||
@override
|
||||
String get paymentRequestMints => '接受的铸造厂';
|
||||
|
||||
@override
|
||||
String get paymentRequestAnyMint => '任何铸造厂';
|
||||
|
||||
@override
|
||||
String get paymentRequestPay => '支付';
|
||||
|
||||
@override
|
||||
String get paymentRequestPaying => '支付中...';
|
||||
|
||||
@override
|
||||
String get paymentRequestSuccess => '付款发送成功';
|
||||
|
||||
@override
|
||||
String get paymentRequestNoTransport => '此请求未配置交付方式';
|
||||
|
||||
@override
|
||||
String get paymentRequestTransport => '传输方式';
|
||||
|
||||
@override
|
||||
String get paymentRequestMintNotAccepted => '您的活跃铸造厂不在接受的铸造厂列表中';
|
||||
|
||||
@override
|
||||
String paymentRequestUnitMismatch(String unit) {
|
||||
return '单位不兼容:请求需要 $unit';
|
||||
}
|
||||
|
||||
@override
|
||||
String get paymentRequestInsufficientBalance => '余额不足';
|
||||
|
||||
@override
|
||||
String get paymentRequestErrorParsing => '读取付款请求时出错';
|
||||
|
||||
@override
|
||||
String get p2pkTitle => 'P2PK密钥';
|
||||
@@ -1131,4 +1175,31 @@ class L10nZh extends L10n {
|
||||
|
||||
@override
|
||||
String get p2pkErrorCannotDeletePrimary => '无法删除主密钥';
|
||||
|
||||
@override
|
||||
String get request => '请求';
|
||||
|
||||
@override
|
||||
String get requestPayment => '请求付款';
|
||||
|
||||
@override
|
||||
String get requestPaymentDescription => '生成统一付款请求';
|
||||
|
||||
@override
|
||||
String get generateRequest => '生成请求';
|
||||
|
||||
@override
|
||||
String get generatingRequest => '正在生成请求...';
|
||||
|
||||
@override
|
||||
String get requestPaymentReceived => '已收到付款';
|
||||
|
||||
@override
|
||||
String get requestDescriptionHint => '描述(可选)';
|
||||
|
||||
@override
|
||||
String get universal => '通用';
|
||||
|
||||
@override
|
||||
String get copiedToClipboard => '已复制到剪贴板';
|
||||
}
|
||||
|
||||
+27
-2
@@ -502,7 +502,22 @@
|
||||
"scanLightningInvoiceHint": "Escaneie um invoice Lightning (lnbc...)",
|
||||
"addMintQuestion": "Adicionar este mint?",
|
||||
"cameraPermissionDenied": "Permissão de câmera negada",
|
||||
"paymentRequestNotSupported": "Solicitações de pagamento ainda não são suportadas",
|
||||
"paymentRequestTitle": "Solicitação de pagamento",
|
||||
"paymentRequestFrom": "Solicitação de",
|
||||
"paymentRequestAmount": "Valor solicitado",
|
||||
"paymentRequestDescription": "Descrição",
|
||||
"paymentRequestMints": "Mints aceitos",
|
||||
"paymentRequestAnyMint": "Qualquer mint",
|
||||
"paymentRequestPay": "Pagar",
|
||||
"paymentRequestPaying": "Pagando...",
|
||||
"paymentRequestSuccess": "Pagamento enviado com sucesso",
|
||||
"paymentRequestNoTransport": "Esta solicitação não tem método de entrega configurado",
|
||||
"paymentRequestTransport": "Transporte",
|
||||
"paymentRequestMintNotAccepted": "Seu mint ativo não está na lista de mints aceitos",
|
||||
"paymentRequestUnitMismatch": "Unidade incompatível: a solicitação requer {unit}",
|
||||
"paymentRequestInsufficientBalance": "Saldo insuficiente",
|
||||
"paymentRequestErrorParsing": "Erro ao ler a solicitação de pagamento",
|
||||
|
||||
"p2pkTitle": "Chaves P2PK",
|
||||
"p2pkSettingsDescription": "Receber ecash bloqueado",
|
||||
"p2pkExperimental": "P2PK é experimental. Use com cautela.",
|
||||
@@ -534,5 +549,15 @@
|
||||
"p2pkErrorInvalidNsec": "nsec inválido",
|
||||
"p2pkErrorKeyAlreadyExists": "Esta chave já existe",
|
||||
"p2pkErrorKeyNotFound": "Chave não encontrada",
|
||||
"p2pkErrorCannotDeletePrimary": "Não é possível excluir a chave principal"
|
||||
"p2pkErrorCannotDeletePrimary": "Não é possível excluir a chave principal",
|
||||
|
||||
"request": "Solicitar",
|
||||
"requestPayment": "Solicitar pagamento",
|
||||
"requestPaymentDescription": "Gerar solicitação de pagamento unificada",
|
||||
"generateRequest": "Gerar solicitação",
|
||||
"generatingRequest": "Gerando solicitação...",
|
||||
"requestPaymentReceived": "Pagamento recebido",
|
||||
"requestDescriptionHint": "Descrição (opcional)",
|
||||
"universal": "Universal",
|
||||
"copiedToClipboard": "Copiado para a área de transferência"
|
||||
}
|
||||
|
||||
+27
-2
@@ -502,7 +502,22 @@
|
||||
"scanLightningInvoiceHint": "Сканируйте Lightning счёт (lnbc...)",
|
||||
"addMintQuestion": "Добавить этот mint?",
|
||||
"cameraPermissionDenied": "Доступ к камере запрещён",
|
||||
"paymentRequestNotSupported": "Запросы на оплату пока не поддерживаются",
|
||||
"paymentRequestTitle": "Запрос на оплату",
|
||||
"paymentRequestFrom": "Запрос от",
|
||||
"paymentRequestAmount": "Запрошенная сумма",
|
||||
"paymentRequestDescription": "Описание",
|
||||
"paymentRequestMints": "Принимаемые минты",
|
||||
"paymentRequestAnyMint": "Любой минт",
|
||||
"paymentRequestPay": "Оплатить",
|
||||
"paymentRequestPaying": "Оплата...",
|
||||
"paymentRequestSuccess": "Платёж успешно отправлен",
|
||||
"paymentRequestNoTransport": "В этом запросе не настроен метод доставки",
|
||||
"paymentRequestTransport": "Транспорт",
|
||||
"paymentRequestMintNotAccepted": "Ваш активный минт не в списке принимаемых",
|
||||
"paymentRequestUnitMismatch": "Несовместимая единица: запрос требует {unit}",
|
||||
"paymentRequestInsufficientBalance": "Недостаточный баланс",
|
||||
"paymentRequestErrorParsing": "Ошибка чтения запроса на оплату",
|
||||
|
||||
"p2pkTitle": "Ключи P2PK",
|
||||
"p2pkSettingsDescription": "Получить заблокированный ecash",
|
||||
"p2pkExperimental": "P2PK экспериментальный. Используйте с осторожностью.",
|
||||
@@ -534,5 +549,15 @@
|
||||
"p2pkErrorInvalidNsec": "Недействительный nsec",
|
||||
"p2pkErrorKeyAlreadyExists": "Этот ключ уже существует",
|
||||
"p2pkErrorKeyNotFound": "Ключ не найден",
|
||||
"p2pkErrorCannotDeletePrimary": "Невозможно удалить основной ключ"
|
||||
"p2pkErrorCannotDeletePrimary": "Невозможно удалить основной ключ",
|
||||
|
||||
"request": "Запросить",
|
||||
"requestPayment": "Запросить платёж",
|
||||
"requestPaymentDescription": "Создать унифицированный запрос на оплату",
|
||||
"generateRequest": "Создать запрос",
|
||||
"generatingRequest": "Создание запроса...",
|
||||
"requestPaymentReceived": "Платёж получен",
|
||||
"requestDescriptionHint": "Описание (необязательно)",
|
||||
"universal": "Универсальный",
|
||||
"copiedToClipboard": "Скопировано в буфер обмена"
|
||||
}
|
||||
|
||||
+27
-2
@@ -502,7 +502,22 @@
|
||||
"scanLightningInvoiceHint": "Changanua ankara ya Lightning (lnbc...)",
|
||||
"addMintQuestion": "Ongeza mint hii?",
|
||||
"cameraPermissionDenied": "Ruhusa ya kamera imekataliwa",
|
||||
"paymentRequestNotSupported": "Maombi ya malipo bado hayatumiki",
|
||||
"paymentRequestTitle": "Ombi la malipo",
|
||||
"paymentRequestFrom": "Ombi kutoka",
|
||||
"paymentRequestAmount": "Kiasi kilichoombwa",
|
||||
"paymentRequestDescription": "Maelezo",
|
||||
"paymentRequestMints": "Mint zinazokubaliwa",
|
||||
"paymentRequestAnyMint": "Mint yoyote",
|
||||
"paymentRequestPay": "Lipa",
|
||||
"paymentRequestPaying": "Inalipa...",
|
||||
"paymentRequestSuccess": "Malipo yametumwa kwa mafanikio",
|
||||
"paymentRequestNoTransport": "Ombi hili halina njia ya uwasilishaji iliyosanidiwa",
|
||||
"paymentRequestTransport": "Njia ya usafirishaji",
|
||||
"paymentRequestMintNotAccepted": "Mint yako hai haiko kwenye orodha ya mint zinazokubaliwa",
|
||||
"paymentRequestUnitMismatch": "Kitengo kisichooana: ombi linahitaji {unit}",
|
||||
"paymentRequestInsufficientBalance": "Salio haitoshi",
|
||||
"paymentRequestErrorParsing": "Hitilafu wakati wa kusoma ombi la malipo",
|
||||
|
||||
"p2pkTitle": "Funguo za P2PK",
|
||||
"p2pkSettingsDescription": "Pokea ecash iliyofungwa",
|
||||
"p2pkExperimental": "P2PK ni ya majaribio. Tumia kwa uangalifu.",
|
||||
@@ -534,5 +549,15 @@
|
||||
"p2pkErrorInvalidNsec": "nsec batili",
|
||||
"p2pkErrorKeyAlreadyExists": "Ufunguo huu tayari upo",
|
||||
"p2pkErrorKeyNotFound": "Ufunguo haujapatikana",
|
||||
"p2pkErrorCannotDeletePrimary": "Haiwezekani kufuta ufunguo mkuu"
|
||||
"p2pkErrorCannotDeletePrimary": "Haiwezekani kufuta ufunguo mkuu",
|
||||
|
||||
"request": "Omba",
|
||||
"requestPayment": "Omba malipo",
|
||||
"requestPaymentDescription": "Tengeneza ombi la malipo la pamoja",
|
||||
"generateRequest": "Tengeneza ombi",
|
||||
"generatingRequest": "Inatengeneza ombi...",
|
||||
"requestPaymentReceived": "Malipo limepokelewa",
|
||||
"requestDescriptionHint": "Maelezo (si lazima)",
|
||||
"universal": "Universal",
|
||||
"copiedToClipboard": "Imenakiliwa kwenye ubao wa kunakili"
|
||||
}
|
||||
|
||||
+27
-2
@@ -502,7 +502,22 @@
|
||||
"scanLightningInvoiceHint": "扫描闪电发票(lnbc...)",
|
||||
"addMintQuestion": "添加此铸造厂?",
|
||||
"cameraPermissionDenied": "相机权限被拒绝",
|
||||
"paymentRequestNotSupported": "付款请求尚不支持",
|
||||
"paymentRequestTitle": "付款请求",
|
||||
"paymentRequestFrom": "来自",
|
||||
"paymentRequestAmount": "请求金额",
|
||||
"paymentRequestDescription": "描述",
|
||||
"paymentRequestMints": "接受的铸造厂",
|
||||
"paymentRequestAnyMint": "任何铸造厂",
|
||||
"paymentRequestPay": "支付",
|
||||
"paymentRequestPaying": "支付中...",
|
||||
"paymentRequestSuccess": "付款发送成功",
|
||||
"paymentRequestNoTransport": "此请求未配置交付方式",
|
||||
"paymentRequestTransport": "传输方式",
|
||||
"paymentRequestMintNotAccepted": "您的活跃铸造厂不在接受的铸造厂列表中",
|
||||
"paymentRequestUnitMismatch": "单位不兼容:请求需要 {unit}",
|
||||
"paymentRequestInsufficientBalance": "余额不足",
|
||||
"paymentRequestErrorParsing": "读取付款请求时出错",
|
||||
|
||||
"p2pkTitle": "P2PK密钥",
|
||||
"p2pkSettingsDescription": "接收锁定的ecash",
|
||||
"p2pkExperimental": "P2PK是实验性功能。请谨慎使用。",
|
||||
@@ -534,5 +549,15 @@
|
||||
"p2pkErrorInvalidNsec": "无效的nsec",
|
||||
"p2pkErrorKeyAlreadyExists": "此密钥已存在",
|
||||
"p2pkErrorKeyNotFound": "未找到密钥",
|
||||
"p2pkErrorCannotDeletePrimary": "无法删除主密钥"
|
||||
"p2pkErrorCannotDeletePrimary": "无法删除主密钥",
|
||||
|
||||
"request": "请求",
|
||||
"requestPayment": "请求付款",
|
||||
"requestPaymentDescription": "生成统一付款请求",
|
||||
"generateRequest": "生成请求",
|
||||
"generatingRequest": "正在生成请求...",
|
||||
"requestPaymentReceived": "已收到付款",
|
||||
"requestDescriptionHint": "描述(可选)",
|
||||
"universal": "通用",
|
||||
"copiedToClipboard": "已复制到剪贴板"
|
||||
}
|
||||
|
||||
@@ -7,13 +7,16 @@ import '../src/rust/api/wallet.dart';
|
||||
import '../src/rust/api/token.dart';
|
||||
import '../src/rust/api/mint_info.dart';
|
||||
import '../src/rust/api/keys.dart';
|
||||
import '../src/rust/api/payment_request.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import '../data/transaction_meta_storage.dart';
|
||||
import '../data/pending_token.dart';
|
||||
import '../data/pending_token_storage.dart';
|
||||
import '../core/utils/keyset_debug.dart';
|
||||
import '../core/utils/p2pk_utils.dart';
|
||||
import '../widgets/effects/cashu_confetti.dart';
|
||||
|
||||
/// Helper class para info de token parseado
|
||||
@@ -76,6 +79,7 @@ class WalletProvider extends ChangeNotifier {
|
||||
static const _activeMintKey = 'wallet_active_mint';
|
||||
static const _activeUnitKey = 'wallet_active_unit';
|
||||
static const _pendingMintInvoicesKey = 'pending_mint_invoices';
|
||||
static const _pendingNostrRequestKey = 'pending_nostr_request';
|
||||
|
||||
/// Mint de Cuba Bitcoin - siempre aparece primero en la lista
|
||||
static const cubaBitcoinMint = 'https://mint.cubabitcoin.org';
|
||||
@@ -1062,6 +1066,27 @@ class WalletProvider extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PAYMENT REQUEST (NUT-18/26)
|
||||
// ============================================================
|
||||
|
||||
/// Paga un Payment Request (NUT-18 creqA / NUT-26 creqB / BIP-321).
|
||||
/// CDK maneja: NUT-10 spending conditions, transporte (Nostr/HTTP POST),
|
||||
/// preparación del token y entrega automática.
|
||||
Future<void> payPaymentRequest(
|
||||
String encodedRequest, {
|
||||
BigInt? customAmount,
|
||||
}) async {
|
||||
final wallet = await getActiveWallet();
|
||||
debugPrint('[PAY_REQUEST] Paying payment request via ${wallet.mintUrl}');
|
||||
await wallet.payPaymentRequest(
|
||||
encoded: encodedRequest,
|
||||
customAmount: customAmount,
|
||||
);
|
||||
debugPrint('[PAY_REQUEST] Payment delivered successfully');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// MINT (Depositar via Lightning)
|
||||
// ============================================================
|
||||
@@ -1223,6 +1248,130 @@ class WalletProvider extends ChangeNotifier {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PENDING NOSTR PAYMENT REQUESTS
|
||||
// ============================================================
|
||||
|
||||
/// TTL for pending Nostr requests (24 hours).
|
||||
static const _pendingNostrRequestTtl = Duration(hours: 24);
|
||||
StreamSubscription<NostrPaymentEvent>? _pendingNostrSubscription;
|
||||
bool _pendingNostrResumeAttempted = false;
|
||||
static const _pendingNostrSecretKey = 'pending_nostr_secret';
|
||||
static const _secureStorage = FlutterSecureStorage();
|
||||
|
||||
/// Save a pending Nostr payment request for recovery after app restart.
|
||||
/// Secret key goes to FlutterSecureStorage, metadata to SharedPreferences.
|
||||
Future<void> savePendingNostrRequest(PersistedRequestData data) async {
|
||||
try {
|
||||
// Secret key in encrypted storage
|
||||
await _secureStorage.write(
|
||||
key: _pendingNostrSecretKey,
|
||||
value: data.secretHex,
|
||||
);
|
||||
// Metadata in SharedPreferences (no secrets)
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_pendingNostrRequestKey, jsonEncode({
|
||||
'pubkeyHex': data.pubkeyHex,
|
||||
'relays': data.relays,
|
||||
'amount': data.amount?.toString(),
|
||||
'unit': data.unit,
|
||||
'mintUrl': data.mintUrl,
|
||||
'createdAt': DateTime.now().toIso8601String(),
|
||||
}));
|
||||
} catch (e) {
|
||||
debugPrint('Error saving pending Nostr request: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the pending Nostr payment request (payment received or cancelled).
|
||||
Future<void> removePendingNostrRequest() async {
|
||||
try {
|
||||
await _secureStorage.delete(key: _pendingNostrSecretKey);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(_pendingNostrRequestKey);
|
||||
} catch (e) {
|
||||
debugPrint('Error removing pending Nostr request: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for a pending Nostr payment request and resume listening.
|
||||
/// Called automatically during app startup. Guarded against duplicate calls.
|
||||
Future<void> resumePendingNostrRequest() async {
|
||||
if (_pendingNostrResumeAttempted) return;
|
||||
_pendingNostrResumeAttempted = true;
|
||||
await _pendingNostrSubscription?.cancel();
|
||||
_pendingNostrSubscription = null;
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final jsonStr = prefs.getString(_pendingNostrRequestKey);
|
||||
if (jsonStr == null) return;
|
||||
|
||||
final map = Map<String, dynamic>.from(jsonDecode(jsonStr));
|
||||
|
||||
// Check TTL
|
||||
final createdAt = DateTime.tryParse(map['createdAt'] ?? '');
|
||||
if (createdAt == null ||
|
||||
DateTime.now().difference(createdAt) > _pendingNostrRequestTtl) {
|
||||
await removePendingNostrRequest();
|
||||
debugPrint('Pending Nostr request expired, removed');
|
||||
return;
|
||||
}
|
||||
|
||||
// Read secret from secure storage
|
||||
final secretHex = await _secureStorage.read(key: _pendingNostrSecretKey);
|
||||
if (secretHex == null) {
|
||||
await prefs.remove(_pendingNostrRequestKey);
|
||||
debugPrint('Pending Nostr request has no secret key, removed');
|
||||
return;
|
||||
}
|
||||
|
||||
// Reconstruct handle
|
||||
final data = PersistedRequestData(
|
||||
secretHex: secretHex,
|
||||
pubkeyHex: map['pubkeyHex'] as String,
|
||||
relays: List<String>.from(map['relays']),
|
||||
amount: map['amount'] != null
|
||||
? BigInt.tryParse(map['amount'] as String)
|
||||
: null,
|
||||
unit: map['unit'] as String,
|
||||
mintUrl: map['mintUrl'] as String,
|
||||
);
|
||||
|
||||
final handle = NostrListenerHandle.fromPersisted(data: data);
|
||||
|
||||
// Get or create the wallet for this mint+unit (lazy instantiation)
|
||||
Wallet wallet;
|
||||
try {
|
||||
wallet = await getWallet(data.mintUrl, data.unit);
|
||||
} catch (e) {
|
||||
debugPrint('No wallet for pending Nostr request: $e');
|
||||
await removePendingNostrRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
// Resume listening in background
|
||||
debugPrint('Resuming pending Nostr payment request...');
|
||||
_pendingNostrSubscription = wallet.waitForNostrPayment(handle: handle).listen(
|
||||
(event) {
|
||||
if (event.state == NostrPaymentState.received) {
|
||||
debugPrint('Pending Nostr payment received: ${event.amount}');
|
||||
removePendingNostrRequest();
|
||||
confettiController.fire();
|
||||
_pendingNostrSubscription?.cancel();
|
||||
_pendingNostrSubscription = null;
|
||||
}
|
||||
},
|
||||
onError: (error) {
|
||||
debugPrint('Pending Nostr listener error (kept for retry): $error');
|
||||
_pendingNostrSubscription = null;
|
||||
_pendingNostrResumeAttempted = false; // Allow retry on next startup
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint('Error resuming pending Nostr request: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Guarda metadata para una transacción de mint (Lightning deposit).
|
||||
Future<void> _saveMintMetadata(Wallet wallet, String invoice) async {
|
||||
try {
|
||||
@@ -1417,6 +1566,9 @@ class WalletProvider extends ChangeNotifier {
|
||||
|
||||
// Vincular transacciones incoming sin metadata con pending invoices
|
||||
await _matchPendingMintInvoices();
|
||||
|
||||
// Resume pending Nostr payment request if app was killed mid-wait
|
||||
await resumePendingNostrRequest();
|
||||
}
|
||||
|
||||
/// Busca transacciones incoming sin metadata y las vincula con
|
||||
@@ -1701,7 +1853,8 @@ class WalletProvider extends ChangeNotifier {
|
||||
/// Retorna el monto recibido si tiene éxito, o lanza excepción.
|
||||
/// Si el token está gastado o es inválido, lo elimina automáticamente.
|
||||
/// Verifica conectividad al mint antes de intentar reclamar.
|
||||
Future<BigInt> claimPendingToken(String id) async {
|
||||
/// [p2pkPrivateKey] clave privada para desbloquear tokens P2PK.
|
||||
Future<BigInt> claimPendingToken(String id, {String? p2pkPrivateKey}) async {
|
||||
final pending = _pendingTokenStorage.get(id);
|
||||
if (pending == null) {
|
||||
throw Exception('Token pendiente no encontrado');
|
||||
@@ -1714,8 +1867,16 @@ class WalletProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
try {
|
||||
// Intentar reclamar usando el método existente
|
||||
final amount = await receiveToken(pending.encoded);
|
||||
BigInt amount;
|
||||
|
||||
if (P2PKUtils.isP2PKLocked(pending.encoded) && p2pkPrivateKey == null) {
|
||||
throw Exception('P2PK token requires a private key to claim');
|
||||
}
|
||||
|
||||
amount = await receiveToken(
|
||||
pending.encoded,
|
||||
p2pkPrivateKey: p2pkPrivateKey,
|
||||
);
|
||||
|
||||
// Éxito: eliminar de pending
|
||||
await _pendingTokenStorage.remove(id);
|
||||
@@ -1744,7 +1905,8 @@ class WalletProvider extends ChangeNotifier {
|
||||
|
||||
/// Verifica y reclama automáticamente tokens pendientes.
|
||||
/// Retorna un mapa con estadísticas: claimed, failed, removed, totalClaimed, unit.
|
||||
Future<Map<String, dynamic>> checkPendingTokens() async {
|
||||
/// [p2pkKeyResolver] función que dado un token encoded retorna la clave privada P2PK (o null).
|
||||
Future<Map<String, dynamic>> checkPendingTokens({String? Function(String encodedToken)? p2pkKeyResolver}) async {
|
||||
final tokens = _pendingTokenStorage.listValid();
|
||||
if (tokens.isEmpty) {
|
||||
return {'claimed': 0, 'failed': 0, 'removed': 0, 'totalClaimed': BigInt.zero, 'unit': _activeUnit};
|
||||
@@ -1758,7 +1920,8 @@ class WalletProvider extends ChangeNotifier {
|
||||
|
||||
for (final token in tokens) {
|
||||
try {
|
||||
final amount = await claimPendingToken(token.id);
|
||||
final p2pkKey = p2pkKeyResolver?.call(token.encoded);
|
||||
final amount = await claimPendingToken(token.id, p2pkPrivateKey: p2pkKey);
|
||||
claimed++;
|
||||
totalClaimed += amount;
|
||||
claimedUnit ??= token.unit; // Usar la unidad del primer token reclamado
|
||||
|
||||
@@ -8,6 +8,7 @@ import '../../widgets/scanner/qr_scanner_widget.dart';
|
||||
import '../../providers/wallet_provider.dart';
|
||||
import '../4_receive/receive_screen.dart';
|
||||
import '../7_melt/melt_screen.dart';
|
||||
import '../11_payment_request/payment_request_screen.dart';
|
||||
|
||||
/// Pantalla de escaneo QR con soporte para diferentes modos
|
||||
class ScanScreen extends StatefulWidget {
|
||||
@@ -195,8 +196,17 @@ class _ScanScreenState extends State<ScanScreen> {
|
||||
break;
|
||||
|
||||
case IncomingDataType.paymentRequest:
|
||||
// TODO: Implementar manejo de payment requests (post-MVP)
|
||||
_showError(l10n.paymentRequestNotSupported);
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PaymentRequestScreen(
|
||||
encodedRequest: data.raw,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case IncomingDataType.unknown:
|
||||
@@ -221,8 +231,10 @@ class _ScanScreenState extends State<ScanScreen> {
|
||||
}
|
||||
|
||||
void _handleInvoiceOnlyMode(ParsedData data) {
|
||||
if (data.type == IncomingDataType.lightningInvoice) {
|
||||
// Retornar el invoice vía callback
|
||||
if (data.type == IncomingDataType.lightningInvoice ||
|
||||
(data.type == IncomingDataType.paymentRequest &&
|
||||
data.invoiceBolt11 != null)) {
|
||||
// Return the invoice — works for pure BOLT11 and BIP-321 URIs with lightning=
|
||||
Navigator.pop(context, data.invoiceBolt11 ?? data.raw);
|
||||
widget.onDataScanned?.call(data.invoiceBolt11 ?? data.raw);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:elcaju/l10n/app_localizations.dart';
|
||||
import '../../core/constants/colors.dart';
|
||||
import '../../core/constants/dimensions.dart';
|
||||
import '../../core/utils/formatters.dart';
|
||||
import '../../src/rust/api/payment_request.dart';
|
||||
import '../../widgets/common/gradient_background.dart';
|
||||
import '../../widgets/common/glass_card.dart';
|
||||
import '../../widgets/common/primary_button.dart';
|
||||
import '../../providers/wallet_provider.dart';
|
||||
|
||||
/// Pantalla de confirmación para pagar un Payment Request (NUT-18/26)
|
||||
class PaymentRequestScreen extends StatefulWidget {
|
||||
/// Raw encoded payment request (creqA, CREQB1, or bitcoin:?creq=)
|
||||
final String encodedRequest;
|
||||
|
||||
const PaymentRequestScreen({super.key, required this.encodedRequest});
|
||||
|
||||
@override
|
||||
State<PaymentRequestScreen> createState() => _PaymentRequestScreenState();
|
||||
}
|
||||
|
||||
class _PaymentRequestScreenState extends State<PaymentRequestScreen> {
|
||||
PaymentRequestInfo? _info;
|
||||
String? _parseError;
|
||||
bool _isPaying = false;
|
||||
String? _payError;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_parseRequest();
|
||||
}
|
||||
|
||||
void _parseRequest() {
|
||||
try {
|
||||
final info = PaymentRequestInfo.parse(encoded: widget.encodedRequest);
|
||||
setState(() => _info = info);
|
||||
} catch (e) {
|
||||
setState(() => _parseError = e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = L10n.of(context)!;
|
||||
|
||||
return GradientBackground(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
title: Text(
|
||||
l10n.paymentRequestTitle,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
leading: IconButton(
|
||||
icon: const Icon(LucideIcons.arrowLeft),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
body: _parseError != null
|
||||
? _buildError(l10n)
|
||||
: _info == null
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: _buildContent(l10n),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildError(L10n l10n) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(AppDimensions.paddingLarge),
|
||||
child: GlassCard(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(LucideIcons.alertCircle, color: Colors.redAccent, size: 48),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
l10n.paymentRequestErrorParsing,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
_parseError!,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 13,
|
||||
color: Colors.white.withValues(alpha: 0.6),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(L10n l10n) {
|
||||
final info = _info!;
|
||||
final walletProvider = context.watch<WalletProvider>();
|
||||
final activeUnit = walletProvider.activeUnit;
|
||||
final requestUnit = info.unit ?? 'sat';
|
||||
|
||||
// Validaciones
|
||||
final unitMismatch = info.unit != null && info.unit != activeUnit;
|
||||
String stripSlash(String url) =>
|
||||
url.endsWith('/') ? url.substring(0, url.length - 1) : url;
|
||||
final normalizedMints = info.mints.map(stripSlash).toSet();
|
||||
final activeMint = walletProvider.activeMintUrl;
|
||||
final mintNotAccepted = normalizedMints.isNotEmpty &&
|
||||
(activeMint == null || !normalizedMints.contains(stripSlash(activeMint)));
|
||||
final hasTransport = info.transports.isNotEmpty;
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(AppDimensions.paddingLarge),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Monto
|
||||
if (info.amount != null)
|
||||
_buildAmountCard(info.amount!, requestUnit, l10n),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Detalles
|
||||
GlassCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Descripción
|
||||
if (info.description != null && info.description!.isNotEmpty) ...[
|
||||
_buildDetailRow(
|
||||
LucideIcons.fileText,
|
||||
l10n.paymentRequestDescription,
|
||||
info.description!,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
|
||||
// Mints aceptados
|
||||
_buildDetailRow(
|
||||
LucideIcons.server,
|
||||
l10n.paymentRequestMints,
|
||||
info.mints.isEmpty
|
||||
? l10n.paymentRequestAnyMint
|
||||
: info.mints.map((m) => Uri.parse(m).host).join(', '),
|
||||
),
|
||||
|
||||
// Transporte (solo mostrar si hay transporte configurado)
|
||||
if (info.transports.isNotEmpty) ...[
|
||||
const SizedBox(height: 12),
|
||||
_buildDetailRow(
|
||||
LucideIcons.send,
|
||||
l10n.paymentRequestTransport,
|
||||
info.transports
|
||||
.map((t) => t.transportType == 'nostr' ? 'Nostr (NIP-17)' : 'HTTP POST')
|
||||
.join(', '),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Warnings
|
||||
if (unitMismatch)
|
||||
_buildWarning(
|
||||
LucideIcons.alertTriangle,
|
||||
l10n.paymentRequestUnitMismatch(requestUnit),
|
||||
),
|
||||
if (mintNotAccepted)
|
||||
_buildWarning(
|
||||
LucideIcons.alertTriangle,
|
||||
l10n.paymentRequestMintNotAccepted,
|
||||
),
|
||||
if (!hasTransport)
|
||||
_buildWarning(
|
||||
LucideIcons.info,
|
||||
l10n.paymentRequestNoTransport,
|
||||
),
|
||||
|
||||
if (_payError != null) ...[
|
||||
const SizedBox(height: 12),
|
||||
_buildWarning(LucideIcons.alertCircle, _payError!),
|
||||
],
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Botón pagar
|
||||
PrimaryButton(
|
||||
text: _isPaying ? l10n.paymentRequestPaying : l10n.paymentRequestPay,
|
||||
icon: LucideIcons.zap,
|
||||
isLoading: _isPaying,
|
||||
onPressed: (!hasTransport || _isPaying)
|
||||
? null
|
||||
: _pay,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAmountCard(BigInt amount, String unit, L10n l10n) {
|
||||
return GlassCard(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
l10n.paymentRequestAmount,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 14,
|
||||
color: Colors.white.withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
UnitFormatter.formatBalanceWithUnit(amount, unit),
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDetailRow(IconData icon, String label, String value) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(icon, size: 18, color: AppColors.primaryAction),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white.withValues(alpha: 0.6),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWarning(IconData icon, String text) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: GlassCard(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, size: 20, color: Colors.amber),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 13,
|
||||
color: Colors.amber,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _pay() async {
|
||||
setState(() {
|
||||
_isPaying = true;
|
||||
_payError = null;
|
||||
});
|
||||
|
||||
try {
|
||||
final walletProvider = context.read<WalletProvider>();
|
||||
await walletProvider.payPaymentRequest(
|
||||
widget.encodedRequest,
|
||||
customAmount: _info!.amount,
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
// Éxito — mostrar snackbar y volver
|
||||
final l10n = L10n.of(context)!;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.paymentRequestSuccess),
|
||||
backgroundColor: Colors.green.shade700,
|
||||
),
|
||||
);
|
||||
Navigator.pop(context, true);
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isPaying = false;
|
||||
_payError = e.toString();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,895 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import '../../src/rust/api/wallet.dart';
|
||||
import '../../src/rust/api/payment_request.dart';
|
||||
import 'package:elcaju/l10n/app_localizations.dart';
|
||||
import '../../core/constants/colors.dart';
|
||||
import '../../core/constants/dimensions.dart';
|
||||
import '../../core/utils/formatters.dart';
|
||||
import '../../core/utils/bip321_builder.dart';
|
||||
import '../../core/services/nfc_service.dart';
|
||||
import '../../widgets/common/gradient_background.dart';
|
||||
import '../../widgets/common/glass_card.dart';
|
||||
import '../../widgets/common/primary_button.dart';
|
||||
import '../../widgets/common/numpad_widget.dart';
|
||||
import '../../providers/wallet_provider.dart';
|
||||
|
||||
enum RequestStatus { input, generating, waiting, received, error }
|
||||
enum QrMode { universal, cashu, lightning }
|
||||
|
||||
/// Pantalla para solicitar pagos via Payment Request unificado.
|
||||
/// Genera creqB (Cashu/Nostr) + Lightning invoice en paralelo.
|
||||
class RequestScreen extends StatefulWidget {
|
||||
const RequestScreen({super.key});
|
||||
|
||||
@override
|
||||
State<RequestScreen> createState() => _RequestScreenState();
|
||||
}
|
||||
|
||||
class _RequestScreenState extends State<RequestScreen> {
|
||||
final TextEditingController _descriptionController = TextEditingController();
|
||||
|
||||
// Input state
|
||||
String _amountValue = '';
|
||||
late String _activeUnit;
|
||||
|
||||
// Request state
|
||||
RequestStatus _status = RequestStatus.input;
|
||||
String? _errorMessage;
|
||||
|
||||
// Payment data
|
||||
String? _creqB;
|
||||
String? _bolt11;
|
||||
QrMode _activeMode = QrMode.cashu;
|
||||
bool _paymentHandled = false;
|
||||
|
||||
// Listeners
|
||||
StreamSubscription<NostrPaymentEvent>? _nostrSubscription;
|
||||
StreamSubscription<MintQuote>? _mintSubscription;
|
||||
|
||||
// NFC
|
||||
NfcState _nfcState = NfcState.unsupported;
|
||||
bool _nfcEmulating = false;
|
||||
|
||||
// Success
|
||||
BigInt _receivedAmount = BigInt.zero;
|
||||
|
||||
late final WalletProvider _walletProvider;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_walletProvider = context.read<WalletProvider>();
|
||||
_activeUnit = _walletProvider.activeUnit;
|
||||
_checkNfc();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_descriptionController.dispose();
|
||||
_nostrSubscription?.cancel();
|
||||
_mintSubscription?.cancel();
|
||||
if (_nfcEmulating) NfcService.stopEmulating();
|
||||
// Clear persisted request if user abandoned without receiving payment
|
||||
if (!_paymentHandled && _creqB != null) {
|
||||
_walletProvider.removePendingNostrRequest();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _checkNfc() async {
|
||||
final state = await NfcService.checkState();
|
||||
if (mounted) setState(() => _nfcState = state);
|
||||
}
|
||||
|
||||
String get _unitLabel => UnitFormatter.getUnitLabel(_activeUnit);
|
||||
BigInt get _amount => UnitFormatter.parseRawDigits(_amountValue, _activeUnit);
|
||||
bool get _isValidAmount => _amount > BigInt.zero;
|
||||
|
||||
// ─── Build ───
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GradientBackground(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(LucideIcons.arrowLeft, color: Colors.white),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text(
|
||||
L10n.of(context)!.requestPayment,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
if (_status == RequestStatus.waiting)
|
||||
IconButton(
|
||||
icon: const Icon(LucideIcons.share2, color: Colors.white),
|
||||
onPressed: _shareRequest,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(child: _buildBody()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
switch (_status) {
|
||||
case RequestStatus.input:
|
||||
return _buildInputView();
|
||||
case RequestStatus.generating:
|
||||
return _buildGeneratingView();
|
||||
case RequestStatus.waiting:
|
||||
return _buildWaitingView();
|
||||
case RequestStatus.received:
|
||||
return _buildSuccessView();
|
||||
case RequestStatus.error:
|
||||
return _buildErrorView();
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Input View ───
|
||||
|
||||
Widget _buildInputView() {
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(AppDimensions.paddingMedium),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildAmountSection(),
|
||||
const SizedBox(height: AppDimensions.paddingLarge),
|
||||
_buildDescriptionSection(),
|
||||
const SizedBox(height: AppDimensions.paddingMedium),
|
||||
if (_errorMessage != null) _buildErrorMessage(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(AppDimensions.paddingMedium),
|
||||
child: PrimaryButton(
|
||||
text: L10n.of(context)!.generateRequest,
|
||||
onPressed: _isValidAmount ? _generateRequest : null,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAmountSection() {
|
||||
final displayAmount =
|
||||
UnitFormatter.formatRawDigitsForDisplay(_amountValue, _activeUnit);
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
displayAmount,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 48,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _isValidAmount || _amountValue.isEmpty
|
||||
? Colors.white
|
||||
: AppColors.error,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
_unitLabel,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 18,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimensions.paddingMedium),
|
||||
NumpadWidget(
|
||||
value: _amountValue,
|
||||
onChanged: (v) => setState(() {
|
||||
_errorMessage = null;
|
||||
_amountValue = v;
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDescriptionSection() {
|
||||
final l10n = L10n.of(context)!;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.descriptionOptional,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimensions.paddingSmall),
|
||||
GlassCard(
|
||||
padding: const EdgeInsets.all(AppDimensions.paddingSmall),
|
||||
child: TextField(
|
||||
controller: _descriptionController,
|
||||
maxLines: 2,
|
||||
maxLength: 100,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.requestDescriptionHint,
|
||||
hintStyle: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 16,
|
||||
color: Colors.white.withValues(alpha: 0.3),
|
||||
),
|
||||
border: InputBorder.none,
|
||||
counterStyle: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 12,
|
||||
color: AppColors.textSecondary.withValues(alpha: 0.5),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorMessage() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: AppDimensions.paddingMedium),
|
||||
padding: const EdgeInsets.all(AppDimensions.paddingMedium),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.error.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: AppColors.error.withValues(alpha: 0.3),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(LucideIcons.alertCircle, color: AppColors.error, size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
_errorMessage!,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 14,
|
||||
color: AppColors.error,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Generating View ───
|
||||
|
||||
Widget _buildGeneratingView() {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const CircularProgressIndicator(color: AppColors.primaryAction),
|
||||
const SizedBox(height: AppDimensions.paddingMedium),
|
||||
Text(
|
||||
L10n.of(context)!.generatingRequest,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 16,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Waiting View (QR + sub-toggle + actions) ───
|
||||
|
||||
Widget _buildWaitingView() {
|
||||
final l10n = L10n.of(context)!;
|
||||
final formattedAmount = UnitFormatter.formatBalance(_amount, _activeUnit);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(AppDimensions.paddingMedium),
|
||||
child: Column(
|
||||
children: [
|
||||
// Amount header
|
||||
Text(
|
||||
'$formattedAmount $_unitLabel',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimensions.paddingLarge),
|
||||
|
||||
// QR Code with logo overlay
|
||||
Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
QrImageView(
|
||||
data: _getActiveQrContent(),
|
||||
version: QrVersions.auto,
|
||||
size: 260,
|
||||
backgroundColor: Colors.white,
|
||||
errorCorrectionLevel: _activeMode == QrMode.universal
|
||||
? QrErrorCorrectLevel.M
|
||||
: QrErrorCorrectLevel.H,
|
||||
),
|
||||
if (_activeMode == QrMode.cashu)
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: Colors.white, width: 4),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: Image.asset(
|
||||
'assets/img/cashu.png',
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_activeMode == QrMode.lightning)
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bitcoinOrange,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: Colors.white, width: 4),
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.zap,
|
||||
color: Colors.white,
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimensions.paddingMedium),
|
||||
|
||||
// Sub-toggle: Universal / Cashu / Lightning
|
||||
_buildModeToggle(),
|
||||
const SizedBox(height: AppDimensions.paddingMedium),
|
||||
|
||||
// Action buttons: Copy, NFC, Share
|
||||
_buildActionButtons(),
|
||||
const SizedBox(height: AppDimensions.paddingMedium),
|
||||
|
||||
// Status indicator
|
||||
_buildStatusIndicator(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Cancel button
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(AppDimensions.paddingMedium),
|
||||
child: GestureDetector(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildModeToggle() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
_buildToggleButton(
|
||||
label: L10n.of(context)!.universal,
|
||||
mode: QrMode.universal,
|
||||
enabled: _bolt11 != null,
|
||||
),
|
||||
_buildToggleButton(
|
||||
label: 'Cashu',
|
||||
mode: QrMode.cashu,
|
||||
enabled: true,
|
||||
),
|
||||
_buildToggleButton(
|
||||
label: 'Lightning',
|
||||
mode: QrMode.lightning,
|
||||
enabled: _bolt11 != null,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildToggleButton({
|
||||
required String label,
|
||||
required QrMode mode,
|
||||
required bool enabled,
|
||||
}) {
|
||||
final isActive = _activeMode == mode;
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: enabled
|
||||
? () {
|
||||
setState(() => _activeMode = mode);
|
||||
_updateNfcPayload();
|
||||
}
|
||||
: null,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
gradient: isActive
|
||||
? const LinearGradient(colors: AppColors.buttonGradient)
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 13,
|
||||
fontWeight: isActive ? FontWeight.w600 : FontWeight.w400,
|
||||
color: enabled
|
||||
? (isActive ? Colors.white : AppColors.textSecondary)
|
||||
: Colors.white.withValues(alpha: 0.3),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButtons() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildActionButton(
|
||||
icon: LucideIcons.copy,
|
||||
label: L10n.of(context)!.copy,
|
||||
onTap: _copyRequest,
|
||||
),
|
||||
if (_nfcState == NfcState.enabled) ...[
|
||||
const SizedBox(width: 16),
|
||||
_buildActionButton(
|
||||
icon: _nfcEmulating ? LucideIcons.wifiOff : LucideIcons.wifi,
|
||||
label: 'NFC',
|
||||
onTap: _toggleNfc,
|
||||
active: _nfcEmulating,
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 16),
|
||||
_buildActionButton(
|
||||
icon: LucideIcons.share2,
|
||||
label: L10n.of(context)!.share,
|
||||
onTap: _shareRequest,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButton({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required VoidCallback onTap,
|
||||
bool active = false,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: active
|
||||
? AppColors.primaryAction.withValues(alpha: 0.2)
|
||||
: Colors.white.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: active
|
||||
? Border.all(color: AppColors.primaryAction.withValues(alpha: 0.5))
|
||||
: null,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, color: active ? AppColors.primaryAction : AppColors.textSecondary, size: 18),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 14,
|
||||
color: active ? AppColors.primaryAction : AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusIndicator() {
|
||||
return GlassCard(
|
||||
padding: const EdgeInsets.all(AppDimensions.paddingMedium),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(AppColors.textSecondary),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
L10n.of(context)!.waitingForPayment,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Success View ───
|
||||
|
||||
Widget _buildSuccessView() {
|
||||
final l10n = L10n.of(context)!;
|
||||
final formattedAmount =
|
||||
'+${UnitFormatter.formatBalance(_receivedAmount, _activeUnit)}';
|
||||
final unitLabel = UnitFormatter.getUnitLabel(_activeUnit);
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.success.withValues(alpha: 0.2),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.checkCircle2,
|
||||
color: AppColors.success,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimensions.paddingMedium),
|
||||
Text(
|
||||
'$formattedAmount $unitLabel',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimensions.paddingSmall),
|
||||
Text(
|
||||
l10n.requestPaymentReceived,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 16,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimensions.paddingLarge * 2),
|
||||
PrimaryButton(
|
||||
text: l10n.backToHome,
|
||||
onPressed: () =>
|
||||
Navigator.popUntil(context, (route) => route.isFirst),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Error View ───
|
||||
|
||||
Widget _buildErrorView() {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.error.withValues(alpha: 0.2),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.alertCircle,
|
||||
color: AppColors.error,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimensions.paddingMedium),
|
||||
Text(
|
||||
L10n.of(context)!.error,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimensions.paddingSmall),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: Text(
|
||||
_errorMessage ?? L10n.of(context)!.unknownError,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 14,
|
||||
color: AppColors.textSecondary.withValues(alpha: 0.7),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppDimensions.paddingLarge),
|
||||
PrimaryButton(
|
||||
text: L10n.of(context)!.back,
|
||||
onPressed: () => setState(() {
|
||||
_status = RequestStatus.input;
|
||||
_errorMessage = null;
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Logic ───
|
||||
|
||||
static const List<String> _defaultNostrRelays = [
|
||||
'wss://relay.damus.io',
|
||||
'wss://relay.primal.net',
|
||||
'wss://nos.lol',
|
||||
];
|
||||
|
||||
Future<void> _generateRequest() async {
|
||||
final walletProvider = context.read<WalletProvider>();
|
||||
final wallet = walletProvider.activeWallet;
|
||||
|
||||
// Clean up previous request state — await to ensure Rust locks are released
|
||||
await _nostrSubscription?.cancel();
|
||||
_nostrSubscription = null;
|
||||
await _mintSubscription?.cancel();
|
||||
_mintSubscription = null;
|
||||
if (_nfcEmulating) {
|
||||
NfcService.stopEmulating();
|
||||
_nfcEmulating = false;
|
||||
}
|
||||
_paymentHandled = false;
|
||||
_creqB = null;
|
||||
_bolt11 = null;
|
||||
_activeMode = QrMode.cashu;
|
||||
|
||||
setState(() => _status = RequestStatus.generating);
|
||||
|
||||
if (wallet == null) {
|
||||
setState(() {
|
||||
_status = RequestStatus.error;
|
||||
_errorMessage = 'No active wallet';
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final description = _descriptionController.text.isNotEmpty
|
||||
? _descriptionController.text
|
||||
: null;
|
||||
|
||||
// 1. Create payment request (creqB + Nostr keys)
|
||||
final request = await wallet.createPaymentRequest(
|
||||
params: CreateRequestParams(
|
||||
amount: _amount,
|
||||
unit: _activeUnit,
|
||||
description: description,
|
||||
nostrRelays: _defaultNostrRelays,
|
||||
),
|
||||
);
|
||||
|
||||
_creqB = request.creqB;
|
||||
|
||||
// Persist handle for recovery if app is killed
|
||||
await walletProvider.savePendingNostrRequest(
|
||||
request.listenerHandle.toPersisted(),
|
||||
);
|
||||
|
||||
// 2. Start Nostr listener
|
||||
_nostrSubscription = wallet
|
||||
.waitForNostrPayment(handle: request.listenerHandle)
|
||||
.listen(_onNostrEvent);
|
||||
|
||||
// 3. Start Lightning invoice generation directly via CDK
|
||||
_mintSubscription = wallet
|
||||
.mint(amount: _amount, description: description)
|
||||
.listen(_onMintEvent);
|
||||
|
||||
setState(() => _status = RequestStatus.waiting);
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_status = RequestStatus.error;
|
||||
_errorMessage = e.toString();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onNostrEvent(NostrPaymentEvent event) {
|
||||
if (!mounted || _paymentHandled) return;
|
||||
if (event.state == NostrPaymentState.received) {
|
||||
_paymentHandled = true;
|
||||
_mintSubscription?.cancel();
|
||||
_onPaymentSuccess(event.amount ?? _amount);
|
||||
}
|
||||
}
|
||||
|
||||
void _onMintEvent(MintQuote quote) {
|
||||
if (!mounted) return;
|
||||
switch (quote.state) {
|
||||
case MintQuoteState.unpaid:
|
||||
if (_bolt11 == null) {
|
||||
setState(() {
|
||||
_bolt11 = quote.request;
|
||||
_activeMode = QrMode.universal;
|
||||
});
|
||||
_updateNfcPayload();
|
||||
}
|
||||
break;
|
||||
case MintQuoteState.issued:
|
||||
if (!_paymentHandled) {
|
||||
_paymentHandled = true;
|
||||
_nostrSubscription?.cancel();
|
||||
_onPaymentSuccess(quote.amount ?? _amount);
|
||||
}
|
||||
break;
|
||||
case MintQuoteState.error:
|
||||
// Lightning failed, but Nostr listener continues
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onPaymentSuccess(BigInt amount) async {
|
||||
if (_nfcEmulating) NfcService.stopEmulating();
|
||||
setState(() {
|
||||
_status = RequestStatus.received;
|
||||
_receivedAmount = amount;
|
||||
});
|
||||
final walletProvider = context.read<WalletProvider>();
|
||||
walletProvider.confettiController.fire();
|
||||
await walletProvider.removePendingNostrRequest();
|
||||
}
|
||||
|
||||
// ─── QR Content ───
|
||||
|
||||
String _getActiveQrContent() {
|
||||
switch (_activeMode) {
|
||||
case QrMode.universal:
|
||||
return buildUnifiedUri(creqB: _creqB!, bolt11: _bolt11);
|
||||
case QrMode.cashu:
|
||||
return _creqB!.toUpperCase();
|
||||
case QrMode.lightning:
|
||||
return _bolt11?.toUpperCase() ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Actions ───
|
||||
|
||||
Future<void> _copyRequest() async {
|
||||
final content = _getActiveQrContent();
|
||||
await Clipboard.setData(ClipboardData(text: content));
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context)!.copiedToClipboard),
|
||||
backgroundColor: AppColors.success,
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _toggleNfc() {
|
||||
if (_nfcEmulating) {
|
||||
NfcService.stopEmulating();
|
||||
setState(() => _nfcEmulating = false);
|
||||
} else {
|
||||
NfcService.startEmulating(_getActiveQrContent());
|
||||
setState(() => _nfcEmulating = true);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateNfcPayload() {
|
||||
if (_nfcEmulating) {
|
||||
NfcService.startEmulating(_getActiveQrContent());
|
||||
}
|
||||
}
|
||||
|
||||
void _shareRequest() async {
|
||||
final content = _getActiveQrContent();
|
||||
final formattedAmount =
|
||||
UnitFormatter.formatBalance(_amount, _activeUnit);
|
||||
final unitLabel = UnitFormatter.getUnitLabel(_activeUnit);
|
||||
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
text: '$formattedAmount $unitLabel\n\n$content',
|
||||
subject: 'Payment Request - $formattedAmount $unitLabel',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,10 @@ import '../../widgets/common/glass_card.dart';
|
||||
import '../../widgets/common/animated_action_button.dart';
|
||||
import '../../providers/wallet_provider.dart';
|
||||
import '../../providers/settings_provider.dart';
|
||||
import '../../providers/p2pk_provider.dart';
|
||||
import '../4_receive/receive_screen.dart';
|
||||
import '../5_send/send_screen.dart';
|
||||
import '../6_mint/mint_screen.dart';
|
||||
import '../12_request/request_screen.dart';
|
||||
import '../7_melt/melt_screen.dart';
|
||||
import '../8_settings/settings_screen.dart';
|
||||
import '../8_settings/mints_screen.dart';
|
||||
@@ -51,7 +52,10 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
if (!walletProvider.hasPendingTokens) return;
|
||||
|
||||
try {
|
||||
final result = await walletProvider.checkPendingTokens();
|
||||
final p2pkProvider = context.read<P2PKProvider>();
|
||||
final result = await walletProvider.checkPendingTokens(
|
||||
p2pkKeyResolver: p2pkProvider.getPrivateKeyForToken,
|
||||
);
|
||||
final claimed = (result['claimed'] as int?) ?? 0;
|
||||
final totalClaimed = result['totalClaimed'] as BigInt? ?? BigInt.zero;
|
||||
final unit = (result['unit'] as String?) ?? walletProvider.activeUnit;
|
||||
@@ -433,14 +437,14 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
},
|
||||
),
|
||||
_MethodOption(
|
||||
icon: LucideIcons.zap,
|
||||
label: l10n.lightning,
|
||||
description: l10n.generateInvoiceToDeposit,
|
||||
icon: LucideIcons.bellRing,
|
||||
label: l10n.request,
|
||||
description: l10n.requestPaymentDescription,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const MintScreen()),
|
||||
MaterialPageRoute(builder: (context) => const RequestScreen()),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -16,6 +16,7 @@ import '../../widgets/common/primary_button.dart';
|
||||
import '../../providers/wallet_provider.dart';
|
||||
import '../../providers/p2pk_provider.dart';
|
||||
import '../10_scanner/scan_screen.dart';
|
||||
import '../11_payment_request/payment_request_screen.dart';
|
||||
|
||||
/// Pantalla para recibir tokens Cashu
|
||||
class ReceiveScreen extends StatefulWidget {
|
||||
@@ -993,6 +994,25 @@ class _ReceiveScreenState extends State<ReceiveScreen> {
|
||||
return;
|
||||
}
|
||||
|
||||
// Detectar payment request y redirigir
|
||||
final parsed = IncomingDataParser.parse(tokenValue);
|
||||
if (parsed.type == IncomingDataType.paymentRequest) {
|
||||
_isValidToken = false;
|
||||
_tokenInfo = null;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PaymentRequestScreen(
|
||||
encodedRequest: tokenValue,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Parsear token real con cdk-flutter
|
||||
final tokenInfo = walletProvider.parseToken(tokenValue);
|
||||
|
||||
|
||||
@@ -581,6 +581,7 @@ class _SendScreenState extends State<SendScreen> {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => _ConfirmationModal(
|
||||
amount: _amount,
|
||||
unit: _activeUnit,
|
||||
|
||||
@@ -80,10 +80,9 @@ class _InvoiceScreenState extends State<InvoiceScreen> {
|
||||
_onMintCompleted();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Cualquier otro estado se trata como error
|
||||
case MintQuoteState.error:
|
||||
_status = MintStatus.error;
|
||||
_errorMessage = L10n.of(context)!.unknownState;
|
||||
_errorMessage = quote.error ?? L10n.of(context)!.unknownError;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -249,6 +249,7 @@ class _MintScreenState extends State<MintScreen> {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => _ConfirmationModal(
|
||||
amount: _amount,
|
||||
unit: _activeUnit,
|
||||
|
||||
@@ -594,15 +594,28 @@ class _MeltScreenState extends State<MeltScreen> {
|
||||
}
|
||||
});
|
||||
|
||||
// Extract lightning invoice from BIP-321 URI if present
|
||||
var inputValue = value;
|
||||
if (value.trim().toLowerCase().startsWith('bitcoin:')) {
|
||||
final parsed = IncomingDataParser.parse(value.trim());
|
||||
if (parsed.invoiceBolt11 != null) {
|
||||
inputValue = parsed.invoiceBolt11!;
|
||||
_invoiceController.text = inputValue;
|
||||
_invoiceController.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: inputValue.length),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Detectar tipo de input (sin mostrar error, solo detectar)
|
||||
final inputType = LnurlService.detectType(value);
|
||||
final inputType = LnurlService.detectType(inputValue);
|
||||
setState(() => _inputType = inputType);
|
||||
|
||||
// Solo procesar automáticamente invoices BOLT11
|
||||
// LNURL y Lightning Address requieren botón explícito
|
||||
if (inputType == LnInputType.bolt11Invoice) {
|
||||
// BOLT11 invoices son 200+ chars; evitar llamar API con input parcial
|
||||
final cleaned = LnurlService.cleanInput(value);
|
||||
final cleaned = LnurlService.cleanInput(inputValue);
|
||||
if (cleaned.length > 50) {
|
||||
_getQuote(cleaned);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import '../../core/utils/formatters.dart';
|
||||
import '../../data/transaction_meta_storage.dart';
|
||||
import '../../data/pending_token.dart';
|
||||
import '../../providers/wallet_provider.dart';
|
||||
import '../../providers/p2pk_provider.dart';
|
||||
import '../../core/utils/p2pk_utils.dart';
|
||||
import '../../widgets/common/gradient_background.dart';
|
||||
|
||||
/// Filtros disponibles para el historial
|
||||
@@ -222,9 +224,16 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
||||
Future<void> _claimPendingToken(PendingToken token) async {
|
||||
final l10n = L10n.of(context)!;
|
||||
final walletProvider = context.read<WalletProvider>();
|
||||
final p2pkProvider = context.read<P2PKProvider>();
|
||||
|
||||
try {
|
||||
final amount = await walletProvider.claimPendingToken(token.id);
|
||||
// Detectar P2PK y obtener clave privada si es necesario
|
||||
String? p2pkKey;
|
||||
if (P2PKUtils.isP2PKLocked(token.encoded)) {
|
||||
p2pkKey = p2pkProvider.getPrivateKeyForToken(token.encoded);
|
||||
}
|
||||
|
||||
final amount = await walletProvider.claimPendingToken(token.id, p2pkPrivateKey: p2pkKey);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'error.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `extract_creq_from_uri`, `parse_payment_request_inner`, `percent_decode`, `wait_for_nostr_payment_inner`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`
|
||||
|
||||
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<CreatedPaymentRequest>>
|
||||
abstract class CreatedPaymentRequest implements RustOpaqueInterface {
|
||||
String get creqA;
|
||||
|
||||
String get creqB;
|
||||
|
||||
NostrListenerHandle get listenerHandle;
|
||||
|
||||
set creqA(String creqA);
|
||||
|
||||
set creqB(String creqB);
|
||||
|
||||
set listenerHandle(NostrListenerHandle listenerHandle);
|
||||
}
|
||||
|
||||
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<NostrListenerHandle>>
|
||||
abstract class NostrListenerHandle implements RustOpaqueInterface {
|
||||
/// Reconstruct a handle from persisted data (e.g. after app restart).
|
||||
static NostrListenerHandle fromPersisted({
|
||||
required PersistedRequestData data,
|
||||
}) => RustLib.instance.api
|
||||
.crateApiPaymentRequestNostrListenerHandleFromPersisted(data: data);
|
||||
|
||||
/// Export handle data for persistence (e.g. SharedPreferences).
|
||||
/// The secret key is exposed as hex — the caller is responsible
|
||||
/// for storing it securely.
|
||||
PersistedRequestData toPersisted();
|
||||
}
|
||||
|
||||
/// Parameters for creating a payment request.
|
||||
class CreateRequestParams {
|
||||
/// Amount to request (in smallest unit, e.g. sats)
|
||||
final BigInt? amount;
|
||||
|
||||
/// Currency unit ("sat", "usd", etc.)
|
||||
final String unit;
|
||||
|
||||
/// Human-readable description
|
||||
final String? description;
|
||||
|
||||
/// Nostr relay URLs for the transport
|
||||
final List<String> nostrRelays;
|
||||
|
||||
const CreateRequestParams({
|
||||
this.amount,
|
||||
required this.unit,
|
||||
this.description,
|
||||
required this.nostrRelays,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
amount.hashCode ^
|
||||
unit.hashCode ^
|
||||
description.hashCode ^
|
||||
nostrRelays.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is CreateRequestParams &&
|
||||
runtimeType == other.runtimeType &&
|
||||
amount == other.amount &&
|
||||
unit == other.unit &&
|
||||
description == other.description &&
|
||||
nostrRelays == other.nostrRelays;
|
||||
}
|
||||
|
||||
/// Event emitted by the Nostr payment listener.
|
||||
class NostrPaymentEvent {
|
||||
final NostrPaymentState state;
|
||||
final BigInt? amount;
|
||||
final String? error;
|
||||
|
||||
const NostrPaymentEvent({required this.state, this.amount, this.error});
|
||||
|
||||
@override
|
||||
int get hashCode => state.hashCode ^ amount.hashCode ^ error.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is NostrPaymentEvent &&
|
||||
runtimeType == other.runtimeType &&
|
||||
state == other.state &&
|
||||
amount == other.amount &&
|
||||
error == other.error;
|
||||
}
|
||||
|
||||
/// State of a Nostr payment listener.
|
||||
enum NostrPaymentState {
|
||||
/// Connected to relays, waiting for payment
|
||||
waiting,
|
||||
|
||||
/// Payment received and tokens claimed
|
||||
received,
|
||||
|
||||
/// Error occurred
|
||||
error,
|
||||
}
|
||||
|
||||
/// Parsed payment request info exposed to Flutter.
|
||||
/// CDK's FromStr auto-detects creqA (CBOR) vs creqB (Bech32m).
|
||||
class PaymentRequestInfo {
|
||||
/// Raw encoded string (creqA... or CREQB1...)
|
||||
final String raw;
|
||||
|
||||
/// Payment id
|
||||
final String? paymentId;
|
||||
|
||||
/// Requested amount (in base unit)
|
||||
final BigInt? amount;
|
||||
|
||||
/// Currency unit ("sat", "usd", etc.)
|
||||
final String? unit;
|
||||
|
||||
/// Whether this is a single-use request
|
||||
final bool? singleUse;
|
||||
|
||||
/// Accepted mints (empty = any mint)
|
||||
final List<String> mints;
|
||||
|
||||
/// Human-readable description
|
||||
final String? description;
|
||||
|
||||
/// Available transports
|
||||
final List<TransportInfo> transports;
|
||||
|
||||
/// Whether NUT-10 spending conditions are required
|
||||
final bool hasNut10;
|
||||
|
||||
const PaymentRequestInfo({
|
||||
required this.raw,
|
||||
this.paymentId,
|
||||
this.amount,
|
||||
this.unit,
|
||||
this.singleUse,
|
||||
required this.mints,
|
||||
this.description,
|
||||
required this.transports,
|
||||
required this.hasNut10,
|
||||
});
|
||||
|
||||
/// Parse a payment request string (creqA, CREQB1, or bitcoin:?creq=).
|
||||
/// Supports NUT-18 (CBOR+base64) and NUT-26 (Bech32m) automatically.
|
||||
static PaymentRequestInfo parse({required String encoded}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiPaymentRequestPaymentRequestInfoParse(encoded: encoded);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
raw.hashCode ^
|
||||
paymentId.hashCode ^
|
||||
amount.hashCode ^
|
||||
unit.hashCode ^
|
||||
singleUse.hashCode ^
|
||||
mints.hashCode ^
|
||||
description.hashCode ^
|
||||
transports.hashCode ^
|
||||
hasNut10.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is PaymentRequestInfo &&
|
||||
runtimeType == other.runtimeType &&
|
||||
raw == other.raw &&
|
||||
paymentId == other.paymentId &&
|
||||
amount == other.amount &&
|
||||
unit == other.unit &&
|
||||
singleUse == other.singleUse &&
|
||||
mints == other.mints &&
|
||||
description == other.description &&
|
||||
transports == other.transports &&
|
||||
hasNut10 == other.hasNut10;
|
||||
}
|
||||
|
||||
/// Serializable data for persisting a NostrListenerHandle across app restarts.
|
||||
class PersistedRequestData {
|
||||
final String secretHex;
|
||||
final String pubkeyHex;
|
||||
final List<String> relays;
|
||||
final BigInt? amount;
|
||||
final String unit;
|
||||
final String mintUrl;
|
||||
|
||||
const PersistedRequestData({
|
||||
required this.secretHex,
|
||||
required this.pubkeyHex,
|
||||
required this.relays,
|
||||
this.amount,
|
||||
required this.unit,
|
||||
required this.mintUrl,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
secretHex.hashCode ^
|
||||
pubkeyHex.hashCode ^
|
||||
relays.hashCode ^
|
||||
amount.hashCode ^
|
||||
unit.hashCode ^
|
||||
mintUrl.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is PersistedRequestData &&
|
||||
runtimeType == other.runtimeType &&
|
||||
secretHex == other.secretHex &&
|
||||
pubkeyHex == other.pubkeyHex &&
|
||||
relays == other.relays &&
|
||||
amount == other.amount &&
|
||||
unit == other.unit &&
|
||||
mintUrl == other.mintUrl;
|
||||
}
|
||||
|
||||
/// Transport info exposed to Flutter
|
||||
class TransportInfo {
|
||||
/// "nostr" or "post"
|
||||
final String transportType;
|
||||
|
||||
/// Target (nprofile or URL)
|
||||
final String target;
|
||||
|
||||
const TransportInfo({required this.transportType, required this.target});
|
||||
|
||||
@override
|
||||
int get hashCode => transportType.hashCode ^ target.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is TransportInfo &&
|
||||
runtimeType == other.runtimeType &&
|
||||
transportType == other.transportType &&
|
||||
target == other.target;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import '../frb_generated.dart';
|
||||
import 'error.dart';
|
||||
import 'mint_info.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'payment_request.dart';
|
||||
import 'token.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `mint_url`, `unit`, `update_balance_streams`
|
||||
@@ -49,6 +50,15 @@ abstract class Wallet implements RustOpaqueInterface {
|
||||
|
||||
Future<void> checkPendingTransactions();
|
||||
|
||||
/// Create a NUT-18 payment request with Nostr transport.
|
||||
///
|
||||
/// Builds a PaymentRequest with the wallet's mint URL and unit,
|
||||
/// generates ephemeral Nostr keys, and returns both creqA and creqB
|
||||
/// encodings plus the keys needed for the Nostr listener.
|
||||
Future<CreatedPaymentRequest> createPaymentRequest({
|
||||
required CreateRequestParams params,
|
||||
});
|
||||
|
||||
Future<void> finalizePendingMelts();
|
||||
|
||||
Future<MintInfo?> getMint();
|
||||
@@ -77,6 +87,16 @@ abstract class Wallet implements RustOpaqueInterface {
|
||||
db: db,
|
||||
);
|
||||
|
||||
/// Pay a NUT-18 payment request.
|
||||
/// Uses CDK's pay_request() which handles:
|
||||
/// - NUT-10 spending conditions
|
||||
/// - Transport selection (Nostr preferred, HTTP POST fallback)
|
||||
/// - Token preparation and delivery
|
||||
Future<void> payPaymentRequest({
|
||||
required String encoded,
|
||||
BigInt? customAmount,
|
||||
});
|
||||
|
||||
Future<PreparedSend> prepareSend({required BigInt amount, SendOptions? opts});
|
||||
|
||||
Future<BigInt> receive({required Token token, ReceiveOptions? opts});
|
||||
@@ -92,6 +112,14 @@ abstract class Wallet implements RustOpaqueInterface {
|
||||
});
|
||||
|
||||
Stream<BigInt> streamBalance();
|
||||
|
||||
/// Wait for an incoming Nostr payment (NIP-17 gift-wrap).
|
||||
///
|
||||
/// Takes the opaque NostrListenerHandle returned by create_payment_request().
|
||||
/// The secret key never leaves Rust memory.
|
||||
Stream<NostrPaymentEvent> waitForNostrPayment({
|
||||
required NostrListenerHandle handle,
|
||||
});
|
||||
}
|
||||
|
||||
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<WalletDatabase>>
|
||||
|
||||
+1301
-50
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@
|
||||
import 'api/error.dart';
|
||||
import 'api/keys.dart';
|
||||
import 'api/mint_info.dart';
|
||||
import 'api/payment_request.dart';
|
||||
import 'api/token.dart';
|
||||
import 'api/wallet.dart';
|
||||
import 'dart:async';
|
||||
@@ -22,6 +23,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
required super.portManager,
|
||||
});
|
||||
|
||||
CrossPlatformFinalizerArg
|
||||
get rust_arc_decrement_strong_count_CreatedPaymentRequestPtr => wire
|
||||
._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequestPtr;
|
||||
|
||||
CrossPlatformFinalizerArg
|
||||
get rust_arc_decrement_strong_count_NostrListenerHandlePtr => wire
|
||||
._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandlePtr;
|
||||
|
||||
CrossPlatformFinalizerArg
|
||||
get rust_arc_decrement_strong_count_PreparedSendPtr => wire
|
||||
._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSendPtr;
|
||||
@@ -41,6 +50,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
AnyhowException dco_decode_AnyhowException(dynamic raw);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -65,6 +86,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -83,6 +110,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -110,6 +149,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Map<String, String> dco_decode_Map_String_String_None(dynamic raw);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -137,6 +188,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
RustStreamSink<MintQuote> dco_decode_StreamSink_mint_quote_Sse(dynamic raw);
|
||||
|
||||
@protected
|
||||
RustStreamSink<NostrPaymentEvent>
|
||||
dco_decode_StreamSink_nostr_payment_event_Sse(dynamic raw);
|
||||
|
||||
@protected
|
||||
RustStreamSink<BigInt> dco_decode_StreamSink_u_64_Sse(dynamic raw);
|
||||
|
||||
@@ -149,6 +204,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
bool dco_decode_box_autoadd_bool(dynamic raw);
|
||||
|
||||
@protected
|
||||
CreateRequestParams dco_decode_box_autoadd_create_request_params(dynamic raw);
|
||||
|
||||
@protected
|
||||
MeltQuote dco_decode_box_autoadd_melt_quote(dynamic raw);
|
||||
|
||||
@@ -158,6 +216,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
MintVersion dco_decode_box_autoadd_mint_version(dynamic raw);
|
||||
|
||||
@protected
|
||||
PersistedRequestData dco_decode_box_autoadd_persisted_request_data(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
ReceiveOptions dco_decode_box_autoadd_receive_options(dynamic raw);
|
||||
|
||||
@@ -181,6 +244,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
ContactInfo dco_decode_contact_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
CreateRequestParams dco_decode_create_request_params(dynamic raw);
|
||||
|
||||
@protected
|
||||
Error dco_decode_error(dynamic raw);
|
||||
|
||||
@@ -217,6 +283,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
List<Transaction> dco_decode_list_transaction(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<TransportInfo> dco_decode_list_transport_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
MeltMethodSettings dco_decode_melt_method_settings(dynamic raw);
|
||||
|
||||
@@ -238,6 +307,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
MintVersion dco_decode_mint_version(dynamic raw);
|
||||
|
||||
@protected
|
||||
NostrPaymentEvent dco_decode_nostr_payment_event(dynamic raw);
|
||||
|
||||
@protected
|
||||
NostrPaymentState dco_decode_nostr_payment_state(dynamic raw);
|
||||
|
||||
@protected
|
||||
Nut04Settings dco_decode_nut_04_settings(dynamic raw);
|
||||
|
||||
@@ -288,6 +363,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw);
|
||||
|
||||
@protected
|
||||
PaymentRequestInfo dco_decode_payment_request_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
PersistedRequestData dco_decode_persisted_request_data(dynamic raw);
|
||||
|
||||
@protected
|
||||
ReceiveOptions dco_decode_receive_options(dynamic raw);
|
||||
|
||||
@@ -312,6 +393,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
TransactionStatus dco_decode_transaction_status(dynamic raw);
|
||||
|
||||
@protected
|
||||
TransportInfo dco_decode_transport_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
BigInt dco_decode_u_64(dynamic raw);
|
||||
|
||||
@@ -327,6 +411,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -351,6 +447,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -369,6 +471,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -398,6 +512,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -427,6 +553,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
RustStreamSink<NostrPaymentEvent>
|
||||
sse_decode_StreamSink_nostr_payment_event_Sse(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
RustStreamSink<BigInt> sse_decode_StreamSink_u_64_Sse(
|
||||
SseDeserializer deserializer,
|
||||
@@ -441,6 +571,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
bool sse_decode_box_autoadd_bool(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
CreateRequestParams sse_decode_box_autoadd_create_request_params(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
MeltQuote sse_decode_box_autoadd_melt_quote(SseDeserializer deserializer);
|
||||
|
||||
@@ -450,6 +585,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
MintVersion sse_decode_box_autoadd_mint_version(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
PersistedRequestData sse_decode_box_autoadd_persisted_request_data(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
ReceiveOptions sse_decode_box_autoadd_receive_options(
|
||||
SseDeserializer deserializer,
|
||||
@@ -475,6 +615,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
ContactInfo sse_decode_contact_info(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
CreateRequestParams sse_decode_create_request_params(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
Error sse_decode_error(SseDeserializer deserializer);
|
||||
|
||||
@@ -517,6 +662,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
List<Transaction> sse_decode_list_transaction(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<TransportInfo> sse_decode_list_transport_info(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
MeltMethodSettings sse_decode_melt_method_settings(
|
||||
SseDeserializer deserializer,
|
||||
@@ -542,6 +692,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
MintVersion sse_decode_mint_version(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
NostrPaymentEvent sse_decode_nostr_payment_event(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrPaymentState sse_decode_nostr_payment_state(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
Nut04Settings sse_decode_nut_04_settings(SseDeserializer deserializer);
|
||||
|
||||
@@ -600,6 +760,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
PaymentRequestInfo sse_decode_payment_request_info(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
PersistedRequestData sse_decode_persisted_request_data(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
ReceiveOptions sse_decode_receive_options(SseDeserializer deserializer);
|
||||
|
||||
@@ -628,6 +798,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
TransactionStatus sse_decode_transaction_status(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
TransportInfo sse_decode_transport_info(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
BigInt sse_decode_u_64(SseDeserializer deserializer);
|
||||
|
||||
@@ -646,6 +819,20 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
CreatedPaymentRequest self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
NostrListenerHandle self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -674,6 +861,13 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
CreatedPaymentRequest self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -695,6 +889,20 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
CreatedPaymentRequest self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
NostrListenerHandle self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -729,6 +937,20 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
CreatedPaymentRequest self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
NostrListenerHandle self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -763,6 +985,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_StreamSink_nostr_payment_event_Sse(
|
||||
RustStreamSink<NostrPaymentEvent> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_StreamSink_u_64_Sse(
|
||||
RustStreamSink<BigInt> self,
|
||||
@@ -778,6 +1006,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_box_autoadd_bool(bool self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_create_request_params(
|
||||
CreateRequestParams self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_melt_quote(
|
||||
MeltQuote self,
|
||||
@@ -796,6 +1030,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_persisted_request_data(
|
||||
PersistedRequestData self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_receive_options(
|
||||
ReceiveOptions self,
|
||||
@@ -826,6 +1066,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_contact_info(ContactInfo self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_create_request_params(
|
||||
CreateRequestParams self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_error(Error self, SseSerializer serializer);
|
||||
|
||||
@@ -883,6 +1129,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_transport_info(
|
||||
List<TransportInfo> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_melt_method_settings(
|
||||
MeltMethodSettings self,
|
||||
@@ -913,6 +1165,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_mint_version(MintVersion self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_nostr_payment_event(
|
||||
NostrPaymentEvent self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_nostr_payment_state(
|
||||
NostrPaymentState self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_nut_04_settings(Nut04Settings self, SseSerializer serializer);
|
||||
|
||||
@@ -982,6 +1246,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_payment_request_info(
|
||||
PaymentRequestInfo self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_persisted_request_data(
|
||||
PersistedRequestData self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_receive_options(
|
||||
ReceiveOptions self,
|
||||
@@ -1021,6 +1297,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_transport_info(TransportInfo self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_u_64(BigInt self, SseSerializer serializer);
|
||||
|
||||
@@ -1048,6 +1327,74 @@ class RustLibWire implements BaseWire {
|
||||
RustLibWire(ffi.DynamicLibrary dynamicLibrary)
|
||||
: _lookup = dynamicLibrary.lookup;
|
||||
|
||||
void
|
||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
ffi.Pointer<ffi.Void> ptr,
|
||||
) {
|
||||
return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
ptr,
|
||||
);
|
||||
}
|
||||
|
||||
late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequestPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
|
||||
'frbgen_elcaju_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest',
|
||||
);
|
||||
late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest =
|
||||
_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequestPtr
|
||||
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
|
||||
|
||||
void
|
||||
rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
ffi.Pointer<ffi.Void> ptr,
|
||||
) {
|
||||
return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
ptr,
|
||||
);
|
||||
}
|
||||
|
||||
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequestPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
|
||||
'frbgen_elcaju_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest',
|
||||
);
|
||||
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest =
|
||||
_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequestPtr
|
||||
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
|
||||
|
||||
void
|
||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
ffi.Pointer<ffi.Void> ptr,
|
||||
) {
|
||||
return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
ptr,
|
||||
);
|
||||
}
|
||||
|
||||
late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandlePtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
|
||||
'frbgen_elcaju_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle',
|
||||
);
|
||||
late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle =
|
||||
_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandlePtr
|
||||
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
|
||||
|
||||
void
|
||||
rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
ffi.Pointer<ffi.Void> ptr,
|
||||
) {
|
||||
return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
ptr,
|
||||
);
|
||||
}
|
||||
|
||||
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandlePtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
|
||||
'frbgen_elcaju_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle',
|
||||
);
|
||||
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle =
|
||||
_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandlePtr
|
||||
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
|
||||
|
||||
void
|
||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
ffi.Pointer<ffi.Void> ptr,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import 'api/error.dart';
|
||||
import 'api/keys.dart';
|
||||
import 'api/mint_info.dart';
|
||||
import 'api/payment_request.dart';
|
||||
import 'api/token.dart';
|
||||
import 'api/wallet.dart';
|
||||
import 'dart:async';
|
||||
@@ -24,6 +25,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
required super.portManager,
|
||||
});
|
||||
|
||||
CrossPlatformFinalizerArg
|
||||
get rust_arc_decrement_strong_count_CreatedPaymentRequestPtr => wire
|
||||
.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest;
|
||||
|
||||
CrossPlatformFinalizerArg
|
||||
get rust_arc_decrement_strong_count_NostrListenerHandlePtr => wire
|
||||
.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle;
|
||||
|
||||
CrossPlatformFinalizerArg
|
||||
get rust_arc_decrement_strong_count_PreparedSendPtr => wire
|
||||
.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend;
|
||||
@@ -43,6 +52,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
AnyhowException dco_decode_AnyhowException(dynamic raw);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -67,6 +88,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -85,6 +112,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -112,6 +151,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Map<String, String> dco_decode_Map_String_String_None(dynamic raw);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -139,6 +190,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
RustStreamSink<MintQuote> dco_decode_StreamSink_mint_quote_Sse(dynamic raw);
|
||||
|
||||
@protected
|
||||
RustStreamSink<NostrPaymentEvent>
|
||||
dco_decode_StreamSink_nostr_payment_event_Sse(dynamic raw);
|
||||
|
||||
@protected
|
||||
RustStreamSink<BigInt> dco_decode_StreamSink_u_64_Sse(dynamic raw);
|
||||
|
||||
@@ -151,6 +206,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
bool dco_decode_box_autoadd_bool(dynamic raw);
|
||||
|
||||
@protected
|
||||
CreateRequestParams dco_decode_box_autoadd_create_request_params(dynamic raw);
|
||||
|
||||
@protected
|
||||
MeltQuote dco_decode_box_autoadd_melt_quote(dynamic raw);
|
||||
|
||||
@@ -160,6 +218,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
MintVersion dco_decode_box_autoadd_mint_version(dynamic raw);
|
||||
|
||||
@protected
|
||||
PersistedRequestData dco_decode_box_autoadd_persisted_request_data(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
ReceiveOptions dco_decode_box_autoadd_receive_options(dynamic raw);
|
||||
|
||||
@@ -183,6 +246,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
ContactInfo dco_decode_contact_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
CreateRequestParams dco_decode_create_request_params(dynamic raw);
|
||||
|
||||
@protected
|
||||
Error dco_decode_error(dynamic raw);
|
||||
|
||||
@@ -219,6 +285,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
List<Transaction> dco_decode_list_transaction(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<TransportInfo> dco_decode_list_transport_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
MeltMethodSettings dco_decode_melt_method_settings(dynamic raw);
|
||||
|
||||
@@ -240,6 +309,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
MintVersion dco_decode_mint_version(dynamic raw);
|
||||
|
||||
@protected
|
||||
NostrPaymentEvent dco_decode_nostr_payment_event(dynamic raw);
|
||||
|
||||
@protected
|
||||
NostrPaymentState dco_decode_nostr_payment_state(dynamic raw);
|
||||
|
||||
@protected
|
||||
Nut04Settings dco_decode_nut_04_settings(dynamic raw);
|
||||
|
||||
@@ -290,6 +365,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw);
|
||||
|
||||
@protected
|
||||
PaymentRequestInfo dco_decode_payment_request_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
PersistedRequestData dco_decode_persisted_request_data(dynamic raw);
|
||||
|
||||
@protected
|
||||
ReceiveOptions dco_decode_receive_options(dynamic raw);
|
||||
|
||||
@@ -314,6 +395,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
TransactionStatus dco_decode_transaction_status(dynamic raw);
|
||||
|
||||
@protected
|
||||
TransportInfo dco_decode_transport_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
BigInt dco_decode_u_64(dynamic raw);
|
||||
|
||||
@@ -329,6 +413,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -353,6 +449,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -371,6 +473,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -400,6 +514,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
CreatedPaymentRequest
|
||||
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrListenerHandle
|
||||
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
PreparedSend
|
||||
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -429,6 +555,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
RustStreamSink<NostrPaymentEvent>
|
||||
sse_decode_StreamSink_nostr_payment_event_Sse(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
RustStreamSink<BigInt> sse_decode_StreamSink_u_64_Sse(
|
||||
SseDeserializer deserializer,
|
||||
@@ -443,6 +573,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
bool sse_decode_box_autoadd_bool(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
CreateRequestParams sse_decode_box_autoadd_create_request_params(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
MeltQuote sse_decode_box_autoadd_melt_quote(SseDeserializer deserializer);
|
||||
|
||||
@@ -452,6 +587,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
MintVersion sse_decode_box_autoadd_mint_version(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
PersistedRequestData sse_decode_box_autoadd_persisted_request_data(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
ReceiveOptions sse_decode_box_autoadd_receive_options(
|
||||
SseDeserializer deserializer,
|
||||
@@ -477,6 +617,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
ContactInfo sse_decode_contact_info(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
CreateRequestParams sse_decode_create_request_params(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
Error sse_decode_error(SseDeserializer deserializer);
|
||||
|
||||
@@ -519,6 +664,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
List<Transaction> sse_decode_list_transaction(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<TransportInfo> sse_decode_list_transport_info(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
MeltMethodSettings sse_decode_melt_method_settings(
|
||||
SseDeserializer deserializer,
|
||||
@@ -544,6 +694,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
MintVersion sse_decode_mint_version(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
NostrPaymentEvent sse_decode_nostr_payment_event(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
NostrPaymentState sse_decode_nostr_payment_state(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
Nut04Settings sse_decode_nut_04_settings(SseDeserializer deserializer);
|
||||
|
||||
@@ -602,6 +762,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
PaymentRequestInfo sse_decode_payment_request_info(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
PersistedRequestData sse_decode_persisted_request_data(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
ReceiveOptions sse_decode_receive_options(SseDeserializer deserializer);
|
||||
|
||||
@@ -630,6 +800,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
TransactionStatus sse_decode_transaction_status(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
TransportInfo sse_decode_transport_info(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
BigInt sse_decode_u_64(SseDeserializer deserializer);
|
||||
|
||||
@@ -648,6 +821,20 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
CreatedPaymentRequest self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
NostrListenerHandle self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -676,6 +863,13 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
CreatedPaymentRequest self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -697,6 +891,20 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
CreatedPaymentRequest self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
NostrListenerHandle self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -731,6 +939,20 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
CreatedPaymentRequest self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
NostrListenerHandle self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
@@ -765,6 +987,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_StreamSink_nostr_payment_event_Sse(
|
||||
RustStreamSink<NostrPaymentEvent> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_StreamSink_u_64_Sse(
|
||||
RustStreamSink<BigInt> self,
|
||||
@@ -780,6 +1008,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_box_autoadd_bool(bool self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_create_request_params(
|
||||
CreateRequestParams self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_melt_quote(
|
||||
MeltQuote self,
|
||||
@@ -798,6 +1032,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_persisted_request_data(
|
||||
PersistedRequestData self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_receive_options(
|
||||
ReceiveOptions self,
|
||||
@@ -828,6 +1068,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_contact_info(ContactInfo self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_create_request_params(
|
||||
CreateRequestParams self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_error(Error self, SseSerializer serializer);
|
||||
|
||||
@@ -885,6 +1131,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_transport_info(
|
||||
List<TransportInfo> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_melt_method_settings(
|
||||
MeltMethodSettings self,
|
||||
@@ -915,6 +1167,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_mint_version(MintVersion self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_nostr_payment_event(
|
||||
NostrPaymentEvent self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_nostr_payment_state(
|
||||
NostrPaymentState self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_nut_04_settings(Nut04Settings self, SseSerializer serializer);
|
||||
|
||||
@@ -984,6 +1248,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_payment_request_info(
|
||||
PaymentRequestInfo self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_persisted_request_data(
|
||||
PersistedRequestData self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_receive_options(
|
||||
ReceiveOptions self,
|
||||
@@ -1023,6 +1299,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_transport_info(TransportInfo self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_u_64(BigInt self, SseSerializer serializer);
|
||||
|
||||
@@ -1041,6 +1320,38 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
class RustLibWire implements BaseWire {
|
||||
RustLibWire.fromExternalLibrary(ExternalLibrary lib);
|
||||
|
||||
void
|
||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
int ptr,
|
||||
) => wasmModule
|
||||
.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
ptr,
|
||||
);
|
||||
|
||||
void
|
||||
rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
int ptr,
|
||||
) => wasmModule
|
||||
.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
ptr,
|
||||
);
|
||||
|
||||
void
|
||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
int ptr,
|
||||
) => wasmModule
|
||||
.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
ptr,
|
||||
);
|
||||
|
||||
void
|
||||
rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
int ptr,
|
||||
) => wasmModule
|
||||
.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
ptr,
|
||||
);
|
||||
|
||||
void
|
||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
int ptr,
|
||||
@@ -1112,6 +1423,26 @@ external RustLibWasmModule get wasmModule;
|
||||
@JS()
|
||||
@anonymous
|
||||
extension type RustLibWasmModule._(JSObject _) implements JSObject {
|
||||
external void
|
||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
int ptr,
|
||||
);
|
||||
|
||||
external void
|
||||
rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerCreatedPaymentRequest(
|
||||
int ptr,
|
||||
);
|
||||
|
||||
external void
|
||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
int ptr,
|
||||
);
|
||||
|
||||
external void
|
||||
rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerNostrListenerHandle(
|
||||
int ptr,
|
||||
);
|
||||
|
||||
external void
|
||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerPreparedSend(
|
||||
int ptr,
|
||||
|
||||
Generated
+354
-2
@@ -17,6 +17,27 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.12"
|
||||
@@ -184,12 +205,49 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-utility"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a34a3b57207a7a1007832416c3e4862378c8451b4e8e093e436f48c2d3d2c151"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"gloo-timers",
|
||||
"tokio",
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-wsocket"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c92385c7c8b3eb2de1b78aeca225212e4c9a69a78b802832759b108681a5069"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-socks",
|
||||
"tokio-tungstenite",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
|
||||
|
||||
[[package]]
|
||||
name = "atomic-destructor"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4"
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
@@ -239,6 +297,12 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "bc-ur"
|
||||
version = "0.12.0"
|
||||
@@ -354,6 +418,15 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "8.0.2"
|
||||
@@ -425,6 +498,7 @@ dependencies = [
|
||||
"ciborium",
|
||||
"lightning",
|
||||
"lightning-invoice",
|
||||
"nostr-sdk",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -439,6 +513,15 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbc"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbor-diag"
|
||||
version = "0.1.12"
|
||||
@@ -490,6 +573,7 @@ dependencies = [
|
||||
"jsonwebtoken",
|
||||
"lightning",
|
||||
"lightning-invoice",
|
||||
"nostr-sdk",
|
||||
"regex",
|
||||
"ring",
|
||||
"rustls",
|
||||
@@ -628,6 +712,30 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chacha20poly1305"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"chacha20",
|
||||
"cipher",
|
||||
"poly1305",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
@@ -669,6 +777,17 @@ dependencies = [
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.0"
|
||||
@@ -807,6 +926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core 0.6.4",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
@@ -915,6 +1035,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -940,6 +1061,12 @@ version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "elcaju_core"
|
||||
version = "0.1.0"
|
||||
@@ -953,6 +1080,7 @@ dependencies = [
|
||||
"flutter_rust_bridge",
|
||||
"getrandom 0.3.4",
|
||||
"log",
|
||||
"nostr-sdk",
|
||||
"oslog",
|
||||
"reqwest",
|
||||
"serde_json",
|
||||
@@ -1313,6 +1441,15 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.12"
|
||||
@@ -1397,7 +1534,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki-roots",
|
||||
"webpki-roots 1.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1584,6 +1721,28 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.12.0"
|
||||
@@ -1754,6 +1913,12 @@ version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
@@ -1832,6 +1997,12 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "negentropy"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0efe882e02d206d8d279c20eb40e03baf7cb5136a1476dc084a324fbc3ec42d"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
@@ -1842,6 +2013,84 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nostr"
|
||||
version = "0.44.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3aa5e3b6a278ed061835fe1ee293b71641e6bf8b401cfe4e1834bbf4ef0a34e1"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"base64 0.22.1",
|
||||
"bech32",
|
||||
"bip39",
|
||||
"bitcoin_hashes 0.14.1",
|
||||
"cbc",
|
||||
"chacha20",
|
||||
"chacha20poly1305",
|
||||
"getrandom 0.2.17",
|
||||
"hex",
|
||||
"instant",
|
||||
"scrypt",
|
||||
"secp256k1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"unicode-normalization",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nostr-database"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"nostr",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nostr-gossip"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade30de16869618919c6b5efc8258f47b654a98b51541eb77f85e8ec5e3c83a6"
|
||||
dependencies = [
|
||||
"nostr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nostr-relay-pool"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b1073ccfbaea5549fb914a9d52c68dab2aecda61535e5143dd73e95445a804b"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"async-wsocket",
|
||||
"atomic-destructor",
|
||||
"hex",
|
||||
"lru",
|
||||
"negentropy",
|
||||
"nostr",
|
||||
"nostr-database",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nostr-sdk"
|
||||
version = "0.44.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "471732576710e779b64f04c55e3f8b5292f865fea228436daf19694f0bf70393"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"nostr",
|
||||
"nostr-database",
|
||||
"nostr-gossip",
|
||||
"nostr-relay-pool",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
@@ -1927,6 +2176,12 @@ version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.2.1"
|
||||
@@ -1967,12 +2222,33 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "3.0.6"
|
||||
@@ -2069,6 +2345,17 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "poly1305"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.13.1"
|
||||
@@ -2365,7 +2652,7 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"webpki-roots 1.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2467,6 +2754,15 @@ version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "salsa20"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.29"
|
||||
@@ -2506,6 +2802,18 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "scrypt"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
|
||||
dependencies = [
|
||||
"password-hash",
|
||||
"pbkdf2",
|
||||
"salsa20",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1"
|
||||
version = "0.29.1"
|
||||
@@ -2659,6 +2967,17 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
@@ -2960,6 +3279,18 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-socks"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"futures-util",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.18"
|
||||
@@ -2985,6 +3316,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tungstenite",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3184,6 +3516,16 @@ version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
@@ -3213,6 +3555,7 @@ dependencies = [
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3405,6 +3748,15 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
||||
dependencies = [
|
||||
"webpki-roots 1.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.6"
|
||||
|
||||
+5
-2
@@ -8,8 +8,8 @@ crate-type = ["cdylib", "staticlib"]
|
||||
|
||||
[dependencies]
|
||||
# Cashu — acceso directo, sin intermediarios
|
||||
cdk = { version = "0.15.1", default-features = false, features = ["wallet"] }
|
||||
cdk-common = { version = "0.15.1", default-features = false }
|
||||
cdk = { version = "0.15.1", default-features = false, features = ["wallet", "nostr"] }
|
||||
cdk-common = { version = "0.15.1", default-features = false, features = ["nostr"] }
|
||||
cdk-sqlite = { version = "0.15.1", default-features = false, features = ["wallet"] }
|
||||
|
||||
# Bridge Flutter <-> Rust
|
||||
@@ -31,6 +31,9 @@ bip39 = { version = "2.1", default-features = false, features = ["std"] }
|
||||
# Entropy
|
||||
getrandom = { version = "0.3", default-features = false, features = ["std"] }
|
||||
|
||||
# Nostr keys (same version as CDK 0.15.1 — CDK does not re-export Keys)
|
||||
nostr-sdk = { version = "0.44.1", default-features = false, features = ["nip04", "nip44", "nip59"] }
|
||||
|
||||
# Async runtime
|
||||
tokio = { version = "1", default-features = false }
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod error;
|
||||
pub mod keys;
|
||||
pub mod mint_info;
|
||||
pub mod payment_request;
|
||||
pub mod token;
|
||||
pub mod wallet;
|
||||
|
||||
@@ -0,0 +1,591 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use cdk::{
|
||||
amount::Amount,
|
||||
mint_url::MintUrl,
|
||||
nuts::{
|
||||
CurrencyUnit, PaymentRequest as CdkPaymentRequest, Transport, TransportType,
|
||||
},
|
||||
wallet::ReceiveOptions as CdkReceiveOptions,
|
||||
};
|
||||
use flutter_rust_bridge::frb;
|
||||
use nostr_sdk::{
|
||||
nips::nip19::{Nip19Profile, ToBech32},
|
||||
Keys as NostrKeys, PublicKey, RelayUrl,
|
||||
};
|
||||
|
||||
use crate::frb_generated::StreamSink;
|
||||
|
||||
use super::{error::Error, wallet::Wallet};
|
||||
|
||||
// ========================================================================
|
||||
// Payment Request parsing (NUT-18 creqA + NUT-26 creqB)
|
||||
// ========================================================================
|
||||
|
||||
/// Parsed payment request info exposed to Flutter.
|
||||
/// CDK's FromStr auto-detects creqA (CBOR) vs creqB (Bech32m).
|
||||
pub struct PaymentRequestInfo {
|
||||
/// Raw encoded string (creqA... or CREQB1...)
|
||||
pub raw: String,
|
||||
/// Payment id
|
||||
pub payment_id: Option<String>,
|
||||
/// Requested amount (in base unit)
|
||||
pub amount: Option<u64>,
|
||||
/// Currency unit ("sat", "usd", etc.)
|
||||
pub unit: Option<String>,
|
||||
/// Whether this is a single-use request
|
||||
pub single_use: Option<bool>,
|
||||
/// Accepted mints (empty = any mint)
|
||||
pub mints: Vec<String>,
|
||||
/// Human-readable description
|
||||
pub description: Option<String>,
|
||||
/// Available transports
|
||||
pub transports: Vec<TransportInfo>,
|
||||
/// Whether NUT-10 spending conditions are required
|
||||
pub has_nut10: bool,
|
||||
}
|
||||
|
||||
/// Transport info exposed to Flutter
|
||||
pub struct TransportInfo {
|
||||
/// "nostr" or "post"
|
||||
pub transport_type: String,
|
||||
/// Target (nprofile or URL)
|
||||
pub target: String,
|
||||
}
|
||||
|
||||
impl PaymentRequestInfo {
|
||||
/// Parse a payment request string (creqA, CREQB1, or bitcoin:?creq=).
|
||||
/// Supports NUT-18 (CBOR+base64) and NUT-26 (Bech32m) automatically.
|
||||
#[frb(sync)]
|
||||
pub fn parse(encoded: String) -> Result<PaymentRequestInfo, Error> {
|
||||
parse_payment_request_inner(encoded)
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner parsing logic, outside of frb macro scope to avoid iterator issues.
|
||||
fn parse_payment_request_inner(encoded: String) -> Result<PaymentRequestInfo, Error> {
|
||||
let creq_str = extract_creq_from_uri(&encoded).unwrap_or(encoded);
|
||||
|
||||
let pr = CdkPaymentRequest::from_str(&creq_str)
|
||||
.map_err(|e| Error::Cdk(format!("Invalid payment request: {e}")))?;
|
||||
|
||||
let mints: Vec<String> = match &pr.mints {
|
||||
Some(list) => {
|
||||
let mut v = Vec::new();
|
||||
for m in list {
|
||||
v.push(format!("{}", m));
|
||||
}
|
||||
v
|
||||
}
|
||||
None => Vec::new(),
|
||||
};
|
||||
let mut transports = Vec::new();
|
||||
for t in &pr.transports {
|
||||
transports.push(TransportInfo {
|
||||
transport_type: match t._type {
|
||||
TransportType::Nostr => "nostr".into(),
|
||||
TransportType::HttpPost => "post".into(),
|
||||
},
|
||||
target: t.target.clone(),
|
||||
});
|
||||
}
|
||||
let has_nut10 = pr.nut10.is_some();
|
||||
|
||||
Ok(PaymentRequestInfo {
|
||||
raw: creq_str,
|
||||
payment_id: pr.payment_id,
|
||||
amount: pr.amount.map(|a| a.into()),
|
||||
unit: pr.unit.map(|u| format!("{}", u)),
|
||||
single_use: pr.single_use,
|
||||
mints,
|
||||
description: pr.description,
|
||||
transports,
|
||||
has_nut10,
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract creq parameter from a BIP-321 bitcoin: URI.
|
||||
/// e.g. "bitcoin:?creq=CREQB1...&lightning=lnbc..." → "CREQB1..."
|
||||
/// Handles percent-encoding and case-insensitive key matching.
|
||||
fn extract_creq_from_uri(input: &str) -> Option<String> {
|
||||
if !input
|
||||
.get(..8)
|
||||
.is_some_and(|scheme| scheme.eq_ignore_ascii_case("bitcoin:"))
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let query = input.split_once('?')?.1;
|
||||
for param in query.split('&') {
|
||||
let (key, value) = param.split_once('=')?;
|
||||
if key.eq_ignore_ascii_case("creq") {
|
||||
return Some(percent_decode(value));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Simple percent-decoding for URI query values.
|
||||
fn percent_decode(value: &str) -> String {
|
||||
let bytes = value.as_bytes();
|
||||
let mut out = Vec::with_capacity(bytes.len());
|
||||
let mut i = 0;
|
||||
while i < bytes.len() {
|
||||
if bytes[i] == b'%' && i + 2 < bytes.len() {
|
||||
if let Ok(byte) = u8::from_str_radix(
|
||||
std::str::from_utf8(&bytes[i + 1..i + 3]).unwrap_or(""),
|
||||
16,
|
||||
) {
|
||||
out.push(byte);
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
out.push(bytes[i]);
|
||||
i += 1;
|
||||
}
|
||||
String::from_utf8(out).unwrap_or_else(|_| value.to_string())
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Payment execution (payer side)
|
||||
// ========================================================================
|
||||
|
||||
impl Wallet {
|
||||
/// Pay a NUT-18 payment request.
|
||||
/// Uses CDK's pay_request() which handles:
|
||||
/// - NUT-10 spending conditions
|
||||
/// - Transport selection (Nostr preferred, HTTP POST fallback)
|
||||
/// - Token preparation and delivery
|
||||
pub async fn pay_payment_request(
|
||||
&self,
|
||||
encoded: String,
|
||||
custom_amount: Option<u64>,
|
||||
) -> Result<(), Error> {
|
||||
let creq_str = extract_creq_from_uri(&encoded).unwrap_or(encoded);
|
||||
|
||||
let pr = CdkPaymentRequest::from_str(&creq_str)
|
||||
.map_err(|e| Error::Cdk(format!("Invalid payment request: {e}")))?;
|
||||
|
||||
self.inner
|
||||
.pay_request(pr, custom_amount.map(Amount::from))
|
||||
.await?;
|
||||
|
||||
self.update_balance_streams().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Payment Request creation (payee/receiver side) — NUT-18/26
|
||||
// ========================================================================
|
||||
|
||||
/// Parameters for creating a payment request.
|
||||
pub struct CreateRequestParams {
|
||||
/// Amount to request (in smallest unit, e.g. sats)
|
||||
pub amount: Option<u64>,
|
||||
/// Currency unit ("sat", "usd", etc.)
|
||||
pub unit: String,
|
||||
/// Human-readable description
|
||||
pub description: Option<String>,
|
||||
/// Nostr relay URLs for the transport
|
||||
pub nostr_relays: Vec<String>,
|
||||
}
|
||||
|
||||
/// Result of creating a payment request.
|
||||
pub struct CreatedPaymentRequest {
|
||||
/// NUT-18 encoding: creqA... (CBOR+base64url)
|
||||
pub creq_a: String,
|
||||
/// NUT-26 encoding: CREQB1... (Bech32m, uppercase for QR)
|
||||
pub creq_b: String,
|
||||
/// Opaque handle holding the ephemeral Nostr keys — pass to wait_for_nostr_payment().
|
||||
/// The secret key never leaves Rust.
|
||||
pub listener_handle: NostrListenerHandle,
|
||||
}
|
||||
|
||||
/// Opaque handle that keeps Nostr ephemeral keys in Rust memory.
|
||||
/// Dart receives this as an opaque reference and passes it back
|
||||
/// to wait_for_nostr_payment() without ever seeing the secret key.
|
||||
/// Also carries the expected payment parameters for validation.
|
||||
#[derive(Clone)]
|
||||
pub struct NostrListenerHandle {
|
||||
keys: NostrKeys,
|
||||
pubkey: PublicKey,
|
||||
relays: Vec<String>,
|
||||
/// Expected amount (None = any amount accepted)
|
||||
expected_amount: Option<Amount>,
|
||||
/// Expected currency unit
|
||||
expected_unit: CurrencyUnit,
|
||||
/// This wallet's mint URL
|
||||
mint_url: MintUrl,
|
||||
}
|
||||
|
||||
/// Serializable data for persisting a NostrListenerHandle across app restarts.
|
||||
pub struct PersistedRequestData {
|
||||
pub secret_hex: String,
|
||||
pub pubkey_hex: String,
|
||||
pub relays: Vec<String>,
|
||||
pub amount: Option<u64>,
|
||||
pub unit: String,
|
||||
pub mint_url: String,
|
||||
}
|
||||
|
||||
impl NostrListenerHandle {
|
||||
/// Export handle data for persistence (e.g. SharedPreferences).
|
||||
/// The secret key is exposed as hex — the caller is responsible
|
||||
/// for storing it securely.
|
||||
#[frb(sync)]
|
||||
pub fn to_persisted(&self) -> PersistedRequestData {
|
||||
PersistedRequestData {
|
||||
secret_hex: self.keys.secret_key().to_secret_hex(),
|
||||
pubkey_hex: self.pubkey.to_hex(),
|
||||
relays: self.relays.clone(),
|
||||
amount: self.expected_amount.map(|a| a.into()),
|
||||
unit: self.expected_unit.to_string(),
|
||||
mint_url: self.mint_url.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reconstruct a handle from persisted data (e.g. after app restart).
|
||||
#[frb(sync)]
|
||||
pub fn from_persisted(data: PersistedRequestData) -> Result<NostrListenerHandle, Error> {
|
||||
let secret_key = nostr_sdk::SecretKey::from_hex(&data.secret_hex)
|
||||
.map_err(|e| Error::Cdk(format!("Invalid persisted secret key: {e}")))?;
|
||||
let keys = NostrKeys::new(secret_key);
|
||||
// Derive pubkey from secret — don't trust the persisted copy
|
||||
let pubkey = keys.public_key;
|
||||
let unit = CurrencyUnit::from_str(&data.unit)
|
||||
.unwrap_or(CurrencyUnit::Custom(data.unit));
|
||||
let mint_url = MintUrl::from_str(&data.mint_url)
|
||||
.map_err(|e| Error::Cdk(format!("Invalid persisted mint URL: {e}")))?;
|
||||
|
||||
Ok(NostrListenerHandle {
|
||||
keys,
|
||||
pubkey,
|
||||
relays: data.relays,
|
||||
expected_amount: data.amount.map(Amount::from),
|
||||
expected_unit: unit,
|
||||
mint_url,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// State of a Nostr payment listener.
|
||||
pub enum NostrPaymentState {
|
||||
/// Connected to relays, waiting for payment
|
||||
Waiting,
|
||||
/// Payment received and tokens claimed
|
||||
Received,
|
||||
/// Error occurred
|
||||
Error,
|
||||
}
|
||||
|
||||
/// Event emitted by the Nostr payment listener.
|
||||
pub struct NostrPaymentEvent {
|
||||
pub state: NostrPaymentState,
|
||||
pub amount: Option<u64>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
/// Create a NUT-18 payment request with Nostr transport.
|
||||
///
|
||||
/// Builds a PaymentRequest with the wallet's mint URL and unit,
|
||||
/// generates ephemeral Nostr keys, and returns both creqA and creqB
|
||||
/// encodings plus the keys needed for the Nostr listener.
|
||||
pub async fn create_payment_request(
|
||||
&self,
|
||||
params: CreateRequestParams,
|
||||
) -> Result<CreatedPaymentRequest, Error> {
|
||||
// Generate ephemeral Nostr keys for this request
|
||||
let keys = NostrKeys::generate();
|
||||
|
||||
// Parse relay URLs for nprofile
|
||||
if params.nostr_relays.is_empty() {
|
||||
return Err(Error::InvalidInput);
|
||||
}
|
||||
|
||||
let relay_urls: Vec<RelayUrl> = params
|
||||
.nostr_relays
|
||||
.iter()
|
||||
.map(|r| RelayUrl::parse(r))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| Error::Cdk(format!("Invalid relay URL: {e}")))?;
|
||||
|
||||
let nprofile = Nip19Profile::new(keys.public_key, relay_urls);
|
||||
let nprofile_bech32 = nprofile
|
||||
.to_bech32()
|
||||
.map_err(|e| Error::Cdk(format!("nprofile encoding failed: {e}")))?;
|
||||
|
||||
// Build the Nostr transport
|
||||
let nostr_transport = Transport {
|
||||
_type: TransportType::Nostr,
|
||||
target: nprofile_bech32,
|
||||
tags: Some(vec![vec!["n".to_string(), "17".to_string()]]),
|
||||
};
|
||||
|
||||
// Build the PaymentRequest with this wallet's mint and unit
|
||||
let mint_url = self.mint_url()?;
|
||||
if params.unit != self.unit {
|
||||
return Err(Error::InvalidInput);
|
||||
}
|
||||
let unit = CurrencyUnit::from_str(&self.unit)
|
||||
.unwrap_or(CurrencyUnit::Custom(self.unit.clone()));
|
||||
|
||||
let pr = CdkPaymentRequest {
|
||||
payment_id: None,
|
||||
amount: params.amount.map(Amount::from),
|
||||
unit: Some(unit.clone()),
|
||||
single_use: Some(true),
|
||||
mints: Some(vec![mint_url.clone()]),
|
||||
description: params.description,
|
||||
transports: vec![nostr_transport],
|
||||
nut10: None,
|
||||
};
|
||||
|
||||
// Encode both formats
|
||||
let creq_a = pr.to_string(); // NUT-18: creqA...
|
||||
let creq_b = pr
|
||||
.to_bech32_string()
|
||||
.map_err(|e| Error::Cdk(format!("Bech32m encoding failed: {e}")))?;
|
||||
|
||||
let pubkey = keys.public_key;
|
||||
Ok(CreatedPaymentRequest {
|
||||
creq_a,
|
||||
creq_b,
|
||||
listener_handle: NostrListenerHandle {
|
||||
keys,
|
||||
pubkey,
|
||||
relays: params.nostr_relays,
|
||||
expected_amount: params.amount.map(Amount::from),
|
||||
expected_unit: unit,
|
||||
mint_url,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Wait for an incoming Nostr payment (NIP-17 gift-wrap).
|
||||
///
|
||||
/// Takes the opaque NostrListenerHandle returned by create_payment_request().
|
||||
/// The secret key never leaves Rust memory.
|
||||
pub async fn wait_for_nostr_payment(
|
||||
&self,
|
||||
handle: NostrListenerHandle,
|
||||
sink: StreamSink<NostrPaymentEvent>,
|
||||
) -> Result<(), Error> {
|
||||
let NostrListenerHandle {
|
||||
keys,
|
||||
pubkey,
|
||||
relays,
|
||||
expected_amount,
|
||||
expected_unit,
|
||||
mint_url,
|
||||
} = handle;
|
||||
|
||||
// Fail fast if the handle was created for a different wallet
|
||||
let current_mint = self.mint_url()?;
|
||||
let current_unit = CurrencyUnit::from_str(&self.unit)
|
||||
.unwrap_or(CurrencyUnit::Custom(self.unit.clone()));
|
||||
if mint_url != current_mint || expected_unit != current_unit {
|
||||
return Err(Error::InvalidInput);
|
||||
}
|
||||
|
||||
if sink
|
||||
.add(NostrPaymentEvent {
|
||||
state: NostrPaymentState::Waiting,
|
||||
amount: None,
|
||||
error: None,
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let _self = self.clone();
|
||||
flutter_rust_bridge::spawn(async move {
|
||||
match wait_for_nostr_payment_inner(
|
||||
&_self, keys, pubkey, relays, expected_amount, expected_unit, mint_url, &sink,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(amount) => {
|
||||
let _ = sink.add(NostrPaymentEvent {
|
||||
state: NostrPaymentState::Received,
|
||||
amount: Some(amount.into()),
|
||||
error: None,
|
||||
});
|
||||
_self.update_balance_streams().await;
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = sink.add(NostrPaymentEvent {
|
||||
state: NostrPaymentState::Error,
|
||||
amount: None,
|
||||
error: Some(e.to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal: connect to relays, listen for gift-wrapped payment, receive tokens.
|
||||
/// Validates incoming payments against expected amount/unit/mint before accepting.
|
||||
/// Periodically checks if the Dart stream is still alive and disconnects if not.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn wait_for_nostr_payment_inner(
|
||||
wallet: &Wallet,
|
||||
keys: NostrKeys,
|
||||
pubkey: PublicKey,
|
||||
relays: Vec<String>,
|
||||
expected_amount: Option<Amount>,
|
||||
expected_unit: CurrencyUnit,
|
||||
expected_mint: MintUrl,
|
||||
sink: &StreamSink<NostrPaymentEvent>,
|
||||
) -> Result<Amount, Error> {
|
||||
use cdk::nuts::{nut00::ProofsMethods, PaymentRequestPayload, Token as CdkToken};
|
||||
use nostr_sdk::{Client, Filter, Kind};
|
||||
use std::time::Duration;
|
||||
|
||||
// Create Nostr client with ephemeral keys
|
||||
let client = Client::new(keys.clone());
|
||||
for relay in &relays {
|
||||
client
|
||||
.add_read_relay(relay)
|
||||
.await
|
||||
.map_err(|e| Error::Cdk(format!("Failed to add relay {relay}: {e}")))?;
|
||||
}
|
||||
|
||||
// Connect with timeout so we fail fast if relays are unreachable
|
||||
tokio::time::timeout(Duration::from_secs(10), client.connect())
|
||||
.await
|
||||
.map_err(|_| Error::Network("Relay connection timed out".to_string()))?;
|
||||
|
||||
// Subscribe to NIP-17 gift-wrap events (kind 1059) addressed to our ephemeral pubkey
|
||||
let filter = Filter::new().pubkey(pubkey).kind(Kind::GiftWrap);
|
||||
client
|
||||
.subscribe(filter, None)
|
||||
.await
|
||||
.map_err(|e| Error::Cdk(format!("Subscription failed: {e}")))?;
|
||||
|
||||
// Listen for notifications with periodic cancellation check.
|
||||
// Every 1s we check if the Dart stream is still alive by attempting
|
||||
// a sink.add(). If it fails, the Dart side disposed the stream and
|
||||
// we disconnect cleanly. Short interval minimizes "Fail to post" spam.
|
||||
let mut notifications = client.notifications();
|
||||
loop {
|
||||
match tokio::time::timeout(Duration::from_secs(1), notifications.recv()).await {
|
||||
Ok(Ok(notification)) => {
|
||||
// Check sink is alive before processing
|
||||
if sink
|
||||
.add(NostrPaymentEvent {
|
||||
state: NostrPaymentState::Waiting,
|
||||
amount: None,
|
||||
error: None,
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
client.disconnect().await;
|
||||
return Err(Error::Network("Listener cancelled by client".to_string()));
|
||||
}
|
||||
|
||||
if let nostr_sdk::RelayPoolNotification::Event { event, .. } = notification {
|
||||
// Try to unwrap NIP-17 gift-wrap
|
||||
let unwrapped = match client.unwrap_gift_wrap(&event).await {
|
||||
Ok(rumor) => rumor,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
// Parse PaymentRequestPayload from the rumor content
|
||||
let payload: PaymentRequestPayload =
|
||||
match serde_json::from_str(&unwrapped.rumor.content) {
|
||||
Ok(p) => p,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
// Validate unit matches
|
||||
if payload.unit != expected_unit {
|
||||
log::warn!(
|
||||
"Ignoring payment: unit mismatch (expected {}, got {})",
|
||||
expected_unit,
|
||||
payload.unit
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate mint matches
|
||||
if payload.mint != expected_mint {
|
||||
log::warn!(
|
||||
"Ignoring payment: mint mismatch (expected {}, got {})",
|
||||
expected_mint,
|
||||
payload.mint
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate amount if a specific amount was requested
|
||||
if let Some(expected) = expected_amount {
|
||||
let received_amount = payload.proofs.total_amount().unwrap_or_default();
|
||||
if received_amount < expected {
|
||||
log::warn!(
|
||||
"Ignoring payment: underpayment (expected {}, got {})",
|
||||
expected,
|
||||
received_amount
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Build a token from the validated payload and receive it
|
||||
let token = CdkToken::new(
|
||||
payload.mint,
|
||||
payload.proofs,
|
||||
payload.memo,
|
||||
payload.unit,
|
||||
);
|
||||
let token_str = token.to_string();
|
||||
|
||||
// Continue listening on receive errors (e.g. already-spent proofs)
|
||||
match wallet
|
||||
.inner
|
||||
.receive(&token_str, CdkReceiveOptions::default())
|
||||
.await
|
||||
{
|
||||
Ok(received) => {
|
||||
client.disconnect().await;
|
||||
return Ok(received);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Failed to receive token, continuing: {e}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Err(_)) => {
|
||||
// Notification channel closed
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
// Timeout — check if the Dart stream is still alive
|
||||
if sink
|
||||
.add(NostrPaymentEvent {
|
||||
state: NostrPaymentState::Waiting,
|
||||
amount: None,
|
||||
error: None,
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
// Dart side cancelled the stream, clean up
|
||||
client.disconnect().await;
|
||||
return Err(Error::Network("Listener cancelled by client".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.disconnect().await;
|
||||
Err(Error::Network(
|
||||
"Nostr listener ended before a payment was received".to_string(),
|
||||
))
|
||||
}
|
||||
+25
-4
@@ -45,7 +45,7 @@ pub struct Wallet {
|
||||
pub unit: String,
|
||||
|
||||
balance_broadcast: broadcast::Sender<u64>,
|
||||
inner: CdkWallet,
|
||||
pub(crate) inner: CdkWallet,
|
||||
seed: [u8; 64],
|
||||
}
|
||||
|
||||
@@ -179,12 +179,33 @@ impl Wallet {
|
||||
.inner
|
||||
.mint_quote(PaymentMethod::BOLT11, Some(amount.into()), description, None)
|
||||
.await?;
|
||||
let _ = sink.add(MintQuote::from(quote.clone()));
|
||||
|
||||
if sink.add(MintQuote::from(quote.clone())).is_err() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let _self = self.clone();
|
||||
flutter_rust_bridge::spawn(async move {
|
||||
loop {
|
||||
sleep(Duration::from_secs(3)).await;
|
||||
|
||||
// Check if the Dart stream is still alive before polling
|
||||
if sink
|
||||
.add(MintQuote {
|
||||
id: quote.id.clone(),
|
||||
request: quote.request.clone(),
|
||||
amount: quote.amount.map(|a| a.into()),
|
||||
expiry: Some(quote.expiry),
|
||||
state: MintQuoteState::Unpaid,
|
||||
token: None,
|
||||
error: None,
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
info!("Mint polling stopped: Dart stream closed for {}", quote.id);
|
||||
break;
|
||||
}
|
||||
|
||||
info!("Checking mint quote state for {}", quote.id);
|
||||
match _self.inner.check_mint_quote_status("e.id).await {
|
||||
Ok(state_res) => match state_res.state {
|
||||
@@ -383,7 +404,7 @@ impl Wallet {
|
||||
|
||||
// === Internal helpers ===
|
||||
|
||||
fn mint_url(&self) -> Result<MintUrl, Error> {
|
||||
pub(crate) fn mint_url(&self) -> Result<MintUrl, Error> {
|
||||
Ok(MintUrl::from_str(&self.mint_url)?)
|
||||
}
|
||||
|
||||
@@ -391,7 +412,7 @@ impl Wallet {
|
||||
CurrencyUnit::from_str(&self.unit).unwrap_or(CurrencyUnit::Custom(self.unit.clone()))
|
||||
}
|
||||
|
||||
async fn update_balance_streams(&self) {
|
||||
pub(crate) async fn update_balance_streams(&self) {
|
||||
let balance = self
|
||||
.inner
|
||||
.total_balance()
|
||||
|
||||
+1223
-108
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user