Compare commits

...
17 changed files with 214 additions and 186 deletions
+141 -115
View File
@@ -1,6 +1,6 @@
## ElCaju 🥜
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Forte11Cuba/elcaju)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Forte11Cuba/elcaju)
*Ask questions about this project using DeepWiki AI*
<p align="center">
@@ -28,7 +28,7 @@
---
## 📱 About ElCaju
## About ElCaju
**ElCaju** is a [Cashu](https://cashu.space) wallet (ecash on Bitcoin) with Cuban identity, brother of [LaChispa](https://github.com/lachispame/) (Lightning). Designed to offer privacy, offline transactions, and a warm tropical experience.
@@ -41,23 +41,31 @@ Cashu is an ecash protocol that enables Bitcoin transactions with maximum privac
---
## Features
## Features
| Feature | Description |
|---------|-------------|
| 🔐 **Total Privacy** | Fungible ecash tokens, no traceable history |
| 📴 **Works Offline** | Store and share tokens without internet |
| **Lightning Bridge** | Deposit and withdraw sats via Lightning Network |
| 🏦 **Multi-Mint** | Connect to multiple Cashu mints simultaneously |
| 💱 **Multi-Unit** | Support for sat, USD, EUR and more |
| 🔑 **Your Seed, Your Money** | Backup with 12 words (BIP39) |
| 🔒 **Optional PIN** | Protect access to your wallet |
| 🌐 **Bilingual** | Spanish and English |
| 🎉 **Friendly UX** | Visual effects and confetti when receiving funds |
| **Total Privacy** | Fungible ecash tokens, no traceable history |
| **Works Offline** | Store, create, and share tokens without internet |
| **Lightning Bridge** | Deposit and withdraw sats via Lightning Network |
| **Multi-Mint** | Connect to unlimited Cashu mints simultaneously |
| **Multi-Unit** | Support for sat, USD, EUR, and any unit offered by mints |
| **P2PK (NUT-10)** | Lock tokens to a Nostr public key — only the recipient can claim |
| **Token Recovery (NUT-13)** | Restore tokens from any mint using your seed phrase |
| **NFC Transfers** | Share tokens via NFC — includes Host Card Emulation (HCE) |
| **Animated QR** | UR-format multi-frame QR codes for large tokens |
| **Peanut Emoji** | Encode tokens as emoji (compatible with cashu.me) |
| **Transaction History** | Full history with filters: Ecash, Lightning, Pending |
| **Pending Tokens** | Save tokens to claim later (up to 50, 30-day expiry) |
| **BTC Price** | Live price via Yadio API with fiat conversion |
| **Your Seed, Your Money** | Backup with 12 words (BIP39) |
| **Optional PIN** | 4-digit PIN to protect wallet access |
| **11 Languages** | ES, EN, PT, FR, RU, DE, IT, KO, ZH, JA, SW |
| **Friendly UX** | Confetti celebrations when receiving funds |
---
## 🚀 Screenshots
## Screenshots
<p align="center">
<img src="assets/img/Captura1.png" width="180"/>
@@ -69,35 +77,42 @@ Cashu is an ecash protocol that enables Bitcoin transactions with maximum privac
---
## 🔧 Technologies
## Technologies
### Frontend
- **Flutter 3.27+**: Cross-platform framework
- **Dart**: Programming language
- **Dart 3.11+**: Programming language
- **Provider**: State management
- **QR Flutter**: QR code generation
- **QR Flutter + Mobile Scanner**: QR generation and scanning
### Backend & Core
- **[cdk-flutter](https://github.com/cashubtc/cdk_flutter)**: Cashu Development Kit (Rust FFI)
- **elcaju_core**: Internal Rust library via [flutter_rust_bridge](https://github.com/fzyzcjy/flutter_rust_bridge) v2.11.1 (FFI)
- **[CDK](https://github.com/cashubtc/cdk) 0.15.1**: Cashu Development Kit (wallet, SQLite, HTTP client, signatory)
- **SQLite**: Local persistence via cdk-sqlite
- **Flutter Secure Storage**: Secure storage for seed and PIN
- **Flutter Secure Storage**: Encrypted storage for seed and PIN
### Protocols
- **Cashu**: NUT-00 to NUT-13 (ecash protocol)
- **Cashu**: NUT-00, 04, 05, 06, 07, 09, 10 (P2PK), 13 (restore)
- **Lightning Network**: Deposits and withdrawals via BOLT11
- **BIP39**: Seed phrase generation
- **BIP39 / BIP32**: Seed phrase generation and key derivation
- **NIP-06**: Nostr key derivation from mnemonic for P2PK
### Native
- **NFC Manager + HCE**: Phone-to-phone token transfers
- **Android NDK 27.0**: Multi-architecture native compilation
- **Cargokit**: Automated Rust build integration
---
## 📦 Installation
## Installation
### Prerequisites
- Flutter SDK (>=3.27.0)
- Dart SDK (>=3.6.0)
- Dart SDK (>=3.11.0)
- Android Studio with Android SDK
- Android NDK 26.3+ (install via Android Studio > SDK Manager > SDK Tools)
- Rust toolchain (>=1.85.0)
- Android NDK 27.0+ (install via Android Studio > SDK Manager > SDK Tools)
- Rust toolchain (edition 2024; rustc >= 1.85.0)
### Clone the Repository
@@ -118,7 +133,7 @@ flutter pub get
# Install Rust if not already installed
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Update to latest stable (requires >= 1.85.0)
# Update to latest stable
rustup update stable
# Install Android targets
@@ -137,61 +152,39 @@ linker = "gcc"
rustflags = ["-C", "link-arg=-fuse-ld=bfd"]
[target.aarch64-linux-android]
linker = "/path/to/Android/Sdk/ndk/26.3.11579264/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang"
linker = "/path/to/Android/Sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang"
[target.armv7-linux-androideabi]
linker = "/path/to/Android/Sdk/ndk/26.3.11579264/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang"
linker = "/path/to/Android/Sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang"
[target.x86_64-linux-android]
linker = "/path/to/Android/Sdk/ndk/26.3.11579264/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang"
linker = "/path/to/Android/Sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang"
[env]
CC_armv7-linux-androideabi = "/path/to/Android/Sdk/ndk/26.3.11579264/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang"
AR_armv7-linux-androideabi = "/path/to/Android/Sdk/ndk/26.3.11579264/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
CC_x86_64-linux-android = "/path/to/Android/Sdk/ndk/26.3.11579264/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang"
AR_x86_64-linux-android = "/path/to/Android/Sdk/ndk/26.3.11579264/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
CC_aarch64-linux-android = "/path/to/Android/Sdk/ndk/26.3.11579264/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang"
AR_aarch64-linux-android = "/path/to/Android/Sdk/ndk/26.3.11579264/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
CC_armv7-linux-androideabi = "/path/to/Android/Sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang"
AR_armv7-linux-androideabi = "/path/to/Android/Sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
CC_x86_64-linux-android = "/path/to/Android/Sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang"
AR_x86_64-linux-android = "/path/to/Android/Sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
CC_aarch64-linux-android = "/path/to/Android/Sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang"
AR_aarch64-linux-android = "/path/to/Android/Sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
```
> **Note**: Replace `/path/to/Android/Sdk` with your actual Android SDK path (usually `~/Android/Sdk` on Linux or `~/Library/Android/sdk` on macOS).
### Compile Native Libraries
The cdk_flutter native libraries must be compiled manually:
```bash
# Navigate to cdk_flutter rust directory
cd ~/.pub-cache/git/cdk_flutter-*/rust
# Build for each architecture
cargo build --release --target aarch64-linux-android
cargo build --release --target armv7-linux-androideabi
cargo build --release --target x86_64-linux-android
# Copy libraries to project
mkdir -p /path/to/elcaju/android/app/src/main/jniLibs/{arm64-v8a,armeabi-v7a,x86_64}
cp target/aarch64-linux-android/release/libcdk_flutter.so \
/path/to/elcaju/android/app/src/main/jniLibs/arm64-v8a/
cp target/armv7-linux-androideabi/release/libcdk_flutter.so \
/path/to/elcaju/android/app/src/main/jniLibs/armeabi-v7a/
cp target/x86_64-linux-android/release/libcdk_flutter.so \
/path/to/elcaju/android/app/src/main/jniLibs/x86_64/
```
### Run in Development
The Rust library compiles automatically via Cargokit during `flutter run`:
```bash
flutter run
```
> **Note**: The first build will take several minutes while Rust compiles. Subsequent builds are incremental.
### Build for Production
```bash
# Android APK (includes all architectures, ~55MB)
# Android APK (includes all architectures)
flutter build apk --release
# Android App Bundle (recommended for Play Store)
@@ -203,100 +196,123 @@ flutter build appbundle --release
For faster builds targeting only modern devices:
```bash
# Build only arm64 library
cargo build --release --target aarch64-linux-android
# Copy to project
cp target/aarch64-linux-android/release/libcdk_flutter.so \
android/app/src/main/jniLibs/arm64-v8a/
# Build APK (~40MB)
flutter build apk --release
flutter build apk --release --target-platform android-arm64
```
---
## 🏗️ Architecture
## Architecture
```
```text
┌─────────────────────────────────────────┐
│ Flutter UI (Dart)
│ Provider + Streams + Intl
│ Flutter UI (Dart 3.11+)
│ Provider + Streams + Intl (11 langs)
├─────────────────────────────────────────┤
WalletProvider / SettingsProvider
(Multi-unit: Map<mintUrl:unit>)
4 Providers: Wallet, Settings,
Price (Yadio), P2PK (NIP-06)
├─────────────────────────────────────────┤
cdk-flutter (FFI)
│ flutter_rust_bridge v2.11.1 │
flutter_rust_bridge v2.11.1 (FFI)
├─────────────────────────────────────────┤
Rust Core (CDK)
Cashu 0.13.4 + SQLite + Bitcoin
elcaju_core (Rust interno)
CDK 0.15.1 + SQLite + BIP39 + Tokio
└─────────────────────────────────────────┘
```
### Project Structure
```
```text
elcaju/
├── lib/
│ ├── main.dart # Entry point
│ ├── main.dart # Entry point (RustLib.init + Providers)
│ ├── core/
│ │ ├── constants/ # Colors, dimensions
│ │ ├── theme/ # App theme
│ │ ── utils/ # Formatters, validators
│ │ ├── constants/ # Colors, dimensions
│ │ ├── models/ # Proof model
│ │ ── services/ # LNURL, NFC, price, proofs
│ │ ├── theme/ # Material 3 dark theme
│ │ └── utils/ # Formatters, parsers, nostr, p2pk, peanut codec
│ ├── data/ # Pending tokens, transaction metadata storage
│ ├── models/ # P2PK key model (npub/nsec)
│ ├── providers/
│ │ ├── wallet_provider.dart # Wallet logic
│ │ ── settings_provider.dart# Preferences and PIN
│ │ ├── wallet_provider.dart # Multi-mint, multi-unit wallet logic
│ │ ── settings_provider.dart # Preferences, PIN, seed
│ │ ├── price_provider.dart # BTC price cache (Yadio API)
│ │ └── p2pk_provider.dart # Nostr key management (NIP-06)
│ ├── screens/
│ │ ├── 1_splash/ # Loading screen
│ │ ├── 2_onboarding/ # Create/restore wallet
│ │ ├── 3_home/ # Main screen
│ │ ├── 4_receive/ # Receive tokens
│ │ ├── 5_send/ # Send tokens
│ │ ├── 6_mint/ # Deposit via Lightning
│ │ ├── 7_melt/ # Withdraw to Lightning
│ │ ── 8_settings/ # Settings
│ │ ├── 1_splash/ # Loading & initialization
│ │ ├── 2_onboarding/ # Create / restore / backup seed
│ │ ├── 3_home/ # Main dashboard
│ │ ├── 4_receive/ # Receive tokens (paste, QR, NFC)
│ │ ├── 5_send/ # Send tokens + offline mode + share
│ │ ├── 6_mint/ # Lightning deposit
│ │ ├── 7_melt/ # Lightning withdrawal
│ │ ── 8_settings/ # Settings, mints, P2PK keys, language
│ │ ├── 9_history/ # Transaction history with filters
│ │ └── 10_scanner/ # QR code scanner
│ ├── src/rust/ # FFI bridge (auto-generated + API bindings)
│ ├── widgets/
│ │ ├── common/ # Buttons, cards, etc.
│ │ ── effects/ # Confetti and animations
└── l10n/ # Translations ES/EN
└── assets/
└── img/ # Logo and assets
│ │ ├── common/ # Buttons, cards, numpad, backgrounds
│ │ ── effects/ # Confetti animation
│ ├── proof/ # Proof selector (offline send)
│ │ └── scanner/ # QR scanner widget
└── l10n/ # 11 languages (354 translation keys)
├── rust/
│ ├── Cargo.toml # elcaju_core: CDK 0.15.1, flutter_rust_bridge 2.11.1
│ └── src/api/ # wallet, token, keys, mint_info, error
├── rust_builder/ # Cargokit integration (auto Rust compilation)
├── android/ # Android config (NDK 27.0, NFC HCE service)
└── assets/img/ # Logo and screenshots
```
---
## 🔐 Security
## Cashu NUT Support
| NUT | Name | Status |
|-----|------|--------|
| NUT-00 | Cashu Protocol | Supported |
| NUT-04 | Mint (Lightning deposit) | Supported |
| NUT-05 | Melt (Lightning withdrawal) | Supported |
| NUT-06 | Token swap | Supported |
| NUT-07 | Spend conditions (witness) | Supported |
| NUT-09 | Token restore | Supported |
| NUT-10 | P2PK (locked tokens) | Supported |
| NUT-13 | Deterministic secrets (recovery) | Supported |
---
## Security
| Feature | Implementation |
|---------|----------------|
| **Seed Phrase** | Stored with Flutter Secure Storage (native encryption) |
| **PIN** | Secure hash, local verification |
| **Local Data** | SQLite with proofs encrypted by Cashu protocol |
| **Seed Phrase** | BIP39 12 words, stored with Flutter Secure Storage (native encryption) |
| **Key Derivation** | BIP32 + NIP-06 (m/44'/1237'/0'/0/0) for P2PK keys |
| **PIN** | 4-digit PIN with secure hash, local verification |
| **Local Data** | SQLite with proofs managed by CDK |
| **P2PK Keys** | Primary (from seed) + up to 10 imported keys, all encrypted |
| **No Tracking** | We don't collect user data |
| **Open Source** | 100% auditable |
### Important
> ⚠️ **Backup your seed phrase (12 words)**. Without it, you won't be able to recover your funds if you lose your device.
> **Backup your seed phrase (12 words)**. Without it, you won't be able to recover your funds if you lose your device.
---
## 📱 Compatibility
## Compatibility
| Platform | Status | Minimum Version |
|----------|--------|-----------------|
| Android | Supported | API 24 (Android 7.0) |
| 🚧 iOS | Coming soon | iOS 12.0+ |
| 🚧 Web | Coming soon | - |
| Android | Supported | API 24 (Android 7.0) |
| iOS | Coming soon | iOS 11.0+ |
| Web | Coming soon | - |
---
## 🌐 Default Mint
## Default Mint
ElCaju comes preconfigured with the Cuba Bitcoin mint:
```
```text
https://mint.cubabitcoin.org
```
@@ -304,7 +320,16 @@ You can add other Cashu mints from the settings.
---
## 🤝 Contributing
## CI/CD
Automated releases via GitHub Actions (`.github/workflows/release.yml`):
- Triggers on Git tags (`v*`)
- Builds multi-architecture APKs (arm64, armv7, x86_64) + universal APK
- Creates GitHub releases automatically
---
## Contributing
Contributions are welcome!
@@ -324,16 +349,17 @@ Open an [issue](https://github.com/Forte11Cuba/elcaju/issues) with:
---
## 📄 License
## License
This project is under the MIT License - see the [LICENSE](LICENSE) file for more details.
---
## 🙏 Credits
## Credits
- **[Cashu](https://cashu.space)** - Ecash protocol
- **[cdk-flutter](https://github.com/cashubtc/cdk_flutter)** - Cashu Development Kit
- **[CDK](https://github.com/cashubtc/cdk)** - Cashu Development Kit
- **[flutter_rust_bridge](https://github.com/fzyzcjy/flutter_rust_bridge)** - Rust-Dart FFI
- **[Cashu4Community](https://cashu4community.xyz)** - Global Cashu community
- **[Cuba Bitcoin](https://cubabitcoin.org)** - Cuban Bitcoin community
+3 -3
View File
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:cdk_flutter/cdk_flutter.dart' hide WalletProvider;
import 'src/rust/frb_generated.dart';
import 'package:elcaju/l10n/app_localizations.dart';
import 'core/theme/app_theme.dart';
import 'providers/wallet_provider.dart';
@@ -15,8 +15,8 @@ import 'screens/1_splash/splash_screen.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Inicializar CDK Flutter (Cashu SDK)
await CdkFlutter.init();
// Inicializar bridge Rust interno (Cashu SDK)
await RustLib.init();
// Configurar orientación y estilo de barra de estado
SystemChrome.setPreferredOrientations([
+1 -1
View File
@@ -11,7 +11,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:bip32/bip32.dart' as bip32;
import 'package:cdk_flutter/cdk_flutter.dart' as cdk;
import '../src/rust/api/keys.dart' as cdk;
import 'package:uuid/uuid.dart';
import '../models/p2pk_key.dart';
+29 -36
View File
@@ -3,8 +3,10 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:cbor/cbor.dart';
import 'package:cdk_flutter/cdk_flutter.dart';
import 'package:http/http.dart' as http;
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 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';
@@ -12,6 +14,7 @@ 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
@@ -177,15 +180,11 @@ class WalletProvider extends ChangeNotifier {
// ============================================================
/// Verifica si podemos alcanzar un mint específico.
/// Hace ping HTTP GET a {mintUrl}/v1/info con timeout de 3 segundos.
/// Usa función Rust que hace GET a {mintUrl}/v1/info.
/// Retorna true si responde 200, false en cualquier otro caso.
Future<bool> canReachMint(String mintUrl) async {
try {
final uri = Uri.parse('$mintUrl/v1/info');
final response = await http.get(uri).timeout(
const Duration(seconds: 3),
);
return response.statusCode == 200;
return await pingMint(mintUrl: mintUrl);
} catch (e) {
debugPrint('Ping to mint failed: $e');
return false;
@@ -409,7 +408,7 @@ class WalletProvider extends ChangeNotifier {
/// Usa mintInfo.nuts.nut04 (MintMethodSettings) para obtener las unidades.
Future<List<String>> _detectAndAddMint(String mintUrl) async {
try {
// Obtener info del mint (función global de cdk_flutter)
// Obtener info del mint (función del bridge Rust)
final mintInfo = await getMintInfo(mintUrl: mintUrl);
// Extraer unidades de nut04 (mint methods)
@@ -446,31 +445,14 @@ class WalletProvider extends ChangeNotifier {
}
/// Obtiene los keysets de un mint y los agrega al caché _keysetUnits.
/// Llama a GET {mintUrl}/v1/keysets para obtener el mapping keyset_id → unit.
/// Usa función Rust que llama a GET {mintUrl}/v1/keysets.
Future<void> _fetchKeysets(String mintUrl) async {
try {
final response = await http.get(
Uri.parse('$mintUrl/v1/keysets'),
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as Map<String, dynamic>;
final keysets = data['keysets'] as List<dynamic>?;
if (keysets != null) {
for (final ks in keysets) {
final id = ks['id'] as String?;
final unit = ks['unit'] as String?;
if (id != null && unit != null) {
_keysetUnits[id] = unit;
}
}
debugPrint('Keysets cargados para $mintUrl: ${keysets.length}');
}
} else {
debugPrint('Error HTTP ${response.statusCode} al obtener keysets de $mintUrl');
final keysets = await fetchKeysets(mintUrl: mintUrl);
for (final ks in keysets) {
_keysetUnits[ks.id] = ks.unit;
}
debugPrint('Keysets cargados para $mintUrl: ${keysets.length}');
} catch (e) {
debugPrint('Error obteniendo keysets de $mintUrl: $e');
// No lanzamos excepción, el caché simplemente no tendrá estos keysets
@@ -1720,7 +1702,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');
@@ -1733,8 +1716,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);
@@ -1763,7 +1754,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};
@@ -1777,7 +1769,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
@@ -64,7 +64,7 @@ class _RestoreWalletScreenState extends State<RestoreWalletScreen> {
final p2pkProvider = context.read<P2PKProvider>();
// Inicializar wallet primero (valida el mnemonic internamente).
// Si es inválido, cdk_flutter lanzará una excepción antes de persistir.
// Si es inválido, el bridge Rust lanzará una excepción antes de persistir.
await walletProvider.initialize(mnemonic);
// Solo guardar mnemonic si initialize() pasó sin error
+5 -1
View File
@@ -11,6 +11,7 @@ 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';
@@ -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;
+1 -1
View File
@@ -4,7 +4,7 @@ import 'package:flutter/services.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:share_plus/share_plus.dart';
import 'package:cdk_flutter/cdk_flutter.dart' as cdk;
import '../../src/rust/api/token.dart' as cdk;
import 'package:elcaju/l10n/app_localizations.dart';
import '../../core/constants/colors.dart';
import '../../core/constants/dimensions.dart';
+3 -4
View File
@@ -4,7 +4,7 @@ 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:cdk_flutter/cdk_flutter.dart' hide WalletProvider;
import '../../src/rust/api/wallet.dart';
import 'package:elcaju/l10n/app_localizations.dart';
import '../../core/constants/colors.dart';
import '../../core/constants/dimensions.dart';
@@ -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;
}
});
},
+1 -1
View File
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:cdk_flutter/cdk_flutter.dart' hide WalletProvider;
import '../../src/rust/api/wallet.dart';
import 'package:elcaju/l10n/app_localizations.dart';
import '../../core/constants/colors.dart';
import '../../core/constants/dimensions.dart';
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:cdk_flutter/cdk_flutter.dart' show MintInfo, ContactInfo;
import '../../src/rust/api/mint_info.dart' show MintInfo, ContactInfo;
import 'package:elcaju/l10n/app_localizations.dart';
import '../../core/constants/colors.dart';
import '../../core/utils/formatters.dart';
+2 -2
View File
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:provider/provider.dart';
import 'package:cdk_flutter/cdk_flutter.dart' show MintInfo;
import '../../src/rust/api/mint_info.dart' show MintInfo;
import 'package:elcaju/l10n/app_localizations.dart';
import '../../core/constants/colors.dart';
import '../../core/constants/dimensions.dart';
@@ -623,7 +623,7 @@ class _AddMintDialogState extends State<_AddMintDialog> {
});
try {
// Intentar agregar el mint (cdk_flutter validará la conexión)
// Intentar agregar el mint (el bridge Rust validará la conexión)
await widget.walletProvider.addMint(url);
if (mounted) {
+12 -3
View File
@@ -5,8 +5,8 @@ import 'package:provider/provider.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:intl/intl.dart';
import 'package:cdk_flutter/cdk_flutter.dart' as cdk;
import 'package:cdk_flutter/cdk_flutter.dart' show Transaction, TransactionDirection, TransactionStatus;
import '../../src/rust/api/token.dart' as cdk;
import '../../src/rust/api/wallet.dart' show Transaction, TransactionDirection, TransactionStatus;
import 'package:elcaju/l10n/app_localizations.dart';
import '../../core/constants/colors.dart';
import '../../core/constants/dimensions.dart';
@@ -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(
+2 -2
View File
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:elcaju/l10n/app_localizations.dart';
import 'package:cdk_flutter/cdk_flutter.dart';
import '../../src/rust/api/token.dart';
import '../../core/constants/colors.dart';
import '../../core/utils/incoming_data_parser.dart';
@@ -36,7 +36,7 @@ class QrScannerWidget extends StatefulWidget {
class _QrScannerWidgetState extends State<QrScannerWidget> {
late MobileScannerController _controller;
// Estado para QR animados (UR multipartes) usando TokenDecoder de cdk-flutter
// Estado para QR animados (UR multipartes) TokenDecoder (Rust bridge)
TokenDecoder? _urDecoder;
bool _isCapturingUr = false;
final Set<String> _urFragmentsSeen = {}; // Trackear fragmentos únicos
+1 -1
View File
@@ -8,7 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
cdk_flutter
rust_builder
)
set(PLUGIN_BUNDLED_LIBRARIES)
+7 -9
View File
@@ -161,15 +161,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.3.5"
cdk_flutter:
dependency: "direct main"
description:
path: "."
ref: HEAD
resolved-ref: "7fb8580530d963c9ff09f23e55a1e56bccd8676b"
url: "https://github.com/cashubtc/cdk_flutter.git"
source: git
version: "0.1.0"
characters:
dependency: transitive
description:
@@ -814,6 +805,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.0"
rust_builder:
dependency: "direct main"
description:
path: rust_builder
relative: true
source: path
version: "0.1.0"
share_plus:
dependency: "direct main"
description:
+3 -4
View File
@@ -15,10 +15,9 @@ dependencies:
cupertino_icons: ^1.0.8
# CDK Flutter (Cashu backend)
cdk_flutter:
git:
url: https://github.com/cashubtc/cdk_flutter.git
# Internal Rust bridge (Cashu backend via cdk)
rust_builder:
path: rust_builder
# State Management
provider: ^6.1.2
+1 -1
View File
@@ -9,7 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
cdk_flutter
rust_builder
)
set(PLUGIN_BUNDLED_LIBRARIES)