Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
286eae3812 | ||
|
|
e1e8057be5 | ||
|
|
a1ce50bcd4 | ||
|
|
e9d245c986 | ||
|
|
9836f8f246 | ||
|
|
88e1483152 | ||
|
|
0e7ff326cf | ||
|
|
188e26784b | ||
|
|
989b710cd8 | ||
|
|
01ea5be578 |
@@ -1,6 +1,6 @@
|
||||
## ElCaju 🥜
|
||||
|
||||
[](https://deepwiki.com/Forte11Cuba/elcaju)
|
||||
[](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
@@ -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([
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -9,7 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
cdk_flutter
|
||||
rust_builder
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
Reference in New Issue
Block a user