From 376632e22dcfb8ec05f9c489934f880f9d28bae7 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Sat, 20 Apr 2024 10:07:50 +0100 Subject: [PATCH 001/100] Allow the app to run without building monero --- cw_haven/android/build.gradle | 1 - cw_monero/android/build.gradle | 1 - .../src/main/kotlin/com/cakewallet/monero/CwMoneroPlugin.kt | 1 - lib/main.dart | 1 - 4 files changed, 4 deletions(-) diff --git a/cw_haven/android/build.gradle b/cw_haven/android/build.gradle index fb941f657..1319e4ad4 100644 --- a/cw_haven/android/build.gradle +++ b/cw_haven/android/build.gradle @@ -35,7 +35,6 @@ android { } externalNativeBuild { cmake { - path "CMakeLists.txt" } } } diff --git a/cw_monero/android/build.gradle b/cw_monero/android/build.gradle index fc4835e81..46b1b4315 100644 --- a/cw_monero/android/build.gradle +++ b/cw_monero/android/build.gradle @@ -39,7 +39,6 @@ android { } externalNativeBuild { cmake { - path "CMakeLists.txt" } } } diff --git a/cw_monero/android/src/main/kotlin/com/cakewallet/monero/CwMoneroPlugin.kt b/cw_monero/android/src/main/kotlin/com/cakewallet/monero/CwMoneroPlugin.kt index 37684a16a..57eec7d00 100644 --- a/cw_monero/android/src/main/kotlin/com/cakewallet/monero/CwMoneroPlugin.kt +++ b/cw_monero/android/src/main/kotlin/com/cakewallet/monero/CwMoneroPlugin.kt @@ -26,7 +26,6 @@ class CwMoneroPlugin: MethodCallHandler { val main = Handler(Looper.getMainLooper()); init { - System.loadLibrary("cw_monero") } @JvmStatic diff --git a/lib/main.dart b/lib/main.dart index b80c9eb85..cf9a766a4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -207,7 +207,6 @@ Future initialSetup( unspentCoinsInfoSource: unspentCoinsInfoSource, secureStorage: secureStorage); await bootstrap(navigatorKey); - monero?.onStartup(); } class App extends StatefulWidget { From 9e9ff7095e91b500114cd47196033fb84d96d55a Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Sun, 21 Apr 2024 00:20:52 +0100 Subject: [PATCH 002/100] Add mwebd --- cw_bitcoin/lib/litecoin_wallet.dart | 15 + cw_bitcoin/lib/litecoin_wallet_service.dart | 6 + cw_bitcoin/pubspec.lock | 47 ++ cw_bitcoin/pubspec.yaml | 3 + cw_mweb/.gitignore | 31 + cw_mweb/.metadata | 36 + cw_mweb/CHANGELOG.md | 3 + cw_mweb/LICENSE | 1 + cw_mweb/README.md | 15 + cw_mweb/analysis_options.yaml | 4 + cw_mweb/android/.gitignore | 9 + cw_mweb/android/build.gradle | 76 ++ cw_mweb/android/settings.gradle | 1 + cw_mweb/android/src/main/AndroidManifest.xml | 3 + .../com/cakewallet/mweb/CwMwebPlugin.kt | 43 + .../cakewallet/cw_mweb/CwMwebPluginTest.kt | 27 + cw_mweb/ios/.gitignore | 38 + cw_mweb/ios/Assets/.gitkeep | 0 cw_mweb/ios/Classes/CwMwebPlugin.swift | 19 + cw_mweb/ios/cw_mweb.podspec | 23 + cw_mweb/lib/cw_mweb.dart | 17 + cw_mweb/lib/cw_mweb_method_channel.dart | 17 + cw_mweb/lib/cw_mweb_platform_interface.dart | 29 + cw_mweb/lib/mwebd.pb.dart | 773 ++++++++++++++++++ cw_mweb/lib/mwebd.pbgrpc.dart | 159 ++++ cw_mweb/macos/Classes/CwMwebPlugin.swift | 19 + cw_mweb/macos/cw_mweb.podspec | 23 + cw_mweb/pubspec.yaml | 74 ++ cw_mweb/test/cw_mweb_method_channel_test.dart | 27 + cw_mweb/test/cw_mweb_test.dart | 29 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + 31 files changed, 1569 insertions(+) create mode 100644 cw_mweb/.gitignore create mode 100644 cw_mweb/.metadata create mode 100644 cw_mweb/CHANGELOG.md create mode 100644 cw_mweb/LICENSE create mode 100644 cw_mweb/README.md create mode 100644 cw_mweb/analysis_options.yaml create mode 100644 cw_mweb/android/.gitignore create mode 100644 cw_mweb/android/build.gradle create mode 100644 cw_mweb/android/settings.gradle create mode 100644 cw_mweb/android/src/main/AndroidManifest.xml create mode 100644 cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt create mode 100644 cw_mweb/android/src/test/kotlin/com/cakewallet/cw_mweb/CwMwebPluginTest.kt create mode 100644 cw_mweb/ios/.gitignore create mode 100644 cw_mweb/ios/Assets/.gitkeep create mode 100644 cw_mweb/ios/Classes/CwMwebPlugin.swift create mode 100644 cw_mweb/ios/cw_mweb.podspec create mode 100644 cw_mweb/lib/cw_mweb.dart create mode 100644 cw_mweb/lib/cw_mweb_method_channel.dart create mode 100644 cw_mweb/lib/cw_mweb_platform_interface.dart create mode 100644 cw_mweb/lib/mwebd.pb.dart create mode 100644 cw_mweb/lib/mwebd.pbgrpc.dart create mode 100644 cw_mweb/macos/Classes/CwMwebPlugin.swift create mode 100644 cw_mweb/macos/cw_mweb.podspec create mode 100644 cw_mweb/pubspec.yaml create mode 100644 cw_mweb/test/cw_mweb_method_channel_test.dart create mode 100644 cw_mweb/test/cw_mweb_test.dart diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index d2379d5a5..f218ddd5d 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; @@ -14,6 +15,8 @@ import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/litecoin_network.dart'; +import 'package:cw_mweb/cw_mweb.dart'; +import 'package:cw_mweb/mwebd.pb.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; part 'litecoin_wallet.g.dart'; @@ -103,6 +106,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } + @action + @override + Future startSync() async { + super.startSync(); + final stub = CwMweb.stub(); + Timer.periodic( + const Duration(seconds: 1), (timer) async { + final resp = await stub.status(StatusRequest()); + print(resp.blockHeaderHeight); + }); + } + @override int feeRate(TransactionPriority priority) { if (priority is LitecoinTransactionPriority) { diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index ee3b0e628..7f2066c30 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'package:path_provider/path_provider.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:hive/hive.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; @@ -10,6 +11,7 @@ import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_base.dart'; +import 'package:cw_mweb/cw_mweb.dart'; import 'package:collection/collection.dart'; class LitecoinWalletService extends WalletService< @@ -26,6 +28,8 @@ class LitecoinWalletService extends WalletService< @override Future create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async { + final appDir = await getApplicationSupportDirectory(); + await CwMweb.start(appDir.path); final wallet = await LitecoinWalletBase.create( mnemonic: await generateMnemonic(), password: credentials.password!, @@ -43,6 +47,8 @@ class LitecoinWalletService extends WalletService< @override Future openWallet(String name, String password) async { + final appDir = await getApplicationSupportDirectory(); + await CwMweb.start(appDir.path); final walletInfo = walletInfoSource.values.firstWhereOrNull( (info) => info.id == WalletBase.idFor(name, getType()))!; diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 50cd432c0..20851ea86 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.7.0" + archive: + dependency: transitive + description: + name: archive + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" + url: "https://pub.dev" + source: hosted + version: "3.4.10" args: dependency: transitive description: @@ -252,6 +260,13 @@ packages: relative: true source: path version: "0.0.1" + cw_mweb: + dependency: "direct main" + description: + path: "../cw_mweb" + relative: true + source: path + version: "0.0.1" dart_style: dependency: transitive description: @@ -334,6 +349,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + googleapis_auth: + dependency: transitive + description: + name: googleapis_auth + sha256: af7c3a3edf9d0de2e1e0a77e994fae0a581c525fa7012af4fa0d4a52ed9484da + url: "https://pub.dev" + source: hosted + version: "1.4.1" graphs: dependency: transitive description: @@ -342,6 +365,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" + grpc: + dependency: "direct main" + description: + name: grpc + sha256: e93ee3bce45c134bf44e9728119102358c7cd69de7832d9a874e2e74eb8cab40 + url: "https://pub.dev" + source: hosted + version: "3.2.4" hex: dependency: transitive description: @@ -374,6 +405,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + http2: + dependency: transitive + description: + name: http2 + sha256: "9ced024a160b77aba8fb8674e38f70875e321d319e6f303ec18e87bd5a4b0c1d" + url: "https://pub.dev" + source: hosted + version: "2.3.0" http_multi_server: dependency: transitive description: @@ -582,6 +621,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + protobuf: + dependency: transitive + description: + name: protobuf + sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d" + url: "https://pub.dev" + source: hosted + version: "3.1.0" provider: dependency: transitive description: diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 632a3140a..033d1f84e 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -35,6 +35,9 @@ dependencies: url: https://github.com/cake-tech/bitcoin_base.git ref: cake-update-v2 blockchain_utils: ^2.1.1 + cw_mweb: + path: ../cw_mweb + grpc: ^3.2.4 dev_dependencies: flutter_test: diff --git a/cw_mweb/.gitignore b/cw_mweb/.gitignore new file mode 100644 index 000000000..8959f5d70 --- /dev/null +++ b/cw_mweb/.gitignore @@ -0,0 +1,31 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ +libs/ diff --git a/cw_mweb/.metadata b/cw_mweb/.metadata new file mode 100644 index 000000000..606303914 --- /dev/null +++ b/cw_mweb/.metadata @@ -0,0 +1,36 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + channel: stable + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + - platform: android + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + - platform: ios + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + - platform: macos + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/cw_mweb/CHANGELOG.md b/cw_mweb/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/cw_mweb/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/cw_mweb/LICENSE b/cw_mweb/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/cw_mweb/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/cw_mweb/README.md b/cw_mweb/README.md new file mode 100644 index 000000000..8a839b1ec --- /dev/null +++ b/cw_mweb/README.md @@ -0,0 +1,15 @@ +# cw_mweb + +A new Flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/cw_mweb/analysis_options.yaml b/cw_mweb/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/cw_mweb/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/cw_mweb/android/.gitignore b/cw_mweb/android/.gitignore new file mode 100644 index 000000000..161bdcdaf --- /dev/null +++ b/cw_mweb/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/cw_mweb/android/build.gradle b/cw_mweb/android/build.gradle new file mode 100644 index 000000000..7e67b98ad --- /dev/null +++ b/cw_mweb/android/build.gradle @@ -0,0 +1,76 @@ +group 'com.cakewallet.mweb' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.3.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.allprojects { + repositories { + flatDir { + dirs project(':cw_mweb').file('libs') + } + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 31 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + test.java.srcDirs += 'src/test/kotlin' + } + + defaultConfig { + minSdkVersion 16 + } + + dependencies { + testImplementation 'org.jetbrains.kotlin:kotlin-test' + testImplementation 'org.mockito:mockito-core:5.0.0' + } + + testOptions { + unitTests.all { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } + } +} + +dependencies { + implementation (name: 'mwebd', ext: 'aar') +} diff --git a/cw_mweb/android/settings.gradle b/cw_mweb/android/settings.gradle new file mode 100644 index 000000000..88fbd66fb --- /dev/null +++ b/cw_mweb/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'cw_mweb' diff --git a/cw_mweb/android/src/main/AndroidManifest.xml b/cw_mweb/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..fd3746a8c --- /dev/null +++ b/cw_mweb/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt new file mode 100644 index 000000000..394b607ab --- /dev/null +++ b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt @@ -0,0 +1,43 @@ +package com.cakewallet.mweb + +import androidx.annotation.NonNull + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result + +import mwebd.Mwebd + +/** CwMwebPlugin */ +class CwMwebPlugin: FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel : MethodChannel + private var started = false + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "cw_mweb") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + if (call.method == "start") { + if (started) return + val dataDir = call.argument("dataDir") ?: "" + val server = Mwebd.newServer("mainnet", dataDir, "") + server.start(12345, true) + started = true + result.success(true) + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } +} diff --git a/cw_mweb/android/src/test/kotlin/com/cakewallet/cw_mweb/CwMwebPluginTest.kt b/cw_mweb/android/src/test/kotlin/com/cakewallet/cw_mweb/CwMwebPluginTest.kt new file mode 100644 index 000000000..baa81332f --- /dev/null +++ b/cw_mweb/android/src/test/kotlin/com/cakewallet/cw_mweb/CwMwebPluginTest.kt @@ -0,0 +1,27 @@ +package com.cakewallet.mweb + +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import kotlin.test.Test +import org.mockito.Mockito + +/* + * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. + * + * Once you have built the plugin's example app, you can run these tests from the command + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or + * you can run them directly from IDEs that support JUnit such as Android Studio. + */ + +internal class CwMwebPluginTest { + @Test + fun onMethodCall_getPlatformVersion_returnsExpectedValue() { + val plugin = CwMwebPlugin() + + val call = MethodCall("getPlatformVersion", null) + val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) + plugin.onMethodCall(call, mockResult) + + Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) + } +} diff --git a/cw_mweb/ios/.gitignore b/cw_mweb/ios/.gitignore new file mode 100644 index 000000000..0c885071e --- /dev/null +++ b/cw_mweb/ios/.gitignore @@ -0,0 +1,38 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/cw_mweb/ios/Assets/.gitkeep b/cw_mweb/ios/Assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift new file mode 100644 index 000000000..d742aab61 --- /dev/null +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -0,0 +1,19 @@ +import Flutter +import UIKit + +public class CwMwebPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "cw_mweb", binaryMessenger: registrar.messenger()) + let instance = CwMwebPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/cw_mweb/ios/cw_mweb.podspec b/cw_mweb/ios/cw_mweb.podspec new file mode 100644 index 000000000..cd0600fa1 --- /dev/null +++ b/cw_mweb/ios/cw_mweb.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint cw_mweb.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'cw_mweb' + s.version = '0.0.1' + s.summary = 'A new Flutter plugin project.' + s.description = <<-DESC +A new Flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '11.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.swift_version = '5.0' +end diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart new file mode 100644 index 000000000..e73887aaf --- /dev/null +++ b/cw_mweb/lib/cw_mweb.dart @@ -0,0 +1,17 @@ +import 'package:grpc/grpc.dart'; +import 'cw_mweb_platform_interface.dart'; +import 'mwebd.pbgrpc.dart'; + +class CwMweb { + static Future start(String dataDir) { + return CwMwebPlatform.instance.start(dataDir); + } + + static stub() { + final channel = ClientChannel('127.0.0.1', + port: 12345, + options: const ChannelOptions( + credentials: ChannelCredentials.insecure())); + return RpcClient(channel); + } +} diff --git a/cw_mweb/lib/cw_mweb_method_channel.dart b/cw_mweb/lib/cw_mweb_method_channel.dart new file mode 100644 index 000000000..112dcfaa7 --- /dev/null +++ b/cw_mweb/lib/cw_mweb_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'cw_mweb_platform_interface.dart'; + +/// An implementation of [CwMwebPlatform] that uses method channels. +class MethodChannelCwMweb extends CwMwebPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('cw_mweb'); + + @override + Future start(String dataDir) async { + final result = await methodChannel.invokeMethod('start', {'dataDir': dataDir}); + return result; + } +} diff --git a/cw_mweb/lib/cw_mweb_platform_interface.dart b/cw_mweb/lib/cw_mweb_platform_interface.dart new file mode 100644 index 000000000..ce518402f --- /dev/null +++ b/cw_mweb/lib/cw_mweb_platform_interface.dart @@ -0,0 +1,29 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'cw_mweb_method_channel.dart'; + +abstract class CwMwebPlatform extends PlatformInterface { + /// Constructs a CwMwebPlatform. + CwMwebPlatform() : super(token: _token); + + static final Object _token = Object(); + + static CwMwebPlatform _instance = MethodChannelCwMweb(); + + /// The default instance of [CwMwebPlatform] to use. + /// + /// Defaults to [MethodChannelCwMweb]. + static CwMwebPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [CwMwebPlatform] when + /// they register themselves. + static set instance(CwMwebPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future start(String dataDir) { + throw UnimplementedError('start() has not been implemented.'); + } +} diff --git a/cw_mweb/lib/mwebd.pb.dart b/cw_mweb/lib/mwebd.pb.dart new file mode 100644 index 000000000..8d139d7eb --- /dev/null +++ b/cw_mweb/lib/mwebd.pb.dart @@ -0,0 +1,773 @@ +// +// Generated code. Do not modify. +// source: mwebd.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:fixnum/fixnum.dart' as $fixnum; +import 'package:protobuf/protobuf.dart' as $pb; + +class StatusRequest extends $pb.GeneratedMessage { + factory StatusRequest() => create(); + StatusRequest._() : super(); + factory StatusRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory StatusRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StatusRequest', createEmptyInstance: create) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + StatusRequest clone() => StatusRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + StatusRequest copyWith(void Function(StatusRequest) updates) => super.copyWith((message) => updates(message as StatusRequest)) as StatusRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static StatusRequest create() => StatusRequest._(); + StatusRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static StatusRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static StatusRequest? _defaultInstance; +} + +class StatusResponse extends $pb.GeneratedMessage { + factory StatusResponse({ + $core.int? blockHeaderHeight, + $core.int? mwebHeaderHeight, + $core.int? mwebUtxosHeight, + }) { + final $result = create(); + if (blockHeaderHeight != null) { + $result.blockHeaderHeight = blockHeaderHeight; + } + if (mwebHeaderHeight != null) { + $result.mwebHeaderHeight = mwebHeaderHeight; + } + if (mwebUtxosHeight != null) { + $result.mwebUtxosHeight = mwebUtxosHeight; + } + return $result; + } + StatusResponse._() : super(); + factory StatusResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory StatusResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StatusResponse', createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'blockHeaderHeight', $pb.PbFieldType.O3) + ..a<$core.int>(2, _omitFieldNames ? '' : 'mwebHeaderHeight', $pb.PbFieldType.O3) + ..a<$core.int>(3, _omitFieldNames ? '' : 'mwebUtxosHeight', $pb.PbFieldType.O3) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + StatusResponse clone() => StatusResponse()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + StatusResponse copyWith(void Function(StatusResponse) updates) => super.copyWith((message) => updates(message as StatusResponse)) as StatusResponse; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static StatusResponse create() => StatusResponse._(); + StatusResponse createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static StatusResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static StatusResponse? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get blockHeaderHeight => $_getIZ(0); + @$pb.TagNumber(1) + set blockHeaderHeight($core.int v) { $_setSignedInt32(0, v); } + @$pb.TagNumber(1) + $core.bool hasBlockHeaderHeight() => $_has(0); + @$pb.TagNumber(1) + void clearBlockHeaderHeight() => clearField(1); + + @$pb.TagNumber(2) + $core.int get mwebHeaderHeight => $_getIZ(1); + @$pb.TagNumber(2) + set mwebHeaderHeight($core.int v) { $_setSignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasMwebHeaderHeight() => $_has(1); + @$pb.TagNumber(2) + void clearMwebHeaderHeight() => clearField(2); + + @$pb.TagNumber(3) + $core.int get mwebUtxosHeight => $_getIZ(2); + @$pb.TagNumber(3) + set mwebUtxosHeight($core.int v) { $_setSignedInt32(2, v); } + @$pb.TagNumber(3) + $core.bool hasMwebUtxosHeight() => $_has(2); + @$pb.TagNumber(3) + void clearMwebUtxosHeight() => clearField(3); +} + +class UtxosRequest extends $pb.GeneratedMessage { + factory UtxosRequest({ + $core.int? fromHeight, + $core.List<$core.int>? scanSecret, + }) { + final $result = create(); + if (fromHeight != null) { + $result.fromHeight = fromHeight; + } + if (scanSecret != null) { + $result.scanSecret = scanSecret; + } + return $result; + } + UtxosRequest._() : super(); + factory UtxosRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory UtxosRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'UtxosRequest', createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'fromHeight', $pb.PbFieldType.O3) + ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'scanSecret', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + UtxosRequest clone() => UtxosRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + UtxosRequest copyWith(void Function(UtxosRequest) updates) => super.copyWith((message) => updates(message as UtxosRequest)) as UtxosRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static UtxosRequest create() => UtxosRequest._(); + UtxosRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static UtxosRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static UtxosRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get fromHeight => $_getIZ(0); + @$pb.TagNumber(1) + set fromHeight($core.int v) { $_setSignedInt32(0, v); } + @$pb.TagNumber(1) + $core.bool hasFromHeight() => $_has(0); + @$pb.TagNumber(1) + void clearFromHeight() => clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get scanSecret => $_getN(1); + @$pb.TagNumber(2) + set scanSecret($core.List<$core.int> v) { $_setBytes(1, v); } + @$pb.TagNumber(2) + $core.bool hasScanSecret() => $_has(1); + @$pb.TagNumber(2) + void clearScanSecret() => clearField(2); +} + +class Utxo extends $pb.GeneratedMessage { + factory Utxo({ + $core.int? height, + $fixnum.Int64? value, + $core.String? address, + $core.String? outputId, + }) { + final $result = create(); + if (height != null) { + $result.height = height; + } + if (value != null) { + $result.value = value; + } + if (address != null) { + $result.address = address; + } + if (outputId != null) { + $result.outputId = outputId; + } + return $result; + } + Utxo._() : super(); + factory Utxo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory Utxo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Utxo', createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'height', $pb.PbFieldType.O3) + ..a<$fixnum.Int64>(2, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) + ..aOS(3, _omitFieldNames ? '' : 'address') + ..aOS(4, _omitFieldNames ? '' : 'outputId') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + Utxo clone() => Utxo()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + Utxo copyWith(void Function(Utxo) updates) => super.copyWith((message) => updates(message as Utxo)) as Utxo; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Utxo create() => Utxo._(); + Utxo createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static Utxo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Utxo? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get height => $_getIZ(0); + @$pb.TagNumber(1) + set height($core.int v) { $_setSignedInt32(0, v); } + @$pb.TagNumber(1) + $core.bool hasHeight() => $_has(0); + @$pb.TagNumber(1) + void clearHeight() => clearField(1); + + @$pb.TagNumber(2) + $fixnum.Int64 get value => $_getI64(1); + @$pb.TagNumber(2) + set value($fixnum.Int64 v) { $_setInt64(1, v); } + @$pb.TagNumber(2) + $core.bool hasValue() => $_has(1); + @$pb.TagNumber(2) + void clearValue() => clearField(2); + + @$pb.TagNumber(3) + $core.String get address => $_getSZ(2); + @$pb.TagNumber(3) + set address($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasAddress() => $_has(2); + @$pb.TagNumber(3) + void clearAddress() => clearField(3); + + @$pb.TagNumber(4) + $core.String get outputId => $_getSZ(3); + @$pb.TagNumber(4) + set outputId($core.String v) { $_setString(3, v); } + @$pb.TagNumber(4) + $core.bool hasOutputId() => $_has(3); + @$pb.TagNumber(4) + void clearOutputId() => clearField(4); +} + +class AddressRequest extends $pb.GeneratedMessage { + factory AddressRequest({ + $core.int? fromIndex, + $core.int? toIndex, + $core.List<$core.int>? scanSecret, + $core.List<$core.int>? spendPubkey, + }) { + final $result = create(); + if (fromIndex != null) { + $result.fromIndex = fromIndex; + } + if (toIndex != null) { + $result.toIndex = toIndex; + } + if (scanSecret != null) { + $result.scanSecret = scanSecret; + } + if (spendPubkey != null) { + $result.spendPubkey = spendPubkey; + } + return $result; + } + AddressRequest._() : super(); + factory AddressRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory AddressRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'AddressRequest', createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'fromIndex', $pb.PbFieldType.OU3) + ..a<$core.int>(2, _omitFieldNames ? '' : 'toIndex', $pb.PbFieldType.OU3) + ..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'scanSecret', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(4, _omitFieldNames ? '' : 'spendPubkey', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + AddressRequest clone() => AddressRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + AddressRequest copyWith(void Function(AddressRequest) updates) => super.copyWith((message) => updates(message as AddressRequest)) as AddressRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AddressRequest create() => AddressRequest._(); + AddressRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static AddressRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static AddressRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get fromIndex => $_getIZ(0); + @$pb.TagNumber(1) + set fromIndex($core.int v) { $_setUnsignedInt32(0, v); } + @$pb.TagNumber(1) + $core.bool hasFromIndex() => $_has(0); + @$pb.TagNumber(1) + void clearFromIndex() => clearField(1); + + @$pb.TagNumber(2) + $core.int get toIndex => $_getIZ(1); + @$pb.TagNumber(2) + set toIndex($core.int v) { $_setUnsignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasToIndex() => $_has(1); + @$pb.TagNumber(2) + void clearToIndex() => clearField(2); + + @$pb.TagNumber(3) + $core.List<$core.int> get scanSecret => $_getN(2); + @$pb.TagNumber(3) + set scanSecret($core.List<$core.int> v) { $_setBytes(2, v); } + @$pb.TagNumber(3) + $core.bool hasScanSecret() => $_has(2); + @$pb.TagNumber(3) + void clearScanSecret() => clearField(3); + + @$pb.TagNumber(4) + $core.List<$core.int> get spendPubkey => $_getN(3); + @$pb.TagNumber(4) + set spendPubkey($core.List<$core.int> v) { $_setBytes(3, v); } + @$pb.TagNumber(4) + $core.bool hasSpendPubkey() => $_has(3); + @$pb.TagNumber(4) + void clearSpendPubkey() => clearField(4); +} + +class AddressResponse extends $pb.GeneratedMessage { + factory AddressResponse({ + $core.Iterable<$core.String>? address, + }) { + final $result = create(); + if (address != null) { + $result.address.addAll(address); + } + return $result; + } + AddressResponse._() : super(); + factory AddressResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory AddressResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'AddressResponse', createEmptyInstance: create) + ..pPS(1, _omitFieldNames ? '' : 'address') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + AddressResponse clone() => AddressResponse()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + AddressResponse copyWith(void Function(AddressResponse) updates) => super.copyWith((message) => updates(message as AddressResponse)) as AddressResponse; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AddressResponse create() => AddressResponse._(); + AddressResponse createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static AddressResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static AddressResponse? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.String> get address => $_getList(0); +} + +class SpentRequest extends $pb.GeneratedMessage { + factory SpentRequest({ + $core.Iterable<$core.String>? outputId, + }) { + final $result = create(); + if (outputId != null) { + $result.outputId.addAll(outputId); + } + return $result; + } + SpentRequest._() : super(); + factory SpentRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory SpentRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SpentRequest', createEmptyInstance: create) + ..pPS(1, _omitFieldNames ? '' : 'outputId') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + SpentRequest clone() => SpentRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + SpentRequest copyWith(void Function(SpentRequest) updates) => super.copyWith((message) => updates(message as SpentRequest)) as SpentRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SpentRequest create() => SpentRequest._(); + SpentRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static SpentRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static SpentRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.String> get outputId => $_getList(0); +} + +class SpentResponse extends $pb.GeneratedMessage { + factory SpentResponse({ + $core.Iterable<$core.String>? outputId, + }) { + final $result = create(); + if (outputId != null) { + $result.outputId.addAll(outputId); + } + return $result; + } + SpentResponse._() : super(); + factory SpentResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory SpentResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SpentResponse', createEmptyInstance: create) + ..pPS(1, _omitFieldNames ? '' : 'outputId') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + SpentResponse clone() => SpentResponse()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + SpentResponse copyWith(void Function(SpentResponse) updates) => super.copyWith((message) => updates(message as SpentResponse)) as SpentResponse; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SpentResponse create() => SpentResponse._(); + SpentResponse createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static SpentResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static SpentResponse? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.String> get outputId => $_getList(0); +} + +class CreateRequest extends $pb.GeneratedMessage { + factory CreateRequest({ + $core.List<$core.int>? rawTx, + $core.List<$core.int>? scanSecret, + $core.List<$core.int>? spendSecret, + $fixnum.Int64? feeRatePerKb, + $core.bool? dryRun, + }) { + final $result = create(); + if (rawTx != null) { + $result.rawTx = rawTx; + } + if (scanSecret != null) { + $result.scanSecret = scanSecret; + } + if (spendSecret != null) { + $result.spendSecret = spendSecret; + } + if (feeRatePerKb != null) { + $result.feeRatePerKb = feeRatePerKb; + } + if (dryRun != null) { + $result.dryRun = dryRun; + } + return $result; + } + CreateRequest._() : super(); + factory CreateRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory CreateRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CreateRequest', createEmptyInstance: create) + ..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'rawTx', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'scanSecret', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'spendSecret', $pb.PbFieldType.OY) + ..a<$fixnum.Int64>(4, _omitFieldNames ? '' : 'feeRatePerKb', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) + ..aOB(5, _omitFieldNames ? '' : 'dryRun') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + CreateRequest clone() => CreateRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + CreateRequest copyWith(void Function(CreateRequest) updates) => super.copyWith((message) => updates(message as CreateRequest)) as CreateRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static CreateRequest create() => CreateRequest._(); + CreateRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static CreateRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static CreateRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.int> get rawTx => $_getN(0); + @$pb.TagNumber(1) + set rawTx($core.List<$core.int> v) { $_setBytes(0, v); } + @$pb.TagNumber(1) + $core.bool hasRawTx() => $_has(0); + @$pb.TagNumber(1) + void clearRawTx() => clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get scanSecret => $_getN(1); + @$pb.TagNumber(2) + set scanSecret($core.List<$core.int> v) { $_setBytes(1, v); } + @$pb.TagNumber(2) + $core.bool hasScanSecret() => $_has(1); + @$pb.TagNumber(2) + void clearScanSecret() => clearField(2); + + @$pb.TagNumber(3) + $core.List<$core.int> get spendSecret => $_getN(2); + @$pb.TagNumber(3) + set spendSecret($core.List<$core.int> v) { $_setBytes(2, v); } + @$pb.TagNumber(3) + $core.bool hasSpendSecret() => $_has(2); + @$pb.TagNumber(3) + void clearSpendSecret() => clearField(3); + + @$pb.TagNumber(4) + $fixnum.Int64 get feeRatePerKb => $_getI64(3); + @$pb.TagNumber(4) + set feeRatePerKb($fixnum.Int64 v) { $_setInt64(3, v); } + @$pb.TagNumber(4) + $core.bool hasFeeRatePerKb() => $_has(3); + @$pb.TagNumber(4) + void clearFeeRatePerKb() => clearField(4); + + @$pb.TagNumber(5) + $core.bool get dryRun => $_getBF(4); + @$pb.TagNumber(5) + set dryRun($core.bool v) { $_setBool(4, v); } + @$pb.TagNumber(5) + $core.bool hasDryRun() => $_has(4); + @$pb.TagNumber(5) + void clearDryRun() => clearField(5); +} + +class CreateResponse extends $pb.GeneratedMessage { + factory CreateResponse({ + $core.List<$core.int>? rawTx, + $core.Iterable<$core.String>? outputId, + }) { + final $result = create(); + if (rawTx != null) { + $result.rawTx = rawTx; + } + if (outputId != null) { + $result.outputId.addAll(outputId); + } + return $result; + } + CreateResponse._() : super(); + factory CreateResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory CreateResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CreateResponse', createEmptyInstance: create) + ..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'rawTx', $pb.PbFieldType.OY) + ..pPS(2, _omitFieldNames ? '' : 'outputId') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + CreateResponse clone() => CreateResponse()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + CreateResponse copyWith(void Function(CreateResponse) updates) => super.copyWith((message) => updates(message as CreateResponse)) as CreateResponse; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static CreateResponse create() => CreateResponse._(); + CreateResponse createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static CreateResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static CreateResponse? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.int> get rawTx => $_getN(0); + @$pb.TagNumber(1) + set rawTx($core.List<$core.int> v) { $_setBytes(0, v); } + @$pb.TagNumber(1) + $core.bool hasRawTx() => $_has(0); + @$pb.TagNumber(1) + void clearRawTx() => clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.String> get outputId => $_getList(1); +} + +class BroadcastRequest extends $pb.GeneratedMessage { + factory BroadcastRequest({ + $core.List<$core.int>? rawTx, + }) { + final $result = create(); + if (rawTx != null) { + $result.rawTx = rawTx; + } + return $result; + } + BroadcastRequest._() : super(); + factory BroadcastRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory BroadcastRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BroadcastRequest', createEmptyInstance: create) + ..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'rawTx', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + BroadcastRequest clone() => BroadcastRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + BroadcastRequest copyWith(void Function(BroadcastRequest) updates) => super.copyWith((message) => updates(message as BroadcastRequest)) as BroadcastRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static BroadcastRequest create() => BroadcastRequest._(); + BroadcastRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static BroadcastRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static BroadcastRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.int> get rawTx => $_getN(0); + @$pb.TagNumber(1) + set rawTx($core.List<$core.int> v) { $_setBytes(0, v); } + @$pb.TagNumber(1) + $core.bool hasRawTx() => $_has(0); + @$pb.TagNumber(1) + void clearRawTx() => clearField(1); +} + +class BroadcastResponse extends $pb.GeneratedMessage { + factory BroadcastResponse({ + $core.String? txid, + }) { + final $result = create(); + if (txid != null) { + $result.txid = txid; + } + return $result; + } + BroadcastResponse._() : super(); + factory BroadcastResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory BroadcastResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BroadcastResponse', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'txid') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + BroadcastResponse clone() => BroadcastResponse()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + BroadcastResponse copyWith(void Function(BroadcastResponse) updates) => super.copyWith((message) => updates(message as BroadcastResponse)) as BroadcastResponse; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static BroadcastResponse create() => BroadcastResponse._(); + BroadcastResponse createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static BroadcastResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static BroadcastResponse? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get txid => $_getSZ(0); + @$pb.TagNumber(1) + set txid($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasTxid() => $_has(0); + @$pb.TagNumber(1) + void clearTxid() => clearField(1); +} + + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/cw_mweb/lib/mwebd.pbgrpc.dart b/cw_mweb/lib/mwebd.pbgrpc.dart new file mode 100644 index 000000000..6bc48cfdf --- /dev/null +++ b/cw_mweb/lib/mwebd.pbgrpc.dart @@ -0,0 +1,159 @@ +// +// Generated code. Do not modify. +// source: mwebd.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:async' as $async; +import 'dart:core' as $core; + +import 'package:grpc/service_api.dart' as $grpc; +import 'package:protobuf/protobuf.dart' as $pb; + +import 'mwebd.pb.dart' as $0; + +export 'mwebd.pb.dart'; + +@$pb.GrpcServiceName('Rpc') +class RpcClient extends $grpc.Client { + static final _$status = $grpc.ClientMethod<$0.StatusRequest, $0.StatusResponse>( + '/Rpc/Status', + ($0.StatusRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.StatusResponse.fromBuffer(value)); + static final _$utxos = $grpc.ClientMethod<$0.UtxosRequest, $0.Utxo>( + '/Rpc/Utxos', + ($0.UtxosRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.Utxo.fromBuffer(value)); + static final _$addresses = $grpc.ClientMethod<$0.AddressRequest, $0.AddressResponse>( + '/Rpc/Addresses', + ($0.AddressRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.AddressResponse.fromBuffer(value)); + static final _$spent = $grpc.ClientMethod<$0.SpentRequest, $0.SpentResponse>( + '/Rpc/Spent', + ($0.SpentRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.SpentResponse.fromBuffer(value)); + static final _$create = $grpc.ClientMethod<$0.CreateRequest, $0.CreateResponse>( + '/Rpc/Create', + ($0.CreateRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.CreateResponse.fromBuffer(value)); + static final _$broadcast = $grpc.ClientMethod<$0.BroadcastRequest, $0.BroadcastResponse>( + '/Rpc/Broadcast', + ($0.BroadcastRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.BroadcastResponse.fromBuffer(value)); + + RpcClient($grpc.ClientChannel channel, + {$grpc.CallOptions? options, + $core.Iterable<$grpc.ClientInterceptor>? interceptors}) + : super(channel, options: options, + interceptors: interceptors); + + $grpc.ResponseFuture<$0.StatusResponse> status($0.StatusRequest request, {$grpc.CallOptions? options}) { + return $createUnaryCall(_$status, request, options: options); + } + + $grpc.ResponseStream<$0.Utxo> utxos($0.UtxosRequest request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$utxos, $async.Stream.fromIterable([request]), options: options); + } + + $grpc.ResponseFuture<$0.AddressResponse> addresses($0.AddressRequest request, {$grpc.CallOptions? options}) { + return $createUnaryCall(_$addresses, request, options: options); + } + + $grpc.ResponseFuture<$0.SpentResponse> spent($0.SpentRequest request, {$grpc.CallOptions? options}) { + return $createUnaryCall(_$spent, request, options: options); + } + + $grpc.ResponseFuture<$0.CreateResponse> create($0.CreateRequest request, {$grpc.CallOptions? options}) { + return $createUnaryCall(_$create, request, options: options); + } + + $grpc.ResponseFuture<$0.BroadcastResponse> broadcast($0.BroadcastRequest request, {$grpc.CallOptions? options}) { + return $createUnaryCall(_$broadcast, request, options: options); + } +} + +@$pb.GrpcServiceName('Rpc') +abstract class RpcServiceBase extends $grpc.Service { + $core.String get $name => 'Rpc'; + + RpcServiceBase() { + $addMethod($grpc.ServiceMethod<$0.StatusRequest, $0.StatusResponse>( + 'Status', + status_Pre, + false, + false, + ($core.List<$core.int> value) => $0.StatusRequest.fromBuffer(value), + ($0.StatusResponse value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.UtxosRequest, $0.Utxo>( + 'Utxos', + utxos_Pre, + false, + true, + ($core.List<$core.int> value) => $0.UtxosRequest.fromBuffer(value), + ($0.Utxo value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.AddressRequest, $0.AddressResponse>( + 'Addresses', + addresses_Pre, + false, + false, + ($core.List<$core.int> value) => $0.AddressRequest.fromBuffer(value), + ($0.AddressResponse value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.SpentRequest, $0.SpentResponse>( + 'Spent', + spent_Pre, + false, + false, + ($core.List<$core.int> value) => $0.SpentRequest.fromBuffer(value), + ($0.SpentResponse value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.CreateRequest, $0.CreateResponse>( + 'Create', + create_Pre, + false, + false, + ($core.List<$core.int> value) => $0.CreateRequest.fromBuffer(value), + ($0.CreateResponse value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.BroadcastRequest, $0.BroadcastResponse>( + 'Broadcast', + broadcast_Pre, + false, + false, + ($core.List<$core.int> value) => $0.BroadcastRequest.fromBuffer(value), + ($0.BroadcastResponse value) => value.writeToBuffer())); + } + + $async.Future<$0.StatusResponse> status_Pre($grpc.ServiceCall call, $async.Future<$0.StatusRequest> request) async { + return status(call, await request); + } + + $async.Stream<$0.Utxo> utxos_Pre($grpc.ServiceCall call, $async.Future<$0.UtxosRequest> request) async* { + yield* utxos(call, await request); + } + + $async.Future<$0.AddressResponse> addresses_Pre($grpc.ServiceCall call, $async.Future<$0.AddressRequest> request) async { + return addresses(call, await request); + } + + $async.Future<$0.SpentResponse> spent_Pre($grpc.ServiceCall call, $async.Future<$0.SpentRequest> request) async { + return spent(call, await request); + } + + $async.Future<$0.CreateResponse> create_Pre($grpc.ServiceCall call, $async.Future<$0.CreateRequest> request) async { + return create(call, await request); + } + + $async.Future<$0.BroadcastResponse> broadcast_Pre($grpc.ServiceCall call, $async.Future<$0.BroadcastRequest> request) async { + return broadcast(call, await request); + } + + $async.Future<$0.StatusResponse> status($grpc.ServiceCall call, $0.StatusRequest request); + $async.Stream<$0.Utxo> utxos($grpc.ServiceCall call, $0.UtxosRequest request); + $async.Future<$0.AddressResponse> addresses($grpc.ServiceCall call, $0.AddressRequest request); + $async.Future<$0.SpentResponse> spent($grpc.ServiceCall call, $0.SpentRequest request); + $async.Future<$0.CreateResponse> create($grpc.ServiceCall call, $0.CreateRequest request); + $async.Future<$0.BroadcastResponse> broadcast($grpc.ServiceCall call, $0.BroadcastRequest request); +} diff --git a/cw_mweb/macos/Classes/CwMwebPlugin.swift b/cw_mweb/macos/Classes/CwMwebPlugin.swift new file mode 100644 index 000000000..9c0dabd40 --- /dev/null +++ b/cw_mweb/macos/Classes/CwMwebPlugin.swift @@ -0,0 +1,19 @@ +import Cocoa +import FlutterMacOS + +public class CwMwebPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "cw_mweb", binaryMessenger: registrar.messenger) + let instance = CwMwebPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString) + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/cw_mweb/macos/cw_mweb.podspec b/cw_mweb/macos/cw_mweb.podspec new file mode 100644 index 000000000..8fadcced9 --- /dev/null +++ b/cw_mweb/macos/cw_mweb.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint cw_mweb.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'cw_mweb' + s.version = '0.0.1' + s.summary = 'A new Flutter plugin project.' + s.description = <<-DESC +A new Flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + + s.platform = :osx, '10.11' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' +end diff --git a/cw_mweb/pubspec.yaml b/cw_mweb/pubspec.yaml new file mode 100644 index 000000000..5416963d8 --- /dev/null +++ b/cw_mweb/pubspec.yaml @@ -0,0 +1,74 @@ +name: cw_mweb +description: A new Flutter plugin project. +version: 0.0.1 +homepage: + +environment: + sdk: '>=3.0.6 <4.0.0' + flutter: ">=3.3.0" + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: com.cakewallet.mweb + pluginClass: CwMwebPlugin + ios: + pluginClass: CwMwebPlugin + macos: + pluginClass: CwMwebPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/cw_mweb/test/cw_mweb_method_channel_test.dart b/cw_mweb/test/cw_mweb_method_channel_test.dart new file mode 100644 index 000000000..bb0523758 --- /dev/null +++ b/cw_mweb/test/cw_mweb_method_channel_test.dart @@ -0,0 +1,27 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:cw_mweb/cw_mweb_method_channel.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + MethodChannelCwMweb platform = MethodChannelCwMweb(); + const MethodChannel channel = MethodChannel('cw_mweb'); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( + channel, + (MethodCall methodCall) async { + return '42'; + }, + ); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} diff --git a/cw_mweb/test/cw_mweb_test.dart b/cw_mweb/test/cw_mweb_test.dart new file mode 100644 index 000000000..3677659a9 --- /dev/null +++ b/cw_mweb/test/cw_mweb_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:cw_mweb/cw_mweb.dart'; +import 'package:cw_mweb/cw_mweb_platform_interface.dart'; +import 'package:cw_mweb/cw_mweb_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockCwMwebPlatform + with MockPlatformInterfaceMixin + implements CwMwebPlatform { + + @override + Future getPlatformVersion() => Future.value('42'); +} + +void main() { + final CwMwebPlatform initialPlatform = CwMwebPlatform.instance; + + test('$MethodChannelCwMweb is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + CwMweb cwMwebPlugin = CwMweb(); + MockCwMwebPlatform fakePlatform = MockCwMwebPlatform(); + CwMwebPlatform.instance = fakePlatform; + + expect(await cwMwebPlugin.getPlatformVersion(), '42'); + }); +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 75a78404f..e309e3142 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,6 +7,7 @@ import Foundation import connectivity_plus import cw_monero +import cw_mweb import device_info_plus import devicelocale import flutter_inappwebview_macos @@ -23,6 +24,7 @@ import wakelock_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin")) + CwMwebPlugin.register(with: registry.registrar(forPlugin: "CwMwebPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) From a3aebbdb78efed727b7e254b3311ef7231767855 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Sun, 21 Apr 2024 12:19:44 +0100 Subject: [PATCH 003/100] Sync status --- cw_bitcoin/lib/litecoin_wallet.dart | 18 +++++++++++++++--- cw_mweb/.gitignore | 1 - cw_mweb/android/.gitignore | 1 + .../kotlin/com/cakewallet/mweb/CwMwebPlugin.kt | 7 +------ cw_mweb/lib/cw_mweb.dart | 13 +++++++------ cw_mweb/lib/cw_mweb_method_channel.dart | 4 ++-- cw_mweb/lib/cw_mweb_platform_interface.dart | 2 +- 7 files changed, 27 insertions(+), 19 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index f218ddd5d..628391eed 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -3,6 +3,7 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/sync_status.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_bitcoin/litecoin_wallet_addresses.dart'; import 'package:cw_core/transaction_priority.dart'; @@ -109,12 +110,23 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @action @override Future startSync() async { - super.startSync(); + await super.startSync(); final stub = CwMweb.stub(); Timer.periodic( - const Duration(seconds: 1), (timer) async { + const Duration(milliseconds: 1500), (timer) async { + final height = await electrumClient.getCurrentBlockChainTip() ?? 0; final resp = await stub.status(StatusRequest()); - print(resp.blockHeaderHeight); + if (resp.blockHeaderHeight < height) { + int h = resp.blockHeaderHeight; + syncStatus = SyncingSyncStatus(height - h, h / height); + } else if (resp.mwebHeaderHeight < height) { + int h = resp.mwebHeaderHeight; + syncStatus = SyncingSyncStatus(height - h, h / height); + } else if (resp.mwebUtxosHeight < height) { + syncStatus = SyncingSyncStatus(1, 0.999); + } else { + syncStatus = SyncedSyncStatus(); + } }); } diff --git a/cw_mweb/.gitignore b/cw_mweb/.gitignore index 8959f5d70..96486fd93 100644 --- a/cw_mweb/.gitignore +++ b/cw_mweb/.gitignore @@ -28,4 +28,3 @@ migrate_working_dir/ .dart_tool/ .packages build/ -libs/ diff --git a/cw_mweb/android/.gitignore b/cw_mweb/android/.gitignore index 161bdcdaf..881f3d95c 100644 --- a/cw_mweb/android/.gitignore +++ b/cw_mweb/android/.gitignore @@ -6,4 +6,5 @@ .DS_Store /build /captures +/libs .cxx diff --git a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt index 394b607ab..cf194417b 100644 --- a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt +++ b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt @@ -17,7 +17,6 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { /// This local reference serves to register the plugin with the Flutter Engine and unregister it /// when the Flutter Engine is detached from the Activity private lateinit var channel : MethodChannel - private var started = false override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "cw_mweb") @@ -26,12 +25,8 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { if (call.method == "start") { - if (started) return val dataDir = call.argument("dataDir") ?: "" - val server = Mwebd.newServer("mainnet", dataDir, "") - server.start(12345, true) - started = true - result.success(true) + result.success(Mwebd.newServer("", dataDir, "").start(0)) } else { result.notImplemented() } diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index e73887aaf..fb3960ab0 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -3,15 +3,16 @@ import 'cw_mweb_platform_interface.dart'; import 'mwebd.pbgrpc.dart'; class CwMweb { - static Future start(String dataDir) { - return CwMwebPlatform.instance.start(dataDir); + static var port; + + static start(String dataDir) async { + port = port ?? await CwMwebPlatform.instance.start(dataDir); } static stub() { - final channel = ClientChannel('127.0.0.1', - port: 12345, + return RpcClient(ClientChannel('127.0.0.1', + port: port, options: const ChannelOptions( - credentials: ChannelCredentials.insecure())); - return RpcClient(channel); + credentials: ChannelCredentials.insecure()))); } } diff --git a/cw_mweb/lib/cw_mweb_method_channel.dart b/cw_mweb/lib/cw_mweb_method_channel.dart index 112dcfaa7..cc880c6df 100644 --- a/cw_mweb/lib/cw_mweb_method_channel.dart +++ b/cw_mweb/lib/cw_mweb_method_channel.dart @@ -10,8 +10,8 @@ class MethodChannelCwMweb extends CwMwebPlatform { final methodChannel = const MethodChannel('cw_mweb'); @override - Future start(String dataDir) async { - final result = await methodChannel.invokeMethod('start', {'dataDir': dataDir}); + Future start(String dataDir) async { + final result = await methodChannel.invokeMethod('start', {'dataDir': dataDir}); return result; } } diff --git a/cw_mweb/lib/cw_mweb_platform_interface.dart b/cw_mweb/lib/cw_mweb_platform_interface.dart index ce518402f..974e07284 100644 --- a/cw_mweb/lib/cw_mweb_platform_interface.dart +++ b/cw_mweb/lib/cw_mweb_platform_interface.dart @@ -23,7 +23,7 @@ abstract class CwMwebPlatform extends PlatformInterface { _instance = instance; } - Future start(String dataDir) { + Future start(String dataDir) { throw UnimplementedError('start() has not been implemented.'); } } From dbc005dcf3a704b922d77af7c42c1530d773c4ba Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Sun, 21 Apr 2024 19:58:45 +0100 Subject: [PATCH 004/100] Fix stub creation --- cw_bitcoin/lib/litecoin_wallet.dart | 2 +- cw_bitcoin/lib/litecoin_wallet_service.dart | 6 ------ cw_mweb/lib/cw_mweb.dart | 13 ++++++------- cw_mweb/pubspec.yaml | 2 ++ 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 628391eed..b6b996b27 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -111,7 +111,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future startSync() async { await super.startSync(); - final stub = CwMweb.stub(); + final stub = await CwMweb.stub(); Timer.periodic( const Duration(milliseconds: 1500), (timer) async { final height = await electrumClient.getCurrentBlockChainTip() ?? 0; diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index 7f2066c30..ee3b0e628 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'package:path_provider/path_provider.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:hive/hive.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; @@ -11,7 +10,6 @@ import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_base.dart'; -import 'package:cw_mweb/cw_mweb.dart'; import 'package:collection/collection.dart'; class LitecoinWalletService extends WalletService< @@ -28,8 +26,6 @@ class LitecoinWalletService extends WalletService< @override Future create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async { - final appDir = await getApplicationSupportDirectory(); - await CwMweb.start(appDir.path); final wallet = await LitecoinWalletBase.create( mnemonic: await generateMnemonic(), password: credentials.password!, @@ -47,8 +43,6 @@ class LitecoinWalletService extends WalletService< @override Future openWallet(String name, String password) async { - final appDir = await getApplicationSupportDirectory(); - await CwMweb.start(appDir.path); final walletInfo = walletInfoSource.values.firstWhereOrNull( (info) => info.id == WalletBase.idFor(name, getType()))!; diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index fb3960ab0..a54295673 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -1,17 +1,16 @@ import 'package:grpc/grpc.dart'; +import 'package:path_provider/path_provider.dart'; import 'cw_mweb_platform_interface.dart'; import 'mwebd.pbgrpc.dart'; class CwMweb { - static var port; + static Future? port; - static start(String dataDir) async { - port = port ?? await CwMwebPlatform.instance.start(dataDir); - } - - static stub() { + static Future stub() async { + final appDir = await getApplicationSupportDirectory(); + port ??= CwMwebPlatform.instance.start(appDir.path); return RpcClient(ClientChannel('127.0.0.1', - port: port, + port: await port ?? 0, options: const ChannelOptions( credentials: ChannelCredentials.insecure()))); } diff --git a/cw_mweb/pubspec.yaml b/cw_mweb/pubspec.yaml index 5416963d8..cfe43c70b 100644 --- a/cw_mweb/pubspec.yaml +++ b/cw_mweb/pubspec.yaml @@ -10,6 +10,8 @@ environment: dependencies: flutter: sdk: flutter + grpc: ^3.2.4 + path_provider: ^2.1.2 plugin_platform_interface: ^2.0.2 dev_dependencies: From 29238effdf17fa5047e8f30c232cab913b1f2482 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Mon, 22 Apr 2024 11:45:16 +0100 Subject: [PATCH 005/100] Generate MWEB addresses --- .../lib/bitcoin_receive_page_option.dart | 8 ++++ cw_bitcoin/lib/electrum_wallet.dart | 4 ++ cw_bitcoin/lib/electrum_wallet_addresses.dart | 8 +++- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 43 +++++++++++++++++-- cw_bitcoin/pubspec.lock | 8 ++-- cw_bitcoin/pubspec.yaml | 4 +- cw_bitcoin_cash/pubspec.yaml | 4 +- lib/bitcoin/cw_bitcoin.dart | 2 + .../screens/dashboard/pages/address_page.dart | 3 +- .../dashboard/receive_option_view_model.dart | 32 +++++++++----- .../wallet_address_list_view_model.dart | 3 +- 11 files changed, 91 insertions(+), 28 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_receive_page_option.dart b/cw_bitcoin/lib/bitcoin_receive_page_option.dart index 2d2339a41..714f1da90 100644 --- a/cw_bitcoin/lib/bitcoin_receive_page_option.dart +++ b/cw_bitcoin/lib/bitcoin_receive_page_option.dart @@ -7,6 +7,7 @@ class BitcoinReceivePageOption implements ReceivePageOption { static const p2tr = BitcoinReceivePageOption._('Taproot (P2TR)'); static const p2wsh = BitcoinReceivePageOption._('Segwit (P2WSH)'); static const p2pkh = BitcoinReceivePageOption._('Legacy (P2PKH)'); + static const mweb = BitcoinReceivePageOption._('MWEB'); const BitcoinReceivePageOption._(this.value); @@ -24,12 +25,19 @@ class BitcoinReceivePageOption implements ReceivePageOption { BitcoinReceivePageOption.p2pkh ]; + static const allLitecoin = [ + BitcoinReceivePageOption.p2wpkh, + BitcoinReceivePageOption.mweb + ]; + factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) { switch (type) { case SegwitAddresType.p2tr: return BitcoinReceivePageOption.p2tr; case SegwitAddresType.p2wsh: return BitcoinReceivePageOption.p2wsh; + case SegwitAddresType.mweb: + return BitcoinReceivePageOption.mweb; case P2pkhAddressType.p2pkh: return BitcoinReceivePageOption.p2pkh; case P2shAddressType.p2wpkhInP2sh: diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 4a76ee5dd..17e9880e9 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1337,6 +1337,8 @@ BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) return P2wshAddress.fromAddress(address: address, network: network); } else if (P2trAddress.regex.hasMatch(address)) { return P2trAddress.fromAddress(address: address, network: network); + } else if (MwebAddress.regex.hasMatch(address)) { + return MwebAddress.fromAddress(address: address, network: network); } else { return P2wpkhAddress.fromAddress(address: address, network: network); } @@ -1351,6 +1353,8 @@ BitcoinAddressType _getScriptType(BitcoinBaseAddress type) { return SegwitAddresType.p2wsh; } else if (type is P2trAddress) { return SegwitAddresType.p2tr; + } else if (type is MwebAddress) { + return SegwitAddresType.mweb; } else { return SegwitAddresType.p2wpkh; } diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index c43d4988a..6a7a9fd18 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -16,6 +16,7 @@ const List ADDRESS_TYPES = [ P2pkhAddressType.p2pkh, SegwitAddresType.p2tr, SegwitAddresType.p2wsh, + SegwitAddresType.mweb, P2shAddressType.p2wpkhInP2sh, ]; @@ -154,6 +155,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); } else if (walletInfo.type == WalletType.litecoin) { await _generateInitialAddresses(); + await _generateInitialAddresses(type: SegwitAddresType.mweb); } else if (walletInfo.type == WalletType.bitcoin) { await _generateInitialAddresses(); await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); @@ -217,6 +219,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { {required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) => ''; + Future getAddressAsync( + {required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) async => + getAddress(index: index, hd: hd, addressType: addressType); + @override Future updateAddressesInBox() async { try { @@ -328,7 +334,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { for (var i = startIndex; i < count + startIndex; i++) { final address = BitcoinAddressRecord( - getAddress(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType), + await getAddressAsync(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType), index: i, isHidden: isHidden, type: type ?? addressPageType, diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 993d17933..fead1de52 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -1,8 +1,11 @@ +import 'package:convert/convert.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; +import 'package:bitcoin_flutter/bitcoin_flutter.dart'; import 'package:cw_bitcoin/utils.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; +import 'package:cw_mweb/cw_mweb.dart'; +import 'package:cw_mweb/mwebd.pb.dart'; import 'package:mobx/mobx.dart'; part 'litecoin_wallet_addresses.g.dart'; @@ -21,8 +24,40 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialChangeAddressIndex, }) : super(walletInfo); + List mweb_addrs = []; + + Future topUpMweb(int index) async { + while (mweb_addrs.length - index < 1000) { + final length = mweb_addrs.length; + final scanSecret = mainHd.derive(0).privKey!; + final spendPubkey = mainHd.derive(1).pubKey!; + final stub = await CwMweb.stub(); + final resp = await stub.addresses(AddressRequest( + fromIndex: length, + toIndex: index + 1000, + scanSecret: hex.decode(scanSecret), + spendPubkey: hex.decode(spendPubkey), + )); + if (mweb_addrs.length == length) { + mweb_addrs.addAll(resp.address); + } + } + } + @override - String getAddress( - {required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) => - generateP2WPKHAddress(hd: hd, index: index, network: network); + String getAddress({required int index, required HDWallet hd, BitcoinAddressType? addressType}) { + if (addressType == SegwitAddresType.mweb) { + topUpMweb(index); + return hd == sideHd ? mweb_addrs[0] : mweb_addrs[index+1]; + } + return generateP2WPKHAddress(hd: hd, index: index, network: network); + } + + @override + Future getAddressAsync({required int index, required HDWallet hd, BitcoinAddressType? addressType}) async { + if (addressType == SegwitAddresType.mweb) { + await topUpMweb(index); + } + return getAddress(index: index, hd: hd, addressType: addressType); + } } diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 20851ea86..f93365d89 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -86,11 +86,9 @@ packages: bitcoin_base: dependency: "direct main" description: - path: "." - ref: cake-update-v2 - resolved-ref: "01d844a5f5a520a31df5254e34169af4664aa769" - url: "https://github.com/cake-tech/bitcoin_base.git" - source: git + path: "../../bitcoin_base" + relative: true + source: path version: "4.2.0" bitcoin_flutter: dependency: "direct main" diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 033d1f84e..9c3c5d751 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -31,9 +31,7 @@ dependencies: unorm_dart: ^0.2.0 cryptography: ^2.0.5 bitcoin_base: - git: - url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v2 + path: ../../bitcoin_base blockchain_utils: ^2.1.1 cw_mweb: path: ../cw_mweb diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index 37827f1ba..e491cea32 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -30,9 +30,7 @@ dependencies: url: https://github.com/cake-tech/bitbox-flutter.git ref: Add-Support-For-OP-Return-data bitcoin_base: - git: - url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v2 + path: ../../bitcoin_base diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 707f1157b..77c940ac4 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -242,6 +242,8 @@ class CWBitcoin extends Bitcoin { return SegwitAddresType.p2tr; case BitcoinReceivePageOption.p2wsh: return SegwitAddresType.p2wsh; + case BitcoinReceivePageOption.mweb: + return SegwitAddresType.mweb; case BitcoinReceivePageOption.p2wpkh: default: return SegwitAddresType.p2wpkh; diff --git a/lib/src/screens/dashboard/pages/address_page.dart b/lib/src/screens/dashboard/pages/address_page.dart index 3c77cad48..0e51f759d 100644 --- a/lib/src/screens/dashboard/pages/address_page.dart +++ b/lib/src/screens/dashboard/pages/address_page.dart @@ -219,7 +219,8 @@ class AddressPage extends BasePage { } break; default: - if (addressListViewModel.type == WalletType.bitcoin) { + if (addressListViewModel.type == WalletType.bitcoin || + addressListViewModel.type == WalletType.litecoin) { addressListViewModel.setAddressType(bitcoin!.getBitcoinAddressType(option)); } } diff --git a/lib/view_model/dashboard/receive_option_view_model.dart b/lib/view_model/dashboard/receive_option_view_model.dart index 1e4726eee..17e17842c 100644 --- a/lib/view_model/dashboard/receive_option_view_model.dart +++ b/lib/view_model/dashboard/receive_option_view_model.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cw_bitcoin/bitcoin_receive_page_option.dart'; import 'package:cw_core/receive_page_option.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; @@ -11,19 +12,30 @@ class ReceiveOptionViewModel = ReceiveOptionViewModelBase with _$ReceiveOptionVi abstract class ReceiveOptionViewModelBase with Store { ReceiveOptionViewModelBase(this._wallet, this.initialPageOption) : selectedReceiveOption = initialPageOption ?? - (_wallet.type == WalletType.bitcoin + (_wallet.type == WalletType.bitcoin || + _wallet.type == WalletType.litecoin ? bitcoin!.getSelectedAddressType(_wallet) : ReceivePageOption.mainnet), _options = [] { - final walletType = _wallet.type; - _options = walletType == WalletType.haven - ? [ReceivePageOption.mainnet] - : walletType == WalletType.bitcoin - ? [ - ...bitcoin!.getBitcoinReceivePageOptions(), - ...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet) - ] - : ReceivePageOptions; + switch (_wallet.type) { + case WalletType.bitcoin: + _options = [ + ...bitcoin!.getBitcoinReceivePageOptions(), + ...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet) + ]; + break; + case WalletType.litecoin: + _options = [ + ...BitcoinReceivePageOption.allLitecoin, + ...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet) + ]; + break; + case WalletType.haven: + _options = [ReceivePageOption.mainnet]; + break; + default: + _options = ReceivePageOptions; + }; } final WalletBase _wallet; diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index 20980f5f0..5f3b2b9c2 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -404,7 +404,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo @action Future setAddressType(dynamic option) async { - if (wallet.type == WalletType.bitcoin) { + if (wallet.type == WalletType.bitcoin || + wallet.type == WalletType.litecoin) { await bitcoin!.setAddressType(wallet, option); } } From 407b73171e96b7ba74e92435ad2d5c60c25014d2 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Mon, 22 Apr 2024 12:48:11 +0100 Subject: [PATCH 006/100] Fix mweb address derivation --- cw_bitcoin/lib/litecoin_wallet.dart | 1 + cw_bitcoin/lib/litecoin_wallet_addresses.dart | 6 ++++-- lib/view_model/dashboard/receive_option_view_model.dart | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index b6b996b27..1608a3dba 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -54,6 +54,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialChangeAddressIndex: initialChangeAddressIndex, mainHd: hd, sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"), + mwebHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/1000'"), network: network, ); autorun((_) { diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index fead1de52..8875efa18 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -17,6 +17,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with WalletInfo walletInfo, { required super.mainHd, required super.sideHd, + required this.mwebHd, required super.network, required super.electrumClient, super.initialAddresses, @@ -24,13 +25,14 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialChangeAddressIndex, }) : super(walletInfo); + final HDWallet mwebHd; List mweb_addrs = []; Future topUpMweb(int index) async { while (mweb_addrs.length - index < 1000) { final length = mweb_addrs.length; - final scanSecret = mainHd.derive(0).privKey!; - final spendPubkey = mainHd.derive(1).pubKey!; + final scanSecret = mwebHd.derive(0x80000000).privKey!; + final spendPubkey = mwebHd.derive(0x80000001).pubKey!; final stub = await CwMweb.stub(); final resp = await stub.addresses(AddressRequest( fromIndex: length, diff --git a/lib/view_model/dashboard/receive_option_view_model.dart b/lib/view_model/dashboard/receive_option_view_model.dart index 17e17842c..472918f4a 100644 --- a/lib/view_model/dashboard/receive_option_view_model.dart +++ b/lib/view_model/dashboard/receive_option_view_model.dart @@ -35,7 +35,7 @@ abstract class ReceiveOptionViewModelBase with Store { break; default: _options = ReceivePageOptions; - }; + } } final WalletBase _wallet; From 90588ee88a01c0bb542b62038e8245653dbb07dc Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Mon, 22 Apr 2024 13:11:31 +0100 Subject: [PATCH 007/100] Use camel-case --- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 8875efa18..e1ee81b31 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -26,11 +26,11 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with }) : super(walletInfo); final HDWallet mwebHd; - List mweb_addrs = []; + List mwebAddrs = []; Future topUpMweb(int index) async { - while (mweb_addrs.length - index < 1000) { - final length = mweb_addrs.length; + while (mwebAddrs.length - index < 1000) { + final length = mwebAddrs.length; final scanSecret = mwebHd.derive(0x80000000).privKey!; final spendPubkey = mwebHd.derive(0x80000001).pubKey!; final stub = await CwMweb.stub(); @@ -40,8 +40,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with scanSecret: hex.decode(scanSecret), spendPubkey: hex.decode(spendPubkey), )); - if (mweb_addrs.length == length) { - mweb_addrs.addAll(resp.address); + if (mwebAddrs.length == length) { + mwebAddrs.addAll(resp.address); } } } @@ -50,7 +50,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with String getAddress({required int index, required HDWallet hd, BitcoinAddressType? addressType}) { if (addressType == SegwitAddresType.mweb) { topUpMweb(index); - return hd == sideHd ? mweb_addrs[0] : mweb_addrs[index+1]; + return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index+1]; } return generateP2WPKHAddress(hd: hd, index: index, network: network); } From 9aa5ef83316d20f51479b3f66e947490e98a84a0 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Mon, 22 Apr 2024 17:18:18 +0100 Subject: [PATCH 008/100] Show utxos in tx list --- cw_bitcoin/lib/electrum.dart | 9 +++++++-- cw_bitcoin/lib/litecoin_wallet.dart | 31 +++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 0553170cc..359f9b36d 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'package:convert/convert.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_amount_format.dart'; import 'package:cw_bitcoin/script_hash.dart'; @@ -263,8 +264,12 @@ class ElectrumClient { await call(method: 'blockchain.transaction.get_merkle', params: [hash, height]) as Map; - Future> getHeader({required int height}) async => - await call(method: 'blockchain.block.get_header', params: [height]) as Map; + Future getBlockTime({required int height}) async { + final header = await call(method: 'blockchain.block.header', params: [height]) as String; + final bd = ByteData.sublistView(Uint8List.fromList(hex.decode(header))); + final timestamp = bd.getUint32(68, Endian.little) * 1000; + return DateTime.fromMillisecondsSinceEpoch(timestamp, isUtc: true); + } Future estimatefee({required int p}) => call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) { diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 1608a3dba..e71a68dee 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -1,9 +1,12 @@ import 'dart:async'; +import 'package:convert/convert.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; +import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/sync_status.dart'; +import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_bitcoin/litecoin_wallet_addresses.dart'; import 'package:cw_core/transaction_priority.dart'; @@ -11,6 +14,7 @@ import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:cw_bitcoin/electrum_wallet_snapshot.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; @@ -36,7 +40,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, - }) : super( + }) : mwebHd = bitcoin.HDWallet.fromSeed(seedBytes, + network: litecoinNetwork).derivePath("m/1000'"), + super( mnemonic: mnemonic, password: password, walletInfo: walletInfo, @@ -54,7 +60,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialChangeAddressIndex: initialChangeAddressIndex, mainHd: hd, sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"), - mwebHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/1000'"), + mwebHd: mwebHd, network: network, ); autorun((_) { @@ -62,6 +68,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { }); } + final bitcoin.HDWallet mwebHd; + static Future create( {required String mnemonic, required String password, @@ -129,6 +137,25 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { syncStatus = SyncedSyncStatus(); } }); + final scanSecret = mwebHd.derive(0x80000000).privKey!; + final req = UtxosRequest(scanSecret: hex.decode(scanSecret)); + await for (var utxo in stub.utxos(req)) { + final status = await stub.status(StatusRequest()); + var date = DateTime.now(); + var confirmations = 0; + if (utxo.height > 0) { + date = await electrumClient.getBlockTime(height: utxo.height); + confirmations = status.blockHeaderHeight - utxo.height + 1; + } + final tx = ElectrumTransactionInfo(WalletType.litecoin, + id: utxo.outputId, height: utxo.height, + amount: utxo.value.toInt(), + direction: TransactionDirection.incoming, + isPending: utxo.height == 0, + date: date, confirmations: confirmations); + transactionHistory.addOne(tx); + await transactionHistory.save(); + } } @override From b07c3d8a54a76dec713a06164086aba615e2846b Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Mon, 22 Apr 2024 18:07:59 +0100 Subject: [PATCH 009/100] A few fixes --- cw_bitcoin/lib/electrum.dart | 2 +- cw_bitcoin/lib/electrum_transaction_info.dart | 4 ++-- cw_bitcoin/lib/litecoin_wallet.dart | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 359f9b36d..d94dccbba 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -268,7 +268,7 @@ class ElectrumClient { final header = await call(method: 'blockchain.block.header', params: [height]) as String; final bd = ByteData.sublistView(Uint8List.fromList(hex.decode(header))); final timestamp = bd.getUint32(68, Endian.little) * 1000; - return DateTime.fromMillisecondsSinceEpoch(timestamp, isUtc: true); + return DateTime.fromMillisecondsSinceEpoch(timestamp); } Future estimatefee({required int p}) => diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index f980bd884..00315319c 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -196,8 +196,8 @@ class ElectrumTransactionInfo extends TransactionInfo { direction: parseTransactionDirectionFromInt(data['direction'] as int), date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int), isPending: data['isPending'] as bool, - inputAddresses: data['inputAddresses'] as List, - outputAddresses: data['outputAddresses'] as List, + inputAddresses: List.from(data['inputAddresses'] as List), + outputAddresses: List.from(data['outputAddresses'] as List), confirmations: data['confirmations'] as int); } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index e71a68dee..8fa5af63b 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -149,10 +149,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } final tx = ElectrumTransactionInfo(WalletType.litecoin, id: utxo.outputId, height: utxo.height, - amount: utxo.value.toInt(), + amount: utxo.value.toInt(), fee: 0, direction: TransactionDirection.incoming, isPending: utxo.height == 0, - date: date, confirmations: confirmations); + date: date, confirmations: confirmations, + inputAddresses: [], + outputAddresses: [utxo.address]); transactionHistory.addOne(tx); await transactionHistory.save(); } From 4fc1de8cb6b217ba051f62a4065f00c2dda13772 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Tue, 23 Apr 2024 00:26:34 +0100 Subject: [PATCH 010/100] Add spent processing --- cw_bitcoin/lib/litecoin_wallet.dart | 50 +++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 8fa5af63b..547a2dda5 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:convert/convert.dart'; +import 'package:crypto/crypto.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; @@ -137,9 +138,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { syncStatus = SyncedSyncStatus(); } }); + processMwebUtxos(); + } + + final Map mwebUtxos = {}; + + Future processMwebUtxos() async { + final stub = await CwMweb.stub(); final scanSecret = mwebHd.derive(0x80000000).privKey!; final req = UtxosRequest(scanSecret: hex.decode(scanSecret)); await for (var utxo in stub.utxos(req)) { + final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; + if (!mwebAddrs.contains(utxo.address)) continue; + mwebUtxos[utxo.outputId] = utxo; + final status = await stub.status(StatusRequest()); var date = DateTime.now(); var confirmations = 0; @@ -160,6 +172,44 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } + Future checkMwebUtxosSpent() async { + final List outputIds = []; + mwebUtxos.forEach((outputId, utxo) { + if (utxo.height > 0) + outputIds.add(outputId); + }); + final stub = await CwMweb.stub(); + final resp = await stub.spent(SpentRequest(outputId: outputIds)); + final spent = resp.outputId; + if (spent.isEmpty) return; + final status = await stub.status(StatusRequest()); + final height = await electrumClient.getCurrentBlockChainTip(); + if (height == null || status.mwebUtxosHeight != height) return; + final date = await electrumClient.getBlockTime(height: height); + int amount = 0; + Set inputAddresses = {}; + var output = AccumulatorSink(); + var input = sha256.startChunkedConversion(output); + for (final outputId in spent) { + input.add(hex.decode(outputId)); + amount += mwebUtxos[outputId]!.value.toInt(); + inputAddresses.add(mwebUtxos[outputId]!.address); + mwebUtxos.remove(outputId); + } + input.close(); + var digest = output.events.single; + final tx = ElectrumTransactionInfo(WalletType.litecoin, + id: digest.toString(), height: height, + amount: amount, fee: 0, + direction: TransactionDirection.outgoing, + isPending: false, + date: date, confirmations: 1, + inputAddresses: inputAddresses.toList(), + outputAddresses: []); + transactionHistory.addOne(tx); + await transactionHistory.save(); + } + @override int feeRate(TransactionPriority priority) { if (priority is LitecoinTransactionPriority) { From 50ef9185f53a2f8e9d30c083aadd834eefe7b5ba Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Tue, 23 Apr 2024 13:37:44 +0100 Subject: [PATCH 011/100] Update balance --- cw_bitcoin/lib/electrum_wallet.dart | 6 +++++- cw_bitcoin/lib/litecoin_wallet.dart | 33 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 17e9880e9..6794d93c5 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -740,7 +740,7 @@ abstract class ElectrumWalletBase Future makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); - Future updateUnspent() async { + Future updateUnspentCoins() async { List updatedUnspentCoins = []; final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet(); @@ -759,6 +759,10 @@ abstract class ElectrumWalletBase })))); unspentCoins = updatedUnspentCoins; + } + + Future updateUnspent() async { + await updateUnspentCoins(); if (unspentCoinsInfo.isEmpty) { unspentCoins.forEach((coin) => _addCoinInfo(coin)); diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 547a2dda5..1ff5b5ac9 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -4,6 +4,7 @@ import 'package:crypto/crypto.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; +import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/sync_status.dart'; @@ -169,6 +170,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { outputAddresses: [utxo.address]); transactionHistory.addOne(tx); await transactionHistory.save(); + await updateUnspent(); + await updateBalance(); } } @@ -210,6 +213,36 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await transactionHistory.save(); } + @override + Future updateUnspentCoins() async { + await super.updateUnspentCoins(); + await checkMwebUtxosSpent(); + final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; + mwebUtxos.forEach((outputId, utxo) { + final addressRecord = walletAddresses.allAddresses.firstWhere( + (addressRecord) => addressRecord.address == utxo.address); + unspentCoins.add(BitcoinUnspent(addressRecord, outputId, + utxo.value.toInt(), mwebAddrs.indexOf(utxo.address))); + }); + } + + @override + Future updateBalance() async { + await super.updateBalance(); + var confirmed = balance[currency]!.confirmed; + var unconfirmed = balance[currency]!.unconfirmed; + mwebUtxos.values.forEach((utxo) { + if (utxo.height > 0) + confirmed += utxo.value.toInt(); + else + unconfirmed += utxo.value.toInt(); + }); + balance[currency] = ElectrumBalance( + confirmed: confirmed, unconfirmed: unconfirmed, + frozen: balance[currency]!.frozen); + await save(); + } + @override int feeRate(TransactionPriority priority) { if (priority is LitecoinTransactionPriority) { From 05b71a372451e8b10a3329ccf245faba97dbc04d Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Tue, 23 Apr 2024 17:16:47 +0100 Subject: [PATCH 012/100] Balance fixes --- cw_bitcoin/lib/electrum_wallet.dart | 8 +++--- cw_bitcoin/lib/litecoin_wallet.dart | 43 +++++++++++++++++++++-------- cw_bitcoin/pubspec.lock | 8 ++++++ cw_bitcoin/pubspec.yaml | 1 + 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 6794d93c5..a2839b1e3 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -157,7 +157,7 @@ abstract class ElectrumWalletBase await updateTransactions(); _subscribeForUpdates(); await updateUnspent(); - await updateBalance(); + await updateBalance(delay: 5); _feeRates = await electrumClient.feeRates(network: network); Timer.periodic( @@ -1196,7 +1196,7 @@ abstract class ElectrumWalletBase }); } - Future _fetchBalances() async { + Future fetchBalances() async { final addresses = walletAddresses.allAddresses.toList(); final balanceFutures = >>[]; for (var i = 0; i < addresses.length; i++) { @@ -1240,8 +1240,8 @@ abstract class ElectrumWalletBase confirmed: totalConfirmed, unconfirmed: totalUnconfirmed, frozen: totalFrozen); } - Future updateBalance() async { - balance[currency] = await _fetchBalances(); + Future updateBalance({int delay = 1}) async { + balance[currency] = await fetchBalances(); await save(); } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 1ff5b5ac9..35bb143f3 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -25,6 +25,7 @@ import 'package:cw_bitcoin/litecoin_network.dart'; import 'package:cw_mweb/cw_mweb.dart'; import 'package:cw_mweb/mwebd.pb.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; +import 'package:queue/queue.dart'; part 'litecoin_wallet.g.dart'; @@ -157,7 +158,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { var date = DateTime.now(); var confirmations = 0; if (utxo.height > 0) { - date = await electrumClient.getBlockTime(height: utxo.height); + while (true) try { + date = await electrumClient.getBlockTime(height: utxo.height); + break; + } catch (err) { + await Future.delayed(Duration(seconds: 1)); + } confirmations = status.blockHeaderHeight - utxo.height + 1; } final tx = ElectrumTransactionInfo(WalletType.litecoin, @@ -169,9 +175,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { inputAddresses: [], outputAddresses: [utxo.address]); transactionHistory.addOne(tx); - await transactionHistory.save(); - await updateUnspent(); - await updateBalance(); + queueUpdate(1); } } @@ -227,20 +231,35 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } @override - Future updateBalance() async { - await super.updateBalance(); - var confirmed = balance[currency]!.confirmed; - var unconfirmed = balance[currency]!.unconfirmed; + Future fetchBalances() async { + final balance = await super.fetchBalances(); + var confirmed = balance.confirmed; + var unconfirmed = balance.unconfirmed; mwebUtxos.values.forEach((utxo) { if (utxo.height > 0) confirmed += utxo.value.toInt(); else unconfirmed += utxo.value.toInt(); }); - balance[currency] = ElectrumBalance( - confirmed: confirmed, unconfirmed: unconfirmed, - frozen: balance[currency]!.frozen); - await save(); + return ElectrumBalance(confirmed: confirmed, + unconfirmed: unconfirmed, frozen: balance.frozen); + } + + @override + Future updateBalance({int delay = 1}) async { + queueUpdate(delay); + } + + Timer? _debounceTimer; + final _debounceQueue = Queue(); + + void queueUpdate(int delay) { + if (_debounceTimer?.isActive ?? false) _debounceTimer?.cancel(); + _debounceTimer = Timer(Duration(seconds: delay), () => + _debounceQueue.add(() async { + await updateUnspent(); + await super.updateBalance(); + })); } @override diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index f93365d89..f081d7e87 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -651,6 +651,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.3" + queue: + dependency: "direct main" + description: + name: queue + sha256: "9a41ecadc15db79010108c06eae229a45c56b18db699760f34e8c9ac9b831ff9" + url: "https://pub.dev" + source: hosted + version: "3.1.0+2" rxdart: dependency: "direct main" description: diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 9c3c5d751..4615f1219 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -36,6 +36,7 @@ dependencies: cw_mweb: path: ../cw_mweb grpc: ^3.2.4 + queue: ^3.1.0+2 dev_dependencies: flutter_test: From 3e5a5f18faed191245dd99a02df5c3e2d817a661 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Tue, 23 Apr 2024 18:31:30 +0100 Subject: [PATCH 013/100] Update address records --- cw_bitcoin/lib/litecoin_wallet.dart | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 35bb143f3..2da4c407c 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -174,6 +174,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { date: date, confirmations: confirmations, inputAddresses: [], outputAddresses: [utxo.address]); + if (transactionHistory.transactions[utxo.outputId] == null) { + final addressRecord = walletAddresses.allAddresses.firstWhere( + (addressRecord) => addressRecord.address == utxo.address); + addressRecord.txCount++; + addressRecord.balance += utxo.value.toInt(); + } transactionHistory.addOne(tx); queueUpdate(1); } @@ -199,8 +205,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { var input = sha256.startChunkedConversion(output); for (final outputId in spent) { input.add(hex.decode(outputId)); - amount += mwebUtxos[outputId]!.value.toInt(); - inputAddresses.add(mwebUtxos[outputId]!.address); + final utxo = mwebUtxos[outputId]!; + final addressRecord = walletAddresses.allAddresses.firstWhere( + (addressRecord) => addressRecord.address == utxo.address); + if (!inputAddresses.contains(utxo.address)) + addressRecord.txCount++; + addressRecord.balance -= utxo.value.toInt(); + amount += utxo.value.toInt(); + inputAddresses.add(utxo.address); mwebUtxos.remove(outputId); } input.close(); From 37ff38f0dfbc7656be1e6dd1f2d1f8755532641a Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Wed, 24 Apr 2024 11:53:43 +0100 Subject: [PATCH 014/100] Get rid of debounce hack --- cw_bitcoin/lib/electrum.dart | 9 ++---- cw_bitcoin/lib/electrum_wallet.dart | 9 ++++-- cw_bitcoin/lib/litecoin_wallet.dart | 44 +++++++++++------------------ cw_bitcoin/pubspec.lock | 8 ------ cw_bitcoin/pubspec.yaml | 1 - cw_mweb/lib/mwebd.pb.dart | 28 ++++++++++++++++++ 6 files changed, 52 insertions(+), 47 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index d94dccbba..0553170cc 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:convert/convert.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_amount_format.dart'; import 'package:cw_bitcoin/script_hash.dart'; @@ -264,12 +263,8 @@ class ElectrumClient { await call(method: 'blockchain.transaction.get_merkle', params: [hash, height]) as Map; - Future getBlockTime({required int height}) async { - final header = await call(method: 'blockchain.block.header', params: [height]) as String; - final bd = ByteData.sublistView(Uint8List.fromList(hex.decode(header))); - final timestamp = bd.getUint32(68, Endian.little) * 1000; - return DateTime.fromMillisecondsSinceEpoch(timestamp); - } + Future> getHeader({required int height}) async => + await call(method: 'blockchain.block.get_header', params: [height]) as Map; Future estimatefee({required int p}) => call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) { diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index a2839b1e3..b25069863 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -21,6 +21,7 @@ import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; import 'package:cw_bitcoin/exceptions.dart'; import 'package:cw_bitcoin/litecoin_network.dart'; +import 'package:cw_bitcoin/litecoin_wallet.dart'; import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; import 'package:cw_bitcoin/script_hash.dart'; import 'package:cw_bitcoin/utils.dart'; @@ -156,8 +157,10 @@ abstract class ElectrumWalletBase syncStatus = AttemptingSyncStatus(); await updateTransactions(); _subscribeForUpdates(); - await updateUnspent(); - await updateBalance(delay: 5); + if (!(this is LitecoinWallet)) { + await updateUnspent(); + await updateBalance(); + } _feeRates = await electrumClient.feeRates(network: network); Timer.periodic( @@ -1240,7 +1243,7 @@ abstract class ElectrumWalletBase confirmed: totalConfirmed, unconfirmed: totalUnconfirmed, frozen: totalFrozen); } - Future updateBalance({int delay = 1}) async { + Future updateBalance() async { balance[currency] = await fetchBalances(); await save(); } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 2da4c407c..7506e368a 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -25,7 +25,6 @@ import 'package:cw_bitcoin/litecoin_network.dart'; import 'package:cw_mweb/cw_mweb.dart'; import 'package:cw_mweb/mwebd.pb.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; -import 'package:queue/queue.dart'; part 'litecoin_wallet.g.dart'; @@ -149,7 +148,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final stub = await CwMweb.stub(); final scanSecret = mwebHd.derive(0x80000000).privKey!; final req = UtxosRequest(scanSecret: hex.decode(scanSecret)); + var initDone = false; await for (var utxo in stub.utxos(req)) { + if (utxo.address.isEmpty) { + await updateUnspent(); + await updateBalance(); + initDone = true; + } final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; if (!mwebAddrs.contains(utxo.address)) continue; mwebUtxos[utxo.outputId] = utxo; @@ -158,12 +163,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { var date = DateTime.now(); var confirmations = 0; if (utxo.height > 0) { - while (true) try { - date = await electrumClient.getBlockTime(height: utxo.height); - break; - } catch (err) { - await Future.delayed(Duration(seconds: 1)); - } + date = DateTime.fromMillisecondsSinceEpoch(utxo.blockTime * 1000); confirmations = status.blockHeaderHeight - utxo.height + 1; } final tx = ElectrumTransactionInfo(WalletType.litecoin, @@ -179,9 +179,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { (addressRecord) => addressRecord.address == utxo.address); addressRecord.txCount++; addressRecord.balance += utxo.value.toInt(); + addressRecord.setAsUsed(); } transactionHistory.addOne(tx); - queueUpdate(1); + if (initDone) { + await updateUnspent(); + await updateBalance(); + } } } @@ -197,8 +201,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (spent.isEmpty) return; final status = await stub.status(StatusRequest()); final height = await electrumClient.getCurrentBlockChainTip(); - if (height == null || status.mwebUtxosHeight != height) return; - final date = await electrumClient.getBlockTime(height: height); + if (height == null || status.blockHeaderHeight != height) return; + if (status.mwebUtxosHeight != height) return; int amount = 0; Set inputAddresses = {}; var output = AccumulatorSink(); @@ -222,7 +226,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { amount: amount, fee: 0, direction: TransactionDirection.outgoing, isPending: false, - date: date, confirmations: 1, + date: DateTime.fromMillisecondsSinceEpoch(status.blockTime * 1000), + confirmations: 1, inputAddresses: inputAddresses.toList(), outputAddresses: []); transactionHistory.addOne(tx); @@ -257,23 +262,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unconfirmed: unconfirmed, frozen: balance.frozen); } - @override - Future updateBalance({int delay = 1}) async { - queueUpdate(delay); - } - - Timer? _debounceTimer; - final _debounceQueue = Queue(); - - void queueUpdate(int delay) { - if (_debounceTimer?.isActive ?? false) _debounceTimer?.cancel(); - _debounceTimer = Timer(Duration(seconds: delay), () => - _debounceQueue.add(() async { - await updateUnspent(); - await super.updateBalance(); - })); - } - @override int feeRate(TransactionPriority priority) { if (priority is LitecoinTransactionPriority) { diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index f081d7e87..f93365d89 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -651,14 +651,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.3" - queue: - dependency: "direct main" - description: - name: queue - sha256: "9a41ecadc15db79010108c06eae229a45c56b18db699760f34e8c9ac9b831ff9" - url: "https://pub.dev" - source: hosted - version: "3.1.0+2" rxdart: dependency: "direct main" description: diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 4615f1219..9c3c5d751 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -36,7 +36,6 @@ dependencies: cw_mweb: path: ../cw_mweb grpc: ^3.2.4 - queue: ^3.1.0+2 dev_dependencies: flutter_test: diff --git a/cw_mweb/lib/mwebd.pb.dart b/cw_mweb/lib/mwebd.pb.dart index 8d139d7eb..d0dd486c0 100644 --- a/cw_mweb/lib/mwebd.pb.dart +++ b/cw_mweb/lib/mwebd.pb.dart @@ -51,6 +51,7 @@ class StatusResponse extends $pb.GeneratedMessage { $core.int? blockHeaderHeight, $core.int? mwebHeaderHeight, $core.int? mwebUtxosHeight, + $core.int? blockTime, }) { final $result = create(); if (blockHeaderHeight != null) { @@ -62,6 +63,9 @@ class StatusResponse extends $pb.GeneratedMessage { if (mwebUtxosHeight != null) { $result.mwebUtxosHeight = mwebUtxosHeight; } + if (blockTime != null) { + $result.blockTime = blockTime; + } return $result; } StatusResponse._() : super(); @@ -72,6 +76,7 @@ class StatusResponse extends $pb.GeneratedMessage { ..a<$core.int>(1, _omitFieldNames ? '' : 'blockHeaderHeight', $pb.PbFieldType.O3) ..a<$core.int>(2, _omitFieldNames ? '' : 'mwebHeaderHeight', $pb.PbFieldType.O3) ..a<$core.int>(3, _omitFieldNames ? '' : 'mwebUtxosHeight', $pb.PbFieldType.O3) + ..a<$core.int>(4, _omitFieldNames ? '' : 'blockTime', $pb.PbFieldType.OU3) ..hasRequiredFields = false ; @@ -122,6 +127,15 @@ class StatusResponse extends $pb.GeneratedMessage { $core.bool hasMwebUtxosHeight() => $_has(2); @$pb.TagNumber(3) void clearMwebUtxosHeight() => clearField(3); + + @$pb.TagNumber(4) + $core.int get blockTime => $_getIZ(3); + @$pb.TagNumber(4) + set blockTime($core.int v) { $_setUnsignedInt32(3, v); } + @$pb.TagNumber(4) + $core.bool hasBlockTime() => $_has(3); + @$pb.TagNumber(4) + void clearBlockTime() => clearField(4); } class UtxosRequest extends $pb.GeneratedMessage { @@ -194,6 +208,7 @@ class Utxo extends $pb.GeneratedMessage { $fixnum.Int64? value, $core.String? address, $core.String? outputId, + $core.int? blockTime, }) { final $result = create(); if (height != null) { @@ -208,6 +223,9 @@ class Utxo extends $pb.GeneratedMessage { if (outputId != null) { $result.outputId = outputId; } + if (blockTime != null) { + $result.blockTime = blockTime; + } return $result; } Utxo._() : super(); @@ -219,6 +237,7 @@ class Utxo extends $pb.GeneratedMessage { ..a<$fixnum.Int64>(2, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) ..aOS(3, _omitFieldNames ? '' : 'address') ..aOS(4, _omitFieldNames ? '' : 'outputId') + ..a<$core.int>(5, _omitFieldNames ? '' : 'blockTime', $pb.PbFieldType.OU3) ..hasRequiredFields = false ; @@ -278,6 +297,15 @@ class Utxo extends $pb.GeneratedMessage { $core.bool hasOutputId() => $_has(3); @$pb.TagNumber(4) void clearOutputId() => clearField(4); + + @$pb.TagNumber(5) + $core.int get blockTime => $_getIZ(4); + @$pb.TagNumber(5) + set blockTime($core.int v) { $_setUnsignedInt32(4, v); } + @$pb.TagNumber(5) + $core.bool hasBlockTime() => $_has(4); + @$pb.TagNumber(5) + void clearBlockTime() => clearField(5); } class AddressRequest extends $pb.GeneratedMessage { From 404672f10fdbc5bc780822af69a434d525df01cc Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Wed, 24 Apr 2024 14:49:51 +0100 Subject: [PATCH 015/100] Get sending up to the confirmation box --- cw_bitcoin/lib/electrum_wallet.dart | 2 +- cw_bitcoin/lib/litecoin_wallet.dart | 6 ++++-- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 7 +++++++ lib/core/address_validator.dart | 16 +++++++++------- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index b25069863..37937f45c 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -157,7 +157,7 @@ abstract class ElectrumWalletBase syncStatus = AttemptingSyncStatus(); await updateTransactions(); _subscribeForUpdates(); - if (!(this is LitecoinWallet)) { + if (this is! LitecoinWallet) { await updateUnspent(); await updateBalance(); } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 7506e368a..adce6e6b7 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -242,8 +242,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { mwebUtxos.forEach((outputId, utxo) { final addressRecord = walletAddresses.allAddresses.firstWhere( (addressRecord) => addressRecord.address == utxo.address); - unspentCoins.add(BitcoinUnspent(addressRecord, outputId, - utxo.value.toInt(), mwebAddrs.indexOf(utxo.address))); + final unspent = BitcoinUnspent(addressRecord, outputId, + utxo.value.toInt(), mwebAddrs.indexOf(utxo.address)); + if (unspent.vout == 0) unspent.isChange = true; + unspentCoins.add(unspent); }); } diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index e1ee81b31..226bd1fc6 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -62,4 +62,11 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with } return getAddress(index: index, hd: hd, addressType: addressType); } + + @action + @override + Future getChangeAddress() async { + await topUpMweb(0); + return mwebAddrs[0]; + } } diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 967cf9bf0..bb69d4ecf 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -9,8 +9,9 @@ class AddressValidator extends TextValidator { AddressValidator({required CryptoCurrency type}) : super( errorMessage: S.current.error_text_address, - useAdditionalValidation: type == CryptoCurrency.btc - ? (String txt) => validateAddress(address: txt, network: BitcoinNetwork.mainnet) + useAdditionalValidation: type == CryptoCurrency.btc || type == CryptoCurrency.ltc + ? (String txt) => validateAddress(address: txt, network: + type == CryptoCurrency.btc ? BitcoinNetwork.mainnet : LitecoinNetwork.mainnet) : null, pattern: getPattern(type), length: getLength(type)); @@ -27,6 +28,8 @@ class AddressValidator extends TextValidator { '|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$'; case CryptoCurrency.btc: return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$'; + case CryptoCurrency.ltc: + return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${MwebAddress.regex.pattern}\$'; case CryptoCurrency.nano: return '[0-9a-zA-Z_]'; case CryptoCurrency.banano: @@ -96,8 +99,6 @@ class AddressValidator extends TextValidator { return '^(?!bitcoincash:)[0-9a-zA-Z]*\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{41}\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{42}\$|^bitcoincash:q|p[0-9a-zA-Z]{41}\$|^bitcoincash:q|p[0-9a-zA-Z]{42}\$'; case CryptoCurrency.bnb: return '[0-9a-zA-Z]'; - case CryptoCurrency.ltc: - return '^(?!(ltc|LTC)1)[0-9a-zA-Z]*\$|(^LTC1[A-Z0-9]*\$)|(^ltc1[a-z0-9]*\$)'; case CryptoCurrency.hbar: return '[0-9a-zA-Z.]'; case CryptoCurrency.zaddr: @@ -144,6 +145,8 @@ class AddressValidator extends TextValidator { return null; case CryptoCurrency.btc: return null; + case CryptoCurrency.ltc: + return null; case CryptoCurrency.dash: return [34]; case CryptoCurrency.eos: @@ -190,8 +193,6 @@ class AddressValidator extends TextValidator { return [42, 43, 44, 54, 55]; case CryptoCurrency.bnb: return [42]; - case CryptoCurrency.ltc: - return [34, 43, 63]; case CryptoCurrency.nano: return [64, 65]; case CryptoCurrency.banano: @@ -278,7 +279,8 @@ class AddressValidator extends TextValidator { case CryptoCurrency.ltc: return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)' '|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)' - '|([^0-9a-zA-Z]|^)ltc[a-zA-Z0-9]{26,45}([^0-9a-zA-Z]|\$)'; + '|([^0-9a-zA-Z]|^)ltc[a-zA-Z0-9]{26,45}([^0-9a-zA-Z]|\$)' + '|([^0-9a-zA-Z]|^)((ltc|t)mweb1q[ac-hj-np-z02-9]{90,120})([^0-9a-zA-Z]|\$)'; case CryptoCurrency.eth: return '0x[0-9a-zA-Z]{42}'; case CryptoCurrency.maticpoly: From 4abe70062fbfa7f1896c3d63235a49bafabf4240 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Thu, 25 Apr 2024 15:23:01 +0100 Subject: [PATCH 016/100] Fee estimation --- cw_bitcoin/lib/electrum.dart | 25 ++++++--- cw_bitcoin/lib/electrum_wallet.dart | 52 ++++++++++++------- cw_bitcoin/lib/litecoin_wallet.dart | 44 ++++++++++++++++ .../com/cakewallet/mweb/CwMwebPlugin.kt | 4 +- cw_mweb/lib/cw_mweb.dart | 5 +- 5 files changed, 100 insertions(+), 30 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 0553170cc..2b3f490a3 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -331,14 +331,19 @@ class ElectrumClient { // "height": 520481, // "hex": "00000020890208a0ae3a3892aa047c5468725846577cfcd9b512b50000000000000000005dc2b02f2d297a9064ee103036c14d678f9afc7e3d9409cf53fd58b82e938e8ecbeca05a2d2103188ce804c4" // } - Future getCurrentBlockChainTip() => - call(method: 'blockchain.headers.subscribe').then((result) { - if (result is Map) { - return result["height"] as int; - } + BehaviorSubject>? tipListener; + int? currentTip; - return null; - }); + Future getCurrentBlockChainTip() async { + final method = 'blockchain.headers.subscribe'; + final cb = (result) => currentTip = result['height'] as int; + if (tipListener == null) { + tipListener = subscribe(id: method, method: method); + tipListener?.listen(cb); + cb(await call(method: method)); + } + return currentTip; + } BehaviorSubject? scripthashUpdate(String scripthash) { _id += 1; @@ -424,6 +429,12 @@ class ElectrumClient { void _methodHandler({required String method, required Map request}) { switch (method) { + case 'blockchain.headers.subscribe': + final params = request['params'] as List; + final id = 'blockchain.headers.subscribe'; + + _tasks[id]?.subject?.add(params.last); + break; case 'blockchain.scripthash.subscribe': final params = request['params'] as List; final scripthash = params.first as String?; diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 37937f45c..1f5c14319 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -392,24 +392,13 @@ abstract class ElectrumWalletBase value: BigInt.from(amountLeftForChangeAndFee), )); - int estimatedSize; - if (network is BitcoinCashNetwork) { - estimatedSize = ForkedTransactionBuilder.estimateTransactionSize( - utxos: utxos, - outputs: outputs, - network: network as BitcoinCashNetwork, - memo: memo, - ); - } else { - estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize( - utxos: utxos, - outputs: outputs, - network: network, - memo: memo, - ); - } - - int fee = feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize); + int fee = await calcFee( + utxos: utxos, + outputs: outputs, + network: network, + memo: memo, + feeRate: feeRate, + ); if (fee == 0) { throw BitcoinTransactionNoFeeException(); @@ -499,6 +488,33 @@ abstract class ElectrumWalletBase ); } + Future calcFee({ + required List utxos, + required List outputs, + required BasedUtxoNetwork network, + String? memo, + required int feeRate}) async { + + int estimatedSize; + if (network is BitcoinCashNetwork) { + estimatedSize = ForkedTransactionBuilder.estimateTransactionSize( + utxos: utxos, + outputs: outputs, + network: network, + memo: memo, + ); + } else { + estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize( + utxos: utxos, + outputs: outputs, + network: network, + memo: memo, + ); + } + + return feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize); + } + @override Future createTransaction(Object credentials) async { try { diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index adce6e6b7..b84675669 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -1,12 +1,15 @@ import 'dart:async'; +import 'dart:math'; import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart'; +import 'package:fixnum/fixnum.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/sync_status.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/unspent_coins_info.dart'; @@ -279,4 +282,45 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return 0; } + + @override + Future calcFee({ + required List utxos, + required List outputs, + required BasedUtxoNetwork network, + String? memo, + required int feeRate}) async { + + final txb = BitcoinTransactionBuilder(utxos: utxos, + outputs: outputs, fee: BigInt.zero, network: network); + final scanSecret = mwebHd.derive(0x80000000).privKey!; + final spendSecret = mwebHd.derive(0x80000001).privKey!; + final stub = await CwMweb.stub(); + final resp = await stub.create(CreateRequest( + rawTx: txb.buildTransaction((a, b, c, d) => '').toBytes(), + scanSecret: hex.decode(scanSecret), + spendSecret: hex.decode(spendSecret), + feeRatePerKb: Int64(feeRate * 1000), + dryRun: true)); + final tx = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); + final posUtxos = utxos.where((utxo) => tx.inputs.any((input) => + input.txId == utxo.utxo.txHash && input.txIndex == utxo.utxo.vout)).toList(); + final preOutputSum = outputs.fold(0, (acc, output) => acc + output.toOutput.amount.toInt()); + final posOutputSum = tx.outputs.fold(0, (acc, output) => acc + output.amount.toInt()); + final mwebInputSum = utxos.sumOfUtxosValue() - posUtxos.sumOfUtxosValue(); + final expectedPegin = max(0, preOutputSum - mwebInputSum.toInt()); + var fee = posOutputSum - expectedPegin; + if (expectedPegin > 0) { + fee += await super.calcFee(utxos: posUtxos, outputs: tx.outputs.map((output) => + BitcoinScriptOutput(script: output.scriptPubKey, value: output.amount)).toList(), + network: network, memo: memo, feeRate: feeRate) + feeRate * 41; + } + return fee; + } + + @override + Future createTransaction(Object credentials) async { + final tx = await super.createTransaction(credentials); + return tx; + } } diff --git a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt index cf194417b..1dfbe12e5 100644 --- a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt +++ b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt @@ -17,6 +17,7 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { /// This local reference serves to register the plugin with the Flutter Engine and unregister it /// when the Flutter Engine is detached from the Activity private lateinit var channel : MethodChannel + private var port: Long? = null override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "cw_mweb") @@ -26,7 +27,8 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { if (call.method == "start") { val dataDir = call.argument("dataDir") ?: "" - result.success(Mwebd.newServer("", dataDir, "").start(0)) + port = port ?: Mwebd.newServer("", dataDir, "").start(0) + result.success(port) } else { result.notImplemented() } diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index a54295673..f16d451d9 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -4,13 +4,10 @@ import 'cw_mweb_platform_interface.dart'; import 'mwebd.pbgrpc.dart'; class CwMweb { - static Future? port; - static Future stub() async { final appDir = await getApplicationSupportDirectory(); - port ??= CwMwebPlatform.instance.start(appDir.path); return RpcClient(ClientChannel('127.0.0.1', - port: await port ?? 0, + port: await CwMwebPlatform.instance.start(appDir.path) ?? 0, options: const ChannelOptions( credentials: ChannelCredentials.insecure()))); } From c96424256e7dcb6d56c13a188989c96363222dea Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Thu, 25 Apr 2024 19:02:49 +0100 Subject: [PATCH 017/100] Stop the daemon if plugin is unloaded --- .../src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt index 1dfbe12e5..d3d4140ef 100644 --- a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt +++ b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt @@ -9,6 +9,7 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result import mwebd.Mwebd +import mwebd.Server /** CwMwebPlugin */ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { @@ -17,6 +18,7 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { /// This local reference serves to register the plugin with the Flutter Engine and unregister it /// when the Flutter Engine is detached from the Activity private lateinit var channel : MethodChannel + private var server: Server? = null private var port: Long? = null override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { @@ -27,7 +29,8 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { if (call.method == "start") { val dataDir = call.argument("dataDir") ?: "" - port = port ?: Mwebd.newServer("", dataDir, "").start(0) + server = server ?: Mwebd.newServer("", dataDir, "") + port = port ?: server?.start(0) result.success(port) } else { result.notImplemented() @@ -36,5 +39,6 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { channel.setMethodCallHandler(null) + server?.stop() } } From b1caf79c1b4f4a3bb124d7eac24b80e38abf1914 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Thu, 25 Apr 2024 23:13:41 +0100 Subject: [PATCH 018/100] Normal fee for non-mweb txns --- cw_bitcoin/lib/electrum_wallet.dart | 1 + cw_bitcoin/lib/litecoin_wallet.dart | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 1f5c14319..586c73ed8 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1250,6 +1250,7 @@ abstract class ElectrumWalletBase totalConfirmed += confirmed; totalUnconfirmed += unconfirmed; + addressRecord.balance = confirmed + unconfirmed; if (confirmed > 0 || unconfirmed > 0) { addressRecord.setAsUsed(); } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index b84675669..c9bee05f1 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -291,6 +291,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { String? memo, required int feeRate}) async { + final spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb); + final paysToMweb = outputs.any((output) => + output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb); + if (!spendsMweb && !paysToMweb) { + return await super.calcFee(utxos: utxos, outputs: outputs, + network: network, memo: memo, feeRate: feeRate); + } final txb = BitcoinTransactionBuilder(utxos: utxos, outputs: outputs, fee: BigInt.zero, network: network); final scanSecret = mwebHd.derive(0x80000000).privKey!; From fea7e7a09767d2f296a4ec4e09bb0b9f26beba4e Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Fri, 26 Apr 2024 01:07:58 +0100 Subject: [PATCH 019/100] Fix fee estimation for send all --- cw_bitcoin/lib/electrum_wallet.dart | 25 +++++++------------------ cw_bitcoin/lib/litecoin_wallet.dart | 5 +++++ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 586c73ed8..281255e53 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -245,24 +245,13 @@ abstract class ElectrumWalletBase throw BitcoinTransactionNoInputsException(); } - int estimatedSize; - if (network is BitcoinCashNetwork) { - estimatedSize = ForkedTransactionBuilder.estimateTransactionSize( - utxos: utxos, - outputs: outputs, - network: network as BitcoinCashNetwork, - memo: memo, - ); - } else { - estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize( - utxos: utxos, - outputs: outputs, - network: network, - memo: memo, - ); - } - - int fee = feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize); + int fee = await calcFee( + utxos: utxos, + outputs: outputs, + network: network, + memo: memo, + feeRate: feeRate, + ); if (fee == 0) { throw BitcoinTransactionNoFeeException(); diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index c9bee05f1..bd69b9e05 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -298,6 +298,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return await super.calcFee(utxos: utxos, outputs: outputs, network: network, memo: memo, feeRate: feeRate); } + if (outputs.length == 1 && outputs[0].toOutput.amount == BigInt.zero) { + outputs = [BitcoinScriptOutput( + script: outputs[0].toOutput.scriptPubKey, + value: utxos.sumOfUtxosValue())]; + } final txb = BitcoinTransactionBuilder(utxos: utxos, outputs: outputs, fee: BigInt.zero, network: network); final scanSecret = mwebHd.derive(0x80000000).privKey!; From 66d98a282cbbaa4e8aad1596b74bbd0a17795593 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Fri, 26 Apr 2024 09:16:40 +0100 Subject: [PATCH 020/100] Don't hash mweb addresses --- cw_bitcoin/lib/electrum_wallet.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 281255e53..23a15c280 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -113,11 +113,13 @@ abstract class ElectrumWalletBase SyncStatus syncStatus; List get scriptHashes => walletAddresses.addressesByReceiveType + .where((addr) => addressTypeFromStr(addr.address, network) is! MwebAddress) .map((addr) => scriptHash(addr.address, network: network)) .toList(); List get publicScriptHashes => walletAddresses.allAddresses .where((addr) => !addr.isHidden) + .where((addr) => addressTypeFromStr(addr.address, network) is! MwebAddress) .map((addr) => scriptHash(addr.address, network: network)) .toList(); @@ -1205,7 +1207,8 @@ abstract class ElectrumWalletBase } Future fetchBalances() async { - final addresses = walletAddresses.allAddresses.toList(); + final addresses = walletAddresses.allAddresses.where((address) => + addressTypeFromStr(address.address, network) is! MwebAddress).toList(); final balanceFutures = >>[]; for (var i = 0; i < addresses.length; i++) { final addressRecord = addresses[i]; From 796c95315ff65d8e6251c92d88ea60ce9de80625 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Fri, 26 Apr 2024 11:19:40 +0100 Subject: [PATCH 021/100] More fee fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index bd69b9e05..fce466b1e 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -303,8 +303,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { script: outputs[0].toOutput.scriptPubKey, value: utxos.sumOfUtxosValue())]; } + final preOutputSum = outputs.fold(BigInt.zero, + (acc, output) => acc + output.toOutput.amount); + final fee = utxos.sumOfUtxosValue() - preOutputSum; final txb = BitcoinTransactionBuilder(utxos: utxos, - outputs: outputs, fee: BigInt.zero, network: network); + outputs: outputs, fee: fee, network: network); final scanSecret = mwebHd.derive(0x80000000).privKey!; final spendSecret = mwebHd.derive(0x80000001).privKey!; final stub = await CwMweb.stub(); @@ -317,17 +320,16 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final tx = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); final posUtxos = utxos.where((utxo) => tx.inputs.any((input) => input.txId == utxo.utxo.txHash && input.txIndex == utxo.utxo.vout)).toList(); - final preOutputSum = outputs.fold(0, (acc, output) => acc + output.toOutput.amount.toInt()); final posOutputSum = tx.outputs.fold(0, (acc, output) => acc + output.amount.toInt()); final mwebInputSum = utxos.sumOfUtxosValue() - posUtxos.sumOfUtxosValue(); - final expectedPegin = max(0, preOutputSum - mwebInputSum.toInt()); - var fee = posOutputSum - expectedPegin; - if (expectedPegin > 0) { - fee += await super.calcFee(utxos: posUtxos, outputs: tx.outputs.map((output) => + final expectedPegin = max(0, (preOutputSum - mwebInputSum).toInt()); + var feeIncrease = posOutputSum - expectedPegin; + if (expectedPegin > 0 && fee == 0) { + feeIncrease += await super.calcFee(utxos: posUtxos, outputs: tx.outputs.map((output) => BitcoinScriptOutput(script: output.scriptPubKey, value: output.amount)).toList(), network: network, memo: memo, feeRate: feeRate) + feeRate * 41; } - return fee; + return fee.toInt() + feeIncrease; } @override From f0157101fbfaee74381c974ba7474385d2f22e00 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Fri, 26 Apr 2024 12:32:38 +0100 Subject: [PATCH 022/100] Broadcast mweb --- cw_bitcoin/lib/litecoin_wallet.dart | 16 ++++++++---- .../lib/pending_bitcoin_transaction.dart | 25 ++++++++++++++++--- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index fce466b1e..a07875c1f 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -8,6 +8,7 @@ import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; +import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/sync_status.dart'; @@ -308,13 +309,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final fee = utxos.sumOfUtxosValue() - preOutputSum; final txb = BitcoinTransactionBuilder(utxos: utxos, outputs: outputs, fee: fee, network: network); - final scanSecret = mwebHd.derive(0x80000000).privKey!; - final spendSecret = mwebHd.derive(0x80000001).privKey!; final stub = await CwMweb.stub(); final resp = await stub.create(CreateRequest( rawTx: txb.buildTransaction((a, b, c, d) => '').toBytes(), - scanSecret: hex.decode(scanSecret), - spendSecret: hex.decode(spendSecret), + scanSecret: hex.decode(mwebHd.derive(0x80000000).privKey!), + spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!), feeRatePerKb: Int64(feeRate * 1000), dryRun: true)); final tx = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); @@ -334,7 +333,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future createTransaction(Object credentials) async { - final tx = await super.createTransaction(credentials); + final tx = await super.createTransaction(credentials) as PendingBitcoinTransaction; + final stub = await CwMweb.stub(); + final resp = await stub.create(CreateRequest( + rawTx: hex.decode(tx.hex), + scanSecret: hex.decode(mwebHd.derive(0x80000000).privKey!), + spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!), + feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000)); + tx.hexOverride = hex.encode(resp.rawTx); return tx; } } diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index a59b4f429..46f152708 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -1,11 +1,14 @@ import 'package:cw_bitcoin/exceptions.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_core/pending_transaction.dart'; import 'package:cw_bitcoin/electrum.dart'; import 'package:cw_bitcoin/bitcoin_amount_format.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:cw_mweb/cw_mweb.dart'; +import 'package:cw_mweb/mwebd.pb.dart'; class PendingBitcoinTransaction with PendingTransaction { PendingBitcoinTransaction( @@ -31,12 +34,14 @@ class PendingBitcoinTransaction with PendingTransaction { final bool hasChange; final bool isSendAll; final bool hasTaprootInputs; + String? idOverride; + String? hexOverride; @override - String get id => _tx.txId(); + String get id => idOverride ?? _tx.txId(); @override - String get hex => _tx.serialize(); + String get hex => hexOverride ?? _tx.serialize(); @override String get amountFormatted => bitcoinAmountToString(amount: amount); @@ -49,8 +54,7 @@ class PendingBitcoinTransaction with PendingTransaction { final List _listeners; - @override - Future commit() async { + Future _commit() async { int? callId; final result = await electrumClient.broadcastTransaction( @@ -78,6 +82,19 @@ class PendingBitcoinTransaction with PendingTransaction { throw BitcoinTransactionCommitFailed(); } + } + + @override + Future commit() async { + if (network is LitecoinNetwork) try { + final stub = await CwMweb.stub(); + final resp = await stub.broadcast(BroadcastRequest(rawTx: BytesUtils.fromHexString(hex))); + idOverride = resp.txid; + } catch (e) { + throw BitcoinTransactionCommitFailed(); + } else { + await _commit(); + } _listeners.forEach((listener) => listener(transactionInfo())); } From 68688ee0d374672ab4dfaadb865e7a6003f08086 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Fri, 26 Apr 2024 13:03:42 +0100 Subject: [PATCH 023/100] Remove test files --- cw_mweb/test/cw_mweb_method_channel_test.dart | 27 ----------------- cw_mweb/test/cw_mweb_test.dart | 29 ------------------- 2 files changed, 56 deletions(-) delete mode 100644 cw_mweb/test/cw_mweb_method_channel_test.dart delete mode 100644 cw_mweb/test/cw_mweb_test.dart diff --git a/cw_mweb/test/cw_mweb_method_channel_test.dart b/cw_mweb/test/cw_mweb_method_channel_test.dart deleted file mode 100644 index bb0523758..000000000 --- a/cw_mweb/test/cw_mweb_method_channel_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:cw_mweb/cw_mweb_method_channel.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - MethodChannelCwMweb platform = MethodChannelCwMweb(); - const MethodChannel channel = MethodChannel('cw_mweb'); - - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( - channel, - (MethodCall methodCall) async { - return '42'; - }, - ); - }); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); - }); - - test('getPlatformVersion', () async { - expect(await platform.getPlatformVersion(), '42'); - }); -} diff --git a/cw_mweb/test/cw_mweb_test.dart b/cw_mweb/test/cw_mweb_test.dart deleted file mode 100644 index 3677659a9..000000000 --- a/cw_mweb/test/cw_mweb_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:cw_mweb/cw_mweb.dart'; -import 'package:cw_mweb/cw_mweb_platform_interface.dart'; -import 'package:cw_mweb/cw_mweb_method_channel.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -class MockCwMwebPlatform - with MockPlatformInterfaceMixin - implements CwMwebPlatform { - - @override - Future getPlatformVersion() => Future.value('42'); -} - -void main() { - final CwMwebPlatform initialPlatform = CwMwebPlatform.instance; - - test('$MethodChannelCwMweb is the default instance', () { - expect(initialPlatform, isInstanceOf()); - }); - - test('getPlatformVersion', () async { - CwMweb cwMwebPlugin = CwMweb(); - MockCwMwebPlatform fakePlatform = MockCwMwebPlatform(); - CwMwebPlatform.instance = fakePlatform; - - expect(await cwMwebPlugin.getPlatformVersion(), '42'); - }); -} From 70b07b2d4739c9f35bd6c21d4b1836e5896b2ae4 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Fri, 26 Apr 2024 13:16:04 +0100 Subject: [PATCH 024/100] One more --- .../cakewallet/cw_mweb/CwMwebPluginTest.kt | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 cw_mweb/android/src/test/kotlin/com/cakewallet/cw_mweb/CwMwebPluginTest.kt diff --git a/cw_mweb/android/src/test/kotlin/com/cakewallet/cw_mweb/CwMwebPluginTest.kt b/cw_mweb/android/src/test/kotlin/com/cakewallet/cw_mweb/CwMwebPluginTest.kt deleted file mode 100644 index baa81332f..000000000 --- a/cw_mweb/android/src/test/kotlin/com/cakewallet/cw_mweb/CwMwebPluginTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.cakewallet.mweb - -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import kotlin.test.Test -import org.mockito.Mockito - -/* - * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. - * - * Once you have built the plugin's example app, you can run these tests from the command - * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or - * you can run them directly from IDEs that support JUnit such as Android Studio. - */ - -internal class CwMwebPluginTest { - @Test - fun onMethodCall_getPlatformVersion_returnsExpectedValue() { - val plugin = CwMwebPlugin() - - val call = MethodCall("getPlatformVersion", null) - val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) - plugin.onMethodCall(call, mockResult) - - Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) - } -} From b1f03345502aeb8c9a9a3d4e354514f9d8b6d674 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Sat, 27 Apr 2024 14:26:43 +0100 Subject: [PATCH 025/100] Confirm sent txns --- cw_bitcoin/lib/litecoin_wallet.dart | 73 +++++++++++++++---- .../lib/pending_bitcoin_transaction.dart | 3 + 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index a07875c1f..483d86645 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:math'; +import 'package:collection/collection.dart'; import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart'; import 'package:fixnum/fixnum.dart'; @@ -122,6 +123,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } + int mwebUtxosHeight = 0; + @action @override Future startSync() async { @@ -142,6 +145,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } else { syncStatus = SyncedSyncStatus(); } + if (resp.mwebUtxosHeight > mwebUtxosHeight) { + mwebUtxosHeight = resp.mwebUtxosHeight; + await checkMwebUtxosSpent(); + } }); processMwebUtxos(); } @@ -170,14 +177,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { date = DateTime.fromMillisecondsSinceEpoch(utxo.blockTime * 1000); confirmations = status.blockHeaderHeight - utxo.height + 1; } - final tx = ElectrumTransactionInfo(WalletType.litecoin, - id: utxo.outputId, height: utxo.height, - amount: utxo.value.toInt(), fee: 0, - direction: TransactionDirection.incoming, - isPending: utxo.height == 0, - date: date, confirmations: confirmations, - inputAddresses: [], - outputAddresses: [utxo.address]); + var tx = transactionHistory.transactions.values.firstWhereOrNull( + (tx) => tx.outputAddresses?.contains(utxo.outputId) ?? false); + if (tx == null) + tx = ElectrumTransactionInfo(WalletType.litecoin, + id: utxo.outputId, height: utxo.height, + amount: utxo.value.toInt(), fee: 0, + direction: TransactionDirection.incoming, + isPending: utxo.height == 0, + date: date, confirmations: confirmations, + inputAddresses: [], + outputAddresses: [utxo.address]); + tx.isPending = utxo.height == 0; + tx.confirmations = confirmations; if (transactionHistory.transactions[utxo.outputId] == null) { final addressRecord = walletAddresses.allAddresses.firstWhere( (addressRecord) => addressRecord.address == utxo.address); @@ -194,11 +206,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } Future checkMwebUtxosSpent() async { - final List outputIds = []; - mwebUtxos.forEach((outputId, utxo) { - if (utxo.height > 0) - outputIds.add(outputId); - }); + while ((await Future.wait(transactionHistory.transactions.values + .where((tx) => tx.direction == TransactionDirection.outgoing && tx.isPending) + .map(checkPendingTransaction))).any((x) => x)); + final outputIds = mwebUtxos.values + .where((utxo) => utxo.height > 0) + .map((utxo) => utxo.outputId).toList(); final stub = await CwMweb.stub(); final resp = await stub.spent(SpentRequest(outputId: outputIds)); final spent = resp.outputId; @@ -212,8 +225,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { var output = AccumulatorSink(); var input = sha256.startChunkedConversion(output); for (final outputId in spent) { - input.add(hex.decode(outputId)); - final utxo = mwebUtxos[outputId]!; + final utxo = mwebUtxos[outputId]; + if (utxo == null) continue; final addressRecord = walletAddresses.allAddresses.firstWhere( (addressRecord) => addressRecord.address == utxo.address); if (!inputAddresses.contains(utxo.address)) @@ -222,7 +235,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { amount += utxo.value.toInt(); inputAddresses.add(utxo.address); mwebUtxos.remove(outputId); + input.add(hex.decode(outputId)); } + if (inputAddresses.isEmpty) return; input.close(); var digest = output.events.single; final tx = ElectrumTransactionInfo(WalletType.litecoin, @@ -238,6 +253,33 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await transactionHistory.save(); } + Future checkPendingTransaction(ElectrumTransactionInfo tx) async { + if (!tx.isPending) return false; + final outputId = [], target = {}; + final isHash = RegExp(r'^[a-f0-9]{64}$').hasMatch; + final spendingOutputIds = tx.inputAddresses?.where(isHash) ?? []; + final payingToOutputIds = tx.outputAddresses?.where(isHash) ?? []; + outputId.addAll(spendingOutputIds); + outputId.addAll(payingToOutputIds); + target.addAll(spendingOutputIds); + for (final outputId in payingToOutputIds) { + final spendingTx = transactionHistory.transactions.values.firstWhereOrNull( + (tx) => tx.inputAddresses?.contains(outputId) ?? false); + if (spendingTx != null && !spendingTx.isPending) + target.add(outputId); + } + if (outputId.isEmpty) return false; + final stub = await CwMweb.stub(); + final resp = await stub.spent(SpentRequest(outputId: outputId)); + if (resp.outputId.toSet() != target) return false; + final status = await stub.status(StatusRequest()); + if (!tx.isPending) return false; + tx.height = status.mwebUtxosHeight; + tx.confirmations = 1; + tx.isPending = false; + return true; + } + @override Future updateUnspentCoins() async { await super.updateUnspentCoins(); @@ -341,6 +383,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!), feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000)); tx.hexOverride = hex.encode(resp.rawTx); + tx.outputs = resp.outputId; return tx; } } diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index 46f152708..69bdaba70 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -36,6 +36,7 @@ class PendingBitcoinTransaction with PendingTransaction { final bool hasTaprootInputs; String? idOverride; String? hexOverride; + List? outputs; @override String get id => idOverride ?? _tx.txId(); @@ -110,5 +111,7 @@ class PendingBitcoinTransaction with PendingTransaction { date: DateTime.now(), isPending: true, confirmations: 0, + inputAddresses: _tx.inputs.map((input) => input.txId).toList(), + outputAddresses: outputs, fee: fee); } From 2c941efcc319e147daf490c67825e3e0daca62f2 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Sat, 27 Apr 2024 17:40:47 +0100 Subject: [PATCH 026/100] Couple of fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 483d86645..ea5176a00 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -188,6 +188,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { date: date, confirmations: confirmations, inputAddresses: [], outputAddresses: [utxo.address]); + tx.height = utxo.height; tx.isPending = utxo.height == 0; tx.confirmations = confirmations; if (transactionHistory.transactions[utxo.outputId] == null) { @@ -271,7 +272,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (outputId.isEmpty) return false; final stub = await CwMweb.stub(); final resp = await stub.spent(SpentRequest(outputId: outputId)); - if (resp.outputId.toSet() != target) return false; + if (!setEquals(resp.outputId.toSet(), target)) return false; final status = await stub.status(StatusRequest()); if (!tx.isPending) return false; tx.height = status.mwebUtxosHeight; From 8964774c5188fd72b6271014f6a603eb53664a36 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Sun, 28 Apr 2024 11:17:28 +0100 Subject: [PATCH 027/100] Resign inputs after mweb create --- cw_bitcoin/lib/litecoin_wallet.dart | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index ea5176a00..6444e217d 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -10,6 +10,7 @@ import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; +import 'package:cw_bitcoin/utils.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/sync_status.dart'; @@ -278,6 +279,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { tx.height = status.mwebUtxosHeight; tx.confirmations = 1; tx.isPending = false; + await transactionHistory.save(); return true; } @@ -366,7 +368,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final mwebInputSum = utxos.sumOfUtxosValue() - posUtxos.sumOfUtxosValue(); final expectedPegin = max(0, (preOutputSum - mwebInputSum).toInt()); var feeIncrease = posOutputSum - expectedPegin; - if (expectedPegin > 0 && fee == 0) { + if (expectedPegin > 0 && fee == BigInt.zero) { feeIncrease += await super.calcFee(utxos: posUtxos, outputs: tx.outputs.map((output) => BitcoinScriptOutput(script: output.scriptPubKey, value: output.amount)).toList(), network: network, memo: memo, feeRate: feeRate) + feeRate * 41; @@ -383,7 +385,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { scanSecret: hex.decode(mwebHd.derive(0x80000000).privKey!), spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!), feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000)); - tx.hexOverride = hex.encode(resp.rawTx); + final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); + tx.hexOverride = tx2.copyWith(witnesses: tx2.inputs.asMap().entries.map((e) { + final utxo = unspentCoins.firstWhere((utxo) => + utxo.hash == e.value.txId && utxo.vout == e.value.txIndex); + final key = generateECPrivate(hd: utxo.bitcoinAddressRecord.isHidden ? + walletAddresses.sideHd : walletAddresses.mainHd, + index: utxo.bitcoinAddressRecord.index, network: network); + final digest = tx2.getTransactionSegwitDigit(txInIndex: e.key, + script: key.getPublic().toP2pkhAddress().toScriptPubKey(), + amount: BigInt.from(utxo.value)); + return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]); + }).toList()).toHex(); tx.outputs = resp.outputId; return tx; } From b39b2bbb996087fb9b7fdb5a6339d18c03fd7c48 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Sun, 28 Apr 2024 14:12:12 +0100 Subject: [PATCH 028/100] Some more fixes --- cw_bitcoin/lib/electrum_wallet.dart | 1 - cw_bitcoin/lib/litecoin_wallet.dart | 11 ++++++----- cw_bitcoin/lib/pending_bitcoin_transaction.dart | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 23a15c280..ffb637004 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -792,7 +792,6 @@ abstract class ElectrumWalletBase coin.isFrozen = coinInfo.isFrozen; coin.isSending = coinInfo.isSending; coin.note = coinInfo.note; - coin.bitcoinAddressRecord.balance += coinInfo.value; } else { _addCoinInfo(coin); } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 6444e217d..6cabcc34e 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -133,6 +133,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final stub = await CwMweb.stub(); Timer.periodic( const Duration(milliseconds: 1500), (timer) async { + if (syncStatus is FailedSyncStatus) return; final height = await electrumClient.getCurrentBlockChainTip() ?? 0; final resp = await stub.status(StatusRequest()); if (resp.blockHeaderHeight < height) { @@ -145,10 +146,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { syncStatus = SyncingSyncStatus(1, 0.999); } else { syncStatus = SyncedSyncStatus(); - } - if (resp.mwebUtxosHeight > mwebUtxosHeight) { - mwebUtxosHeight = resp.mwebUtxosHeight; - await checkMwebUtxosSpent(); + if (resp.mwebUtxosHeight > mwebUtxosHeight) { + mwebUtxosHeight = resp.mwebUtxosHeight; + await checkMwebUtxosSpent(); + } } }); processMwebUtxos(); @@ -192,7 +193,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { tx.height = utxo.height; tx.isPending = utxo.height == 0; tx.confirmations = confirmations; - if (transactionHistory.transactions[utxo.outputId] == null) { + if (transactionHistory.transactions[tx.id] == null) { final addressRecord = walletAddresses.allAddresses.firstWhere( (addressRecord) => addressRecord.address == utxo.address); addressRecord.txCount++; diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index 69bdaba70..20cb1ebc6 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -1,3 +1,4 @@ +import 'package:grpc/grpc.dart'; import 'package:cw_bitcoin/exceptions.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; @@ -91,8 +92,8 @@ class PendingBitcoinTransaction with PendingTransaction { final stub = await CwMweb.stub(); final resp = await stub.broadcast(BroadcastRequest(rawTx: BytesUtils.fromHexString(hex))); idOverride = resp.txid; - } catch (e) { - throw BitcoinTransactionCommitFailed(); + } on GrpcError catch (e) { + throw BitcoinTransactionCommitFailed(errorMessage: e.message); } else { await _commit(); } From a936a3b6610ca81b025342d290dc26ec95a6f458 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Sun, 28 Apr 2024 19:46:07 +0100 Subject: [PATCH 029/100] Update balance after sending --- cw_bitcoin/lib/litecoin_wallet.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 6cabcc34e..4c589b134 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -399,6 +399,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]); }).toList()).toHex(); tx.outputs = resp.outputId; - return tx; + return tx..addListener((transaction) async { + transaction.inputAddresses?.forEach(mwebUtxos.remove); + await updateUnspent(); + await updateBalance(); + }); } } From d1ccf6eb0b26632854fda3583fa215f3bcfdd534 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Mon, 29 Apr 2024 12:11:59 +0100 Subject: [PATCH 030/100] Correctly update address records --- cw_bitcoin/lib/litecoin_wallet.dart | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 4c589b134..703571bd5 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -188,15 +188,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { direction: TransactionDirection.incoming, isPending: utxo.height == 0, date: date, confirmations: confirmations, - inputAddresses: [], - outputAddresses: [utxo.address]); + inputAddresses: [], outputAddresses: [utxo.outputId]); tx.height = utxo.height; tx.isPending = utxo.height == 0; tx.confirmations = confirmations; - if (transactionHistory.transactions[tx.id] == null) { + var isNew = transactionHistory.transactions[tx.id] == null; + if (!(tx.outputAddresses?.contains(utxo.address) ?? false)) { + tx.outputAddresses?.add(utxo.address); + isNew = true; + } + if (isNew) { final addressRecord = walletAddresses.allAddresses.firstWhere( (addressRecord) => addressRecord.address == utxo.address); - addressRecord.txCount++; + if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) + addressRecord.txCount++; addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); } @@ -400,7 +405,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { }).toList()).toHex(); tx.outputs = resp.outputId; return tx..addListener((transaction) async { - transaction.inputAddresses?.forEach(mwebUtxos.remove); + final addresses = {}; + transaction.inputAddresses?.forEach((id) { + final utxo = mwebUtxos.remove(id); + if (utxo == null) return; + final addressRecord = walletAddresses.allAddresses.firstWhere( + (addressRecord) => addressRecord.address == utxo.address); + if (!addresses.contains(utxo.address)) { + addressRecord.txCount++; + addresses.add(utxo.address); + } + addressRecord.balance -= utxo.value.toInt(); + }); + transaction.inputAddresses?.addAll(addresses); + transactionHistory.addOne(transaction); await updateUnspent(); await updateBalance(); }); From 7978ad447690b386f2058c10f318f5045b1aa24b Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Mon, 29 Apr 2024 13:08:05 +0100 Subject: [PATCH 031/100] Update confs --- cw_bitcoin/lib/litecoin_wallet.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 703571bd5..fba4bbc4a 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -149,6 +149,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (resp.mwebUtxosHeight > mwebUtxosHeight) { mwebUtxosHeight = resp.mwebUtxosHeight; await checkMwebUtxosSpent(); + for (final transaction in transactionHistory.transactions.values) { + if (transaction.isPending) continue; + final confirmations = mwebUtxosHeight - transaction.height + 1; + if (transaction.confirmations == confirmations) continue; + transaction.confirmations = confirmations; + transactionHistory.addOne(transaction); + } + await transactionHistory.save(); } } }); From 7173c4941ec8ae46f174f21b97984fa3d184e4bd Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 17 May 2024 10:39:22 -0700 Subject: [PATCH 032/100] [skip ci] updates --- cw_bitcoin/pubspec.lock | 12 +++++++----- cw_bitcoin/pubspec.yaml | 4 +++- cw_bitcoin_cash/pubspec.yaml | 4 +++- model_generator.sh | 1 + pubspec_base.yaml | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 85b2513e5..0dde34370 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -86,9 +86,11 @@ packages: bitcoin_base: dependency: "direct main" description: - path: "../../bitcoin_base" - relative: true - source: path + path: "." + ref: cake-mweb + resolved-ref: "9389210f4c0376ac6f5dfe5aeabc042adc76449c" + url: "https://github.com/cake-tech/bitcoin_base.git" + source: git version: "4.2.0" bitcoin_flutter: dependency: "direct main" @@ -696,10 +698,10 @@ packages: dependency: transitive description: name: protobuf - sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d" + sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "2.1.0" provider: dependency: transitive description: diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 1dac9367c..69b02cd76 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -31,7 +31,9 @@ dependencies: unorm_dart: ^0.2.0 cryptography: ^2.0.5 bitcoin_base: - path: ../../bitcoin_base + git: + url: https://github.com/cake-tech/bitcoin_base.git + ref: cake-mweb blockchain_utils: ^2.1.1 ledger_flutter: ^1.0.1 ledger_bitcoin: diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index 0318e3d36..88c1cee16 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -30,7 +30,9 @@ dependencies: url: https://github.com/cake-tech/bitbox-flutter.git ref: Add-Support-For-OP-Return-data bitcoin_base: - path: ../../bitcoin_base + git: + url: https://github.com/cake-tech/bitcoin_base.git + ref: cake-mweb diff --git a/model_generator.sh b/model_generator.sh index ee88644b6..9f4857f99 100755 --- a/model_generator.sh +++ b/model_generator.sh @@ -7,6 +7,7 @@ cd cw_nano && flutter pub get && flutter packages pub run build_runner build --d cd cw_bitcoin_cash && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_solana && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_tron && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. +cd cw_mweb && flutter pub get && cd .. cd cw_ethereum && flutter pub get && cd .. cd cw_polygon && flutter pub get && cd .. flutter packages pub run build_runner build --delete-conflicting-outputs diff --git a/pubspec_base.yaml b/pubspec_base.yaml index a2be113e4..7b54658cb 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -106,7 +106,7 @@ dependencies: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v2 + ref: cake-mweb ledger_flutter: ^1.0.1 dev_dependencies: From ec8b3f8476bf8552b0a5b39e5e5fa62eecb2b439 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 20 May 2024 15:45:16 -0700 Subject: [PATCH 033/100] [skip ci] add dep overrides --- cw_bitcoin/pubspec.yaml | 1 + pubspec_base.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 69b02cd76..67e5ceed8 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -53,6 +53,7 @@ dev_dependencies: dependency_overrides: watcher: ^1.1.0 + protobuf: ^3.1.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 7b54658cb..a7133b1c7 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -139,6 +139,7 @@ dependency_overrides: url: https://github.com/cake-tech/web3dart.git ref: cake flutter_secure_storage_platform_interface: 1.0.2 + protobuf: ^3.1.0 flutter_icons: image_path: "assets/images/app_logo.png" From 26510736f59a9782f7ee1e0c0d336ba7d8d5b15a Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 24 May 2024 11:00:43 -0700 Subject: [PATCH 034/100] working --- cw_mweb/ios/Classes/CwMwebPlugin.swift | 39 +++++++++++++++++-- cw_mweb/ios/cw_mweb.podspec | 3 ++ ios/Runner.xcodeproj/project.pbxproj | 10 +++-- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index d742aab61..a5fa63c5f 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -1,5 +1,6 @@ import Flutter import UIKit +import Mwebd public class CwMwebPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { @@ -8,10 +9,42 @@ public class CwMwebPlugin: NSObject, FlutterPlugin { registrar.addMethodCallDelegate(instance, channel: channel) } + private static var server: MwebdServer? + private static var port: Int = 0 + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "getPlatformVersion": - result("iOS " + UIDevice.current.systemVersion) + switch call.method { + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + case "start": + let args = call.arguments as? [String: String] + print("args: \(args)") + let dataDir = args?["dataDir"] + var error: NSError? + + if CwMwebPlugin.server == nil { + CwMwebPlugin.server = MwebdNewServer("", dataDir, "", &error) + + if let server = CwMwebPlugin.server { + do { + print("starting server \(CwMwebPlugin.port)") + try server.start(0, ret0_: &CwMwebPlugin.port) + result(CwMwebPlugin.port) + } catch let startError as NSError { + result(FlutterError(code: "Server Start Error", message: startError.localizedDescription, details: nil)) + } + } else if let error = error { + result(FlutterError(code: "Server Creation Error", message: error.localizedDescription, details: nil)) + } else { + result(FlutterError(code: "Unknown Error", message: "Failed to create server", details: nil)) + } + } else { +// result(FlutterError(code: "Server Already Running", message: "The server is already running", details: nil)) + result(CwMwebPlugin.port) + } + + + // result(0) default: result(FlutterMethodNotImplemented) } diff --git a/cw_mweb/ios/cw_mweb.podspec b/cw_mweb/ios/cw_mweb.podspec index cd0600fa1..4a1903bae 100644 --- a/cw_mweb/ios/cw_mweb.podspec +++ b/cw_mweb/ios/cw_mweb.podspec @@ -20,4 +20,7 @@ A new Flutter plugin project. # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } s.swift_version = '5.0' + s.ios.vendored_frameworks = 'Mwebd.xcframework' + s.preserve_paths = 'Mwebd.xcframework/**/*' + end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 8ed46a028..365a13dfb 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9F46EE5D2BC11178009318F5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; AD0937B0140D5A4C24E73BEA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + C58D93382C00FAC6004BCF69 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -60,6 +61,7 @@ 06957875428D0F5AAE053765 /* Frameworks */ = { isa = PBXGroup; children = ( + C58D93382C00FAC6004BCF69 /* libresolv.tbd */, 0C9986A3251A932F00D566FD /* CryptoSwift.framework */, 3C663361C56EBB242598F609 /* Pods_Runner.framework */, ); @@ -164,7 +166,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -355,7 +357,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -449,7 +451,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -499,7 +501,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a33..5e31d3d34 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Date: Wed, 29 May 2024 08:54:50 -0700 Subject: [PATCH 035/100] small fix --- cw_bitcoin/lib/litecoin_wallet.dart | 262 +++++++++++++++------------- 1 file changed, 145 insertions(+), 117 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 86d62ef05..6853d6a84 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -49,9 +49,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, - }) : mwebHd = bitcoin.HDWallet.fromSeed(seedBytes, - network: litecoinNetwork).derivePath("m/1000'"), - super( + }) : mwebHd = + bitcoin.HDWallet.fromSeed(seedBytes, network: litecoinNetwork).derivePath("m/1000'"), + super( mnemonic: mnemonic, password: password, walletInfo: walletInfo, @@ -78,6 +78,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } final bitcoin.HDWallet mwebHd; + Timer? _syncTimer; static Future create( {required String mnemonic, @@ -147,44 +148,50 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future startSync() async { await super.startSync(); final stub = await CwMweb.stub(); - Timer.periodic( - const Duration(milliseconds: 1500), (timer) async { - if (syncStatus is FailedSyncStatus) return; - final height = await electrumClient.getCurrentBlockChainTip() ?? 0; - final resp = await stub.status(StatusRequest()); - if (resp.blockHeaderHeight < height) { - int h = resp.blockHeaderHeight; - syncStatus = SyncingSyncStatus(height - h, h / height); - } else if (resp.mwebHeaderHeight < height) { - int h = resp.mwebHeaderHeight; - syncStatus = SyncingSyncStatus(height - h, h / height); - } else if (resp.mwebUtxosHeight < height) { - syncStatus = SyncingSyncStatus(1, 0.999); - } else { + _syncTimer?.cancel(); + _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { + print(syncStatus); + if (syncStatus is FailedSyncStatus) return; + final height = await electrumClient.getCurrentBlockChainTip() ?? 0; + final resp = await stub.status(StatusRequest()); + if (resp.blockHeaderHeight < height) { + int h = resp.blockHeaderHeight; + syncStatus = SyncingSyncStatus(height - h, h / height); + } else if (resp.mwebHeaderHeight < height) { + int h = resp.mwebHeaderHeight; + syncStatus = SyncingSyncStatus(height - h, h / height); + } else if (resp.mwebUtxosHeight < height) { + syncStatus = SyncingSyncStatus(1, 0.999); + } else { + // prevent unnecessary reaction triggers: + if (syncStatus is! SyncedSyncStatus) { syncStatus = SyncedSyncStatus(); - if (resp.mwebUtxosHeight > mwebUtxosHeight) { - mwebUtxosHeight = resp.mwebUtxosHeight; - await checkMwebUtxosSpent(); - for (final transaction in transactionHistory.transactions.values) { - if (transaction.isPending) continue; - final confirmations = mwebUtxosHeight - transaction.height + 1; - if (transaction.confirmations == confirmations) continue; - transaction.confirmations = confirmations; - transactionHistory.addOne(transaction); - } - await transactionHistory.save(); - } } - }); + + if (resp.mwebUtxosHeight > mwebUtxosHeight) { + mwebUtxosHeight = resp.mwebUtxosHeight; + await checkMwebUtxosSpent(); + for (final transaction in transactionHistory.transactions.values) { + if (transaction.isPending) continue; + final confirmations = mwebUtxosHeight - transaction.height + 1; + if (transaction.confirmations == confirmations) continue; + transaction.confirmations = confirmations; + transactionHistory.addOne(transaction); + } + await transactionHistory.save(); + } + } + }); processMwebUtxos(); } final Map mwebUtxos = {}; + int lastMwebUtxosHeight = 0; Future processMwebUtxos() async { final stub = await CwMweb.stub(); final scanSecret = mwebHd.derive(0x80000000).privKey!; - final req = UtxosRequest(scanSecret: hex.decode(scanSecret)); + final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: lastMwebUtxosHeight); var initDone = false; await for (var utxo in stub.utxos(req)) { if (utxo.address.isEmpty) { @@ -203,16 +210,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { date = DateTime.fromMillisecondsSinceEpoch(utxo.blockTime * 1000); confirmations = status.blockHeaderHeight - utxo.height + 1; } - var tx = transactionHistory.transactions.values.firstWhereOrNull( - (tx) => tx.outputAddresses?.contains(utxo.outputId) ?? false); + var tx = transactionHistory.transactions.values + .firstWhereOrNull((tx) => tx.outputAddresses?.contains(utxo.outputId) ?? false); if (tx == null) tx = ElectrumTransactionInfo(WalletType.litecoin, - id: utxo.outputId, height: utxo.height, - amount: utxo.value.toInt(), fee: 0, - direction: TransactionDirection.incoming, - isPending: utxo.height == 0, - date: date, confirmations: confirmations, - inputAddresses: [], outputAddresses: [utxo.outputId]); + id: utxo.outputId, + height: utxo.height, + amount: utxo.value.toInt(), + fee: 0, + direction: TransactionDirection.incoming, + isPending: utxo.height == 0, + date: date, + confirmations: confirmations, + inputAddresses: [], + outputAddresses: [utxo.outputId]); tx.height = utxo.height; tx.isPending = utxo.height == 0; tx.confirmations = confirmations; @@ -222,10 +233,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { isNew = true; } if (isNew) { - final addressRecord = walletAddresses.allAddresses.firstWhere( - (addressRecord) => addressRecord.address == utxo.address); - if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) - addressRecord.txCount++; + final addressRecord = walletAddresses.allAddresses + .firstWhere((addressRecord) => addressRecord.address == utxo.address); + if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) addressRecord.txCount++; addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); } @@ -234,16 +244,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await updateUnspent(); await updateBalance(); } + lastMwebUtxosHeight = utxo.height; + print("latest mweb utxo height: $lastMwebUtxosHeight"); } } Future checkMwebUtxosSpent() async { while ((await Future.wait(transactionHistory.transactions.values - .where((tx) => tx.direction == TransactionDirection.outgoing && tx.isPending) - .map(checkPendingTransaction))).any((x) => x)); - final outputIds = mwebUtxos.values - .where((utxo) => utxo.height > 0) - .map((utxo) => utxo.outputId).toList(); + .where((tx) => tx.direction == TransactionDirection.outgoing && tx.isPending) + .map(checkPendingTransaction))) + .any((x) => x)); + final outputIds = + mwebUtxos.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId).toList(); final stub = await CwMweb.stub(); final resp = await stub.spent(SpentRequest(outputId: outputIds)); final spent = resp.outputId; @@ -259,10 +271,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { for (final outputId in spent) { final utxo = mwebUtxos[outputId]; if (utxo == null) continue; - final addressRecord = walletAddresses.allAddresses.firstWhere( - (addressRecord) => addressRecord.address == utxo.address); - if (!inputAddresses.contains(utxo.address)) - addressRecord.txCount++; + final addressRecord = walletAddresses.allAddresses + .firstWhere((addressRecord) => addressRecord.address == utxo.address); + if (!inputAddresses.contains(utxo.address)) addressRecord.txCount++; addressRecord.balance -= utxo.value.toInt(); amount += utxo.value.toInt(); inputAddresses.add(utxo.address); @@ -273,14 +284,16 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { input.close(); var digest = output.events.single; final tx = ElectrumTransactionInfo(WalletType.litecoin, - id: digest.toString(), height: height, - amount: amount, fee: 0, - direction: TransactionDirection.outgoing, - isPending: false, - date: DateTime.fromMillisecondsSinceEpoch(status.blockTime * 1000), - confirmations: 1, - inputAddresses: inputAddresses.toList(), - outputAddresses: []); + id: digest.toString(), + height: height, + amount: amount, + fee: 0, + direction: TransactionDirection.outgoing, + isPending: false, + date: DateTime.fromMillisecondsSinceEpoch(status.blockTime * 1000), + confirmations: 1, + inputAddresses: inputAddresses.toList(), + outputAddresses: []); transactionHistory.addOne(tx); await transactionHistory.save(); } @@ -295,10 +308,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { outputId.addAll(payingToOutputIds); target.addAll(spendingOutputIds); for (final outputId in payingToOutputIds) { - final spendingTx = transactionHistory.transactions.values.firstWhereOrNull( - (tx) => tx.inputAddresses?.contains(outputId) ?? false); - if (spendingTx != null && !spendingTx.isPending) - target.add(outputId); + final spendingTx = transactionHistory.transactions.values + .firstWhereOrNull((tx) => tx.inputAddresses?.contains(outputId) ?? false); + if (spendingTx != null && !spendingTx.isPending) target.add(outputId); } if (outputId.isEmpty) return false; final stub = await CwMweb.stub(); @@ -319,10 +331,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await checkMwebUtxosSpent(); final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; mwebUtxos.forEach((outputId, utxo) { - final addressRecord = walletAddresses.allAddresses.firstWhere( - (addressRecord) => addressRecord.address == utxo.address); - final unspent = BitcoinUnspent(addressRecord, outputId, - utxo.value.toInt(), mwebAddrs.indexOf(utxo.address)); + final addressRecord = walletAddresses.allAddresses + .firstWhere((addressRecord) => addressRecord.address == utxo.address); + final unspent = BitcoinUnspent( + addressRecord, outputId, utxo.value.toInt(), mwebAddrs.indexOf(utxo.address)); if (unspent.vout == 0) unspent.isChange = true; unspentCoins.add(unspent); }); @@ -339,8 +351,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { else unconfirmed += utxo.value.toInt(); }); - return ElectrumBalance(confirmed: confirmed, - unconfirmed: unconfirmed, frozen: balance.frozen); + return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } @override @@ -360,30 +371,30 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } @override - Future calcFee({ - required List utxos, + Future calcFee( + {required List utxos, required List outputs, required BasedUtxoNetwork network, String? memo, required int feeRate}) async { - final spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb); - final paysToMweb = outputs.any((output) => - output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb); + final paysToMweb = outputs + .any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb); if (!spendsMweb && !paysToMweb) { - return await super.calcFee(utxos: utxos, outputs: outputs, - network: network, memo: memo, feeRate: feeRate); + return await super + .calcFee(utxos: utxos, outputs: outputs, network: network, memo: memo, feeRate: feeRate); } if (outputs.length == 1 && outputs[0].toOutput.amount == BigInt.zero) { - outputs = [BitcoinScriptOutput( - script: outputs[0].toOutput.scriptPubKey, - value: utxos.sumOfUtxosValue())]; + outputs = [ + BitcoinScriptOutput( + script: outputs[0].toOutput.scriptPubKey, value: utxos.sumOfUtxosValue()) + ]; } - final preOutputSum = outputs.fold(BigInt.zero, - (acc, output) => acc + output.toOutput.amount); + final preOutputSum = + outputs.fold(BigInt.zero, (acc, output) => acc + output.toOutput.amount); final fee = utxos.sumOfUtxosValue() - preOutputSum; - final txb = BitcoinTransactionBuilder(utxos: utxos, - outputs: outputs, fee: fee, network: network); + final txb = + BitcoinTransactionBuilder(utxos: utxos, outputs: outputs, fee: fee, network: network); final stub = await CwMweb.stub(); final resp = await stub.create(CreateRequest( rawTx: txb.buildTransaction((a, b, c, d) => '').toBytes(), @@ -392,16 +403,25 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { feeRatePerKb: Int64(feeRate * 1000), dryRun: true)); final tx = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); - final posUtxos = utxos.where((utxo) => tx.inputs.any((input) => - input.txId == utxo.utxo.txHash && input.txIndex == utxo.utxo.vout)).toList(); + final posUtxos = utxos + .where((utxo) => tx.inputs + .any((input) => input.txId == utxo.utxo.txHash && input.txIndex == utxo.utxo.vout)) + .toList(); final posOutputSum = tx.outputs.fold(0, (acc, output) => acc + output.amount.toInt()); final mwebInputSum = utxos.sumOfUtxosValue() - posUtxos.sumOfUtxosValue(); final expectedPegin = max(0, (preOutputSum - mwebInputSum).toInt()); var feeIncrease = posOutputSum - expectedPegin; if (expectedPegin > 0 && fee == BigInt.zero) { - feeIncrease += await super.calcFee(utxos: posUtxos, outputs: tx.outputs.map((output) => - BitcoinScriptOutput(script: output.scriptPubKey, value: output.amount)).toList(), - network: network, memo: memo, feeRate: feeRate) + feeRate * 41; + feeIncrease += await super.calcFee( + utxos: posUtxos, + outputs: tx.outputs + .map((output) => + BitcoinScriptOutput(script: output.scriptPubKey, value: output.amount)) + .toList(), + network: network, + memo: memo, + feeRate: feeRate) + + feeRate * 41; } return fee.toInt() + feeIncrease; } @@ -416,35 +436,43 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!), feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000)); final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); - tx.hexOverride = tx2.copyWith(witnesses: tx2.inputs.asMap().entries.map((e) { - final utxo = unspentCoins.firstWhere((utxo) => - utxo.hash == e.value.txId && utxo.vout == e.value.txIndex); - final key = generateECPrivate(hd: utxo.bitcoinAddressRecord.isHidden ? - walletAddresses.sideHd : walletAddresses.mainHd, - index: utxo.bitcoinAddressRecord.index, network: network); - final digest = tx2.getTransactionSegwitDigit(txInIndex: e.key, - script: key.getPublic().toP2pkhAddress().toScriptPubKey(), - amount: BigInt.from(utxo.value)); - return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]); - }).toList()).toHex(); + tx.hexOverride = tx2 + .copyWith( + witnesses: tx2.inputs.asMap().entries.map((e) { + final utxo = unspentCoins + .firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex); + final key = generateECPrivate( + hd: utxo.bitcoinAddressRecord.isHidden + ? walletAddresses.sideHd + : walletAddresses.mainHd, + index: utxo.bitcoinAddressRecord.index, + network: network); + final digest = tx2.getTransactionSegwitDigit( + txInIndex: e.key, + script: key.getPublic().toP2pkhAddress().toScriptPubKey(), + amount: BigInt.from(utxo.value)); + return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]); + }).toList()) + .toHex(); tx.outputs = resp.outputId; - return tx..addListener((transaction) async { - final addresses = {}; - transaction.inputAddresses?.forEach((id) { - final utxo = mwebUtxos.remove(id); - if (utxo == null) return; - final addressRecord = walletAddresses.allAddresses.firstWhere( - (addressRecord) => addressRecord.address == utxo.address); - if (!addresses.contains(utxo.address)) { - addressRecord.txCount++; - addresses.add(utxo.address); - } - addressRecord.balance -= utxo.value.toInt(); + return tx + ..addListener((transaction) async { + final addresses = {}; + transaction.inputAddresses?.forEach((id) { + final utxo = mwebUtxos.remove(id); + if (utxo == null) return; + final addressRecord = walletAddresses.allAddresses + .firstWhere((addressRecord) => addressRecord.address == utxo.address); + if (!addresses.contains(utxo.address)) { + addressRecord.txCount++; + addresses.add(utxo.address); + } + addressRecord.balance -= utxo.value.toInt(); + }); + transaction.inputAddresses?.addAll(addresses); + transactionHistory.addOne(transaction); + await updateUnspent(); + await updateBalance(); }); - transaction.inputAddresses?.addAll(addresses); - transactionHistory.addOne(transaction); - await updateUnspent(); - await updateBalance(); - }); } } From 4e1b96e99b4de3d7f06b30b113f9a3d61dd9f310 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 29 May 2024 09:18:21 -0700 Subject: [PATCH 036/100] merge fixes [skip ci] --- cw_bitcoin/lib/electrum_wallet.dart | 2 +- cw_bitcoin/lib/litecoin_wallet.dart | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 50740d27e..3f2307bf1 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -835,7 +835,7 @@ abstract class ElectrumWalletBase required int feeRate, List? inputPrivKeyInfos, List? vinOutpoints, - }) async { + }) async { int estimatedSize; if (network is BitcoinCashNetwork) { diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 479f80e1f..235f98e61 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -370,18 +370,28 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } @override - Future calcFee( - {required List utxos, - required List outputs, - required BasedUtxoNetwork network, - String? memo, - required int feeRate}) async { + Future calcFee({ + required List utxos, + required List outputs, + required BasedUtxoNetwork network, + String? memo, + required int feeRate, + List? inputPrivKeyInfos, + List? vinOutpoints, + }) async { final spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb); final paysToMweb = outputs .any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb); if (!spendsMweb && !paysToMweb) { - return await super - .calcFee(utxos: utxos, outputs: outputs, network: network, memo: memo, feeRate: feeRate); + return await super.calcFee( + utxos: utxos, + outputs: outputs, + network: network, + memo: memo, + feeRate: feeRate, + inputPrivKeyInfos: inputPrivKeyInfos, + vinOutpoints: vinOutpoints, + ); } if (outputs.length == 1 && outputs[0].toOutput.amount == BigInt.zero) { outputs = [ From 0882ba016fc3585ab0f9642a1b244e613f8cd415 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 29 May 2024 19:08:33 -0700 Subject: [PATCH 037/100] merge fixes [skip ci] --- cw_bitcoin/lib/electrum.dart | 8 +------- cw_bitcoin/lib/electrum_wallet.dart | 8 -------- cw_bitcoin/lib/litecoin_wallet.dart | 4 ++-- cw_bitcoin/pubspec.yaml | 4 ++++ cw_bitcoin_cash/pubspec.yaml | 4 ++++ pubspec_base.yaml | 4 ++++ 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 7578f39a0..b7005d8cd 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -355,19 +355,13 @@ class ElectrumClient { // } BehaviorSubject>? tipListener; int? currentTip; - Future getCurrentBlockChainTip() => - callWithTimeout(method: 'blockchain.headers.subscribe').then((result) { - if (result is Map) { - return result["height"] as int; - } - Future getCurrentBlockChainTip() async { final method = 'blockchain.headers.subscribe'; final cb = (result) => currentTip = result['height'] as int; if (tipListener == null) { tipListener = subscribe(id: method, method: method); tipListener?.listen(cb); - cb(await call(method: method)); + callWithTimeout(method: method).then(cb); } return currentTip; } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 3f2307bf1..8b15b97af 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -582,14 +582,6 @@ abstract class ElectrumWalletBase throw BitcoinTransactionNoInputsException(); } - int fee = await calcFee( - utxos: utxos, - outputs: outputs, - network: network, - memo: memo, - feeRate: feeRate, - ); - return UtxoDetails( availableInputs: availableInputs, unconfirmedCoins: unconfirmedCoins, diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 235f98e61..d5c70bd44 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -325,8 +325,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } @override - Future updateUnspentCoins() async { - await super.updateUnspentCoins(); + Future updateUnspent() async { + await super.updateUnspent(); await checkMwebUtxosSpent(); final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; mwebUtxos.forEach((outputId, utxo) { diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 8f577c53c..031d9419d 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -62,6 +62,10 @@ dev_dependencies: dependency_overrides: watcher: ^1.1.0 protobuf: ^3.1.0 + bitcoin_base: + git: + url: https://github.com/cake-tech/bitcoin_base + ref: cake-mweb # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index 635177778..367a025cb 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -47,6 +47,10 @@ dev_dependencies: dependency_overrides: watcher: ^1.1.0 + bitcoin_base: + git: + url: https://github.com/cake-tech/bitcoin_base + ref: cake-mweb # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/pubspec_base.yaml b/pubspec_base.yaml index d17964b55..4feefbc91 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -140,6 +140,10 @@ dependency_overrides: ref: cake flutter_secure_storage_platform_interface: 1.0.2 protobuf: ^3.1.0 + bitcoin_base: + git: + url: https://github.com/cake-tech/bitcoin_base + ref: cake-mweb flutter_icons: image_path: "assets/images/app_logo.png" From 2377a344bd8cce394006edb9bd1cb4cdc0190034 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 30 May 2024 09:09:29 -0700 Subject: [PATCH 038/100] [skip ci] minor fixes --- cw_bitcoin/lib/electrum_wallet.dart | 26 ++++++++++++-------------- cw_bitcoin/lib/litecoin_wallet.dart | 5 ++++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 8b15b97af..79a98b37d 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -420,15 +420,13 @@ abstract class ElectrumWalletBase await _subscribeForUpdates(); await updateTransactions(); - _subscribeForUpdates(); if (this is! LitecoinWallet) { await updateAllUnspents(); await updateBalance(); } - _feeRates = await electrumClient.feeRates(network: network); - + await updateFeeRates(); Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); if (alwaysScan == true) { @@ -820,15 +818,14 @@ abstract class ElectrumWalletBase } Future calcFee({ - required List utxos, - required List outputs, - required BasedUtxoNetwork network, - String? memo, - required int feeRate, - List? inputPrivKeyInfos, - List? vinOutpoints, - }) async { - + required List utxos, + required List outputs, + required BasedUtxoNetwork network, + String? memo, + required int feeRate, + List? inputPrivKeyInfos, + List? vinOutpoints, + }) async { int estimatedSize; if (network is BitcoinCashNetwork) { estimatedSize = ForkedTransactionBuilder.estimateTransactionSize( @@ -1707,8 +1704,9 @@ abstract class ElectrumWalletBase } Future fetchBalances() async { - final addresses = walletAddresses.allAddresses.where((address) => - addressTypeFromStr(address.address, network) is! MwebAddress).toList(); + final addresses = walletAddresses.allAddresses + .where((address) => addressTypeFromStr(address.address, network) is! MwebAddress) + .toList(); final balanceFutures = >>[]; for (var i = 0; i < addresses.length; i++) { final addressRecord = addresses[i]; diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index d5c70bd44..2b670600e 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -149,10 +149,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final stub = await CwMweb.stub(); _syncTimer?.cancel(); _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { - print(syncStatus); if (syncStatus is FailedSyncStatus) return; final height = await electrumClient.getCurrentBlockChainTip() ?? 0; final resp = await stub.status(StatusRequest()); + print("stats:"); + print(resp.mwebHeaderHeight); + print(resp.mwebUtxosHeight); + print(height); if (resp.blockHeaderHeight < height) { int h = resp.blockHeaderHeight; syncStatus = SyncingSyncStatus(height - h, h / height); From 126323584e603b72128b333f98b91ff5d1e42545 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 31 May 2024 10:38:21 -0700 Subject: [PATCH 039/100] silent payment fixes [skip ci] --- cw_bitcoin/lib/bitcoin_receive_page_option.dart | 2 ++ cw_bitcoin/lib/litecoin_wallet.dart | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_receive_page_option.dart b/cw_bitcoin/lib/bitcoin_receive_page_option.dart index ef2ab2866..8e72bc7b0 100644 --- a/cw_bitcoin/lib/bitcoin_receive_page_option.dart +++ b/cw_bitcoin/lib/bitcoin_receive_page_option.dart @@ -45,6 +45,8 @@ class BitcoinReceivePageOption implements ReceivePageOption { return P2shAddressType.p2wpkhInP2sh; case BitcoinReceivePageOption.silent_payments: return SilentPaymentsAddresType.p2sp; + case BitcoinReceivePageOption.mweb: + return SegwitAddresType.mweb; case BitcoinReceivePageOption.p2wpkh: default: return SegwitAddresType.p2wpkh; diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 2b670600e..d4adb1035 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -152,10 +152,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (syncStatus is FailedSyncStatus) return; final height = await electrumClient.getCurrentBlockChainTip() ?? 0; final resp = await stub.status(StatusRequest()); - print("stats:"); - print(resp.mwebHeaderHeight); - print(resp.mwebUtxosHeight); - print(height); + // print("stats:"); + // print(resp.mwebHeaderHeight); + // print(resp.mwebUtxosHeight); + // print(height); if (resp.blockHeaderHeight < height) { int h = resp.blockHeaderHeight; syncStatus = SyncingSyncStatus(height - h, h / height); From f354ae8c7df4f2b5ec0a1f69fdcc3b31102c7209 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Sat, 8 Jun 2024 14:26:48 +0200 Subject: [PATCH 040/100] updates [skip ci] --- cw_bitcoin/lib/litecoin_wallet.dart | 24 ++++++++++++++++-- cw_core/lib/hive_type_ids.dart | 1 + cw_core/lib/mweb_utxo.dart | 25 +++++++++++++++++++ .../com/cakewallet/mweb/CwMwebPlugin.kt | 1 + cw_mweb/ios/Classes/CwMwebPlugin.swift | 6 +++++ scripts/android/build_mwebd.sh | 7 ++++++ 6 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 cw_core/lib/mweb_utxo.dart create mode 100644 scripts/android/build_mwebd.sh diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index d4adb1035..61f75d211 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -3,6 +3,8 @@ import 'dart:math'; import 'package:collection/collection.dart'; import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart'; +import 'package:cw_core/cake_hive.dart'; +import 'package:cw_core/mweb_utxo.dart'; import 'package:fixnum/fixnum.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; @@ -77,6 +79,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } final bitcoin.HDWallet mwebHd; + late final Box mwebUtxosBox; Timer? _syncTimer; static Future create( @@ -140,7 +143,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } + final Map mwebUtxos = {}; int mwebUtxosHeight = 0; + int lastMwebUtxosHeight = 2699272; @action @override @@ -187,8 +192,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { processMwebUtxos(); } - final Map mwebUtxos = {}; - int lastMwebUtxosHeight = 0; + Future initMwebUtxosBox() async { + final boxName = "${walletInfo.name.replaceAll(" ", "_")}_${MwebUtxo.boxName}"; + + mwebUtxosBox = await CakeHive.openBox(boxName); + } + + // final Map mwebUtxo = MwebUtxo(); + + @override + Future init() async { + await initMwebUtxosBox(); + } Future processMwebUtxos() async { final stub = await CwMweb.stub(); @@ -487,4 +502,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await updateBalance(); }); } + + @override + Future save() async { + await super.save(); + } } diff --git a/cw_core/lib/hive_type_ids.dart b/cw_core/lib/hive_type_ids.dart index 4da616a79..6432c484b 100644 --- a/cw_core/lib/hive_type_ids.dart +++ b/cw_core/lib/hive_type_ids.dart @@ -18,3 +18,4 @@ const SPL_TOKEN_TYPE_ID = 16; const DERIVATION_INFO_TYPE_ID = 17; const TRON_TOKEN_TYPE_ID = 18; const HARDWARE_WALLET_TYPE_TYPE_ID = 19; +const MWEB_UTXO_TYPE_ID = 20; \ No newline at end of file diff --git a/cw_core/lib/mweb_utxo.dart b/cw_core/lib/mweb_utxo.dart new file mode 100644 index 000000000..02f162a37 --- /dev/null +++ b/cw_core/lib/mweb_utxo.dart @@ -0,0 +1,25 @@ +import 'package:cw_core/hive_type_ids.dart'; +import 'package:hive/hive.dart'; + +// part 'mweb_utxo.g.dart'; + +@HiveType(typeId: MWEB_UTXO_TYPE_ID) +class MwebUtxo extends HiveObject { + MwebUtxo({ + required this.address, + this.accountIndex, + required this.label, + }); + + static const typeId = MWEB_UTXO_TYPE_ID; + static const boxName = 'MwebUtxo'; + + @HiveField(0) + int? accountIndex; + + @HiveField(1, defaultValue: '') + String address; + + @HiveField(2, defaultValue: '') + String label; +} diff --git a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt index d3d4140ef..fa4fc909f 100644 --- a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt +++ b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt @@ -40,5 +40,6 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { channel.setMethodCallHandler(null) server?.stop() + server = null } } diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index a5fa63c5f..fa1ac39ad 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -49,4 +49,10 @@ public class CwMwebPlugin: NSObject, FlutterPlugin { result(FlutterMethodNotImplemented) } } + + deinit { + // Perform cleanup tasks + CwMwebPlugin.server?.stop() + CwMwebPlugin.server = nil + } } diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh new file mode 100644 index 000000000..412bef973 --- /dev/null +++ b/scripts/android/build_mwebd.sh @@ -0,0 +1,7 @@ +git clone https://github.com/ltcmweb/mwebd +cd mwebd +go install github.com/ltcmweb/mwebd/cmd/mwebd@latest +gomobile bind -target=android -androidapi 19 github.com/ltcmweb/mwebd + +mkdir -p ../../../cw_mweb/android/libs/ +mv ./mwebd.aar $_ \ No newline at end of file From b3e0c0b734d11000ad943e899b40923dd9690b2f Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Sat, 8 Jun 2024 15:00:26 +0200 Subject: [PATCH 041/100] save [skip ci] --- scripts/android/build_mwebd.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) mode change 100644 => 100755 scripts/android/build_mwebd.sh diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh old mode 100644 new mode 100755 index 412bef973..8a5e6813f --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -1,7 +1,6 @@ git clone https://github.com/ltcmweb/mwebd cd mwebd go install github.com/ltcmweb/mwebd/cmd/mwebd@latest -gomobile bind -target=android -androidapi 19 github.com/ltcmweb/mwebd - +gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd mkdir -p ../../../cw_mweb/android/libs/ mv ./mwebd.aar $_ \ No newline at end of file From cb84a47fc78f1c4c2aa7ade6a906454cddc69cff Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 18 Jun 2024 19:07:13 -0700 Subject: [PATCH 042/100] use mwebutxos box --- cw_bitcoin/lib/litecoin_wallet.dart | 41 ++++++++++++++++++----------- cw_core/lib/mweb_utxo.dart | 22 +++++++++++----- lib/main.dart | 5 ++++ 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 61f75d211..0e1cf6f06 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -143,7 +143,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } - final Map mwebUtxos = {}; + // final Map mwebUtxos = {}; int mwebUtxosHeight = 0; int lastMwebUtxosHeight = 2699272; @@ -198,8 +198,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { mwebUtxosBox = await CakeHive.openBox(boxName); } - // final Map mwebUtxo = MwebUtxo(); - @override Future init() async { await initMwebUtxosBox(); @@ -210,15 +208,22 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final scanSecret = mwebHd.derive(0x80000000).privKey!; final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: lastMwebUtxosHeight); var initDone = false; - await for (var utxo in stub.utxos(req)) { - if (utxo.address.isEmpty) { + await for (Utxo sUtxo in stub.utxos(req)) { + if (sUtxo.address.isEmpty) { await updateUnspent(); await updateBalance(); initDone = true; } final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; - if (!mwebAddrs.contains(utxo.address)) continue; - mwebUtxos[utxo.outputId] = utxo; + if (!mwebAddrs.contains(sUtxo.address)) continue; + final utxo = MwebUtxo( + address: sUtxo.address, + blockTime: sUtxo.blockTime, + height: sUtxo.height, + outputId: sUtxo.outputId, + value: sUtxo.value.toInt(), + ); + mwebUtxosBox.put(utxo.outputId, utxo); final status = await stub.status(StatusRequest()); var date = DateTime.now(); @@ -272,7 +277,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { .map(checkPendingTransaction))) .any((x) => x)); final outputIds = - mwebUtxos.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId).toList(); + mwebUtxosBox.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId); + final stub = await CwMweb.stub(); final resp = await stub.spent(SpentRequest(outputId: outputIds)); final spent = resp.outputId; @@ -286,7 +292,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { var output = AccumulatorSink(); var input = sha256.startChunkedConversion(output); for (final outputId in spent) { - final utxo = mwebUtxos[outputId]; + final utxo = mwebUtxosBox.get(outputId); if (utxo == null) continue; final addressRecord = walletAddresses.allAddresses .firstWhere((addressRecord) => addressRecord.address == utxo.address); @@ -294,7 +300,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { addressRecord.balance -= utxo.value.toInt(); amount += utxo.value.toInt(); inputAddresses.add(utxo.address); - mwebUtxos.remove(outputId); + mwebUtxosBox.delete(outputId); input.add(hex.decode(outputId)); } if (inputAddresses.isEmpty) return; @@ -347,7 +353,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await super.updateUnspent(); await checkMwebUtxosSpent(); final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; - mwebUtxos.forEach((outputId, utxo) { + mwebUtxosBox.keys.forEach((dynamic oId) { + final String outputId = oId as String; + final utxo = mwebUtxosBox.get(outputId); + if (utxo == null) return; final addressRecord = walletAddresses.allAddresses .firstWhere((addressRecord) => addressRecord.address == utxo.address); final unspent = BitcoinUnspent( @@ -362,11 +371,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final balance = await super.fetchBalances(); var confirmed = balance.confirmed; var unconfirmed = balance.unconfirmed; - mwebUtxos.values.forEach((utxo) { - if (utxo.height > 0) + mwebUtxosBox.values.forEach((utxo) { + if (utxo.height > 0) { confirmed += utxo.value.toInt(); - else + } else { unconfirmed += utxo.value.toInt(); + } }); return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } @@ -486,7 +496,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ..addListener((transaction) async { final addresses = {}; transaction.inputAddresses?.forEach((id) { - final utxo = mwebUtxos.remove(id); + final utxo = mwebUtxosBox.get(id); if (utxo == null) return; final addressRecord = walletAddresses.allAddresses .firstWhere((addressRecord) => addressRecord.address == utxo.address); @@ -495,6 +505,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { addresses.add(utxo.address); } addressRecord.balance -= utxo.value.toInt(); + mwebUtxosBox.delete(id); }); transaction.inputAddresses?.addAll(addresses); transactionHistory.addOne(transaction); diff --git a/cw_core/lib/mweb_utxo.dart b/cw_core/lib/mweb_utxo.dart index 02f162a37..f8dfab395 100644 --- a/cw_core/lib/mweb_utxo.dart +++ b/cw_core/lib/mweb_utxo.dart @@ -1,25 +1,33 @@ import 'package:cw_core/hive_type_ids.dart'; import 'package:hive/hive.dart'; -// part 'mweb_utxo.g.dart'; +part 'mweb_utxo.g.dart'; @HiveType(typeId: MWEB_UTXO_TYPE_ID) class MwebUtxo extends HiveObject { MwebUtxo({ + required this.height, + required this.value, required this.address, - this.accountIndex, - required this.label, + required this.outputId, + required this.blockTime, }); static const typeId = MWEB_UTXO_TYPE_ID; static const boxName = 'MwebUtxo'; @HiveField(0) - int? accountIndex; + int height; - @HiveField(1, defaultValue: '') + @HiveField(1) + int value; + + @HiveField(2) String address; - @HiveField(2, defaultValue: '') - String label; + @HiveField(3) + String outputId; + + @HiveField(4) + int blockTime; } diff --git a/lib/main.dart b/lib/main.dart index 7e56f35e4..eb7bca5a6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,7 @@ import 'package:cake_wallet/view_model/link_view_model.dart'; import 'package:cw_core/address_info.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/hive_type_ids.dart'; +import 'package:cw_core/mweb_utxo.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -166,6 +167,10 @@ Future initializeAppConfigs() async { CakeHive.registerAdapter(AnonpayInvoiceInfoAdapter()); } + if (!CakeHive.isAdapterRegistered(MwebUtxo.typeId)) { + CakeHive.registerAdapter(MwebUtxoAdapter()); + } + final secureStorage = secureStorageShared; final transactionDescriptionsBoxKey = From e595d2d6cd0a2e5b2d0af00ab258ca8f031adaf4 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 24 Jun 2024 12:48:42 -0700 Subject: [PATCH 043/100] [skip ci] lots of fixes, still testing --- cw_bitcoin/lib/litecoin_wallet.dart | 236 +++++++++++------- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 11 +- .../lib/pending_bitcoin_transaction.dart | 1 + 3 files changed, 155 insertions(+), 93 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 0e1cf6f06..5a58196e1 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -51,18 +51,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, + int? initialMwebHeight, }) : mwebHd = bitcoin.HDWallet.fromSeed(seedBytes, network: litecoinNetwork).derivePath("m/1000'"), super( - mnemonic: mnemonic, - password: password, - walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfo, - networkType: litecoinNetwork, - initialAddresses: initialAddresses, - initialBalance: initialBalance, - seedBytes: seedBytes, - currency: CryptoCurrency.ltc) { + mnemonic: mnemonic, + password: password, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfo, + networkType: litecoinNetwork, + initialAddresses: initialAddresses, + initialBalance: initialBalance, + seedBytes: seedBytes, + currency: CryptoCurrency.ltc, + ) { walletAddresses = LitecoinWalletAddresses( walletInfo, initialAddresses: initialAddresses, @@ -81,6 +83,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final bitcoin.HDWallet mwebHd; late final Box mwebUtxosBox; Timer? _syncTimer; + // late int lastMwebUtxosHeight; + int mwebUtxosHeight = 0; static Future create( {required String mnemonic, @@ -143,10 +147,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } - // final Map mwebUtxos = {}; - int mwebUtxosHeight = 0; - int lastMwebUtxosHeight = 2699272; - @action @override Future startSync() async { @@ -158,7 +158,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final height = await electrumClient.getCurrentBlockChainTip() ?? 0; final resp = await stub.status(StatusRequest()); // print("stats:"); - // print(resp.mwebHeaderHeight); + // print("???????????????????"); + // print(resp.blockHeaderHeight); // print(resp.mwebUtxosHeight); // print(height); if (resp.blockHeaderHeight < height) { @@ -183,6 +184,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final confirmations = mwebUtxosHeight - transaction.height + 1; if (transaction.confirmations == confirmations) continue; transaction.confirmations = confirmations; + print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@4"); transactionHistory.addOne(transaction); } await transactionHistory.save(); @@ -206,24 +208,16 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future processMwebUtxos() async { final stub = await CwMweb.stub(); final scanSecret = mwebHd.derive(0x80000000).privKey!; - final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: lastMwebUtxosHeight); - var initDone = false; - await for (Utxo sUtxo in stub.utxos(req)) { - if (sUtxo.address.isEmpty) { - await updateUnspent(); - await updateBalance(); + print("SCANNING FROM HEIGHT: ${walletInfo.restoreHeight}"); + final req = + UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: walletInfo.restoreHeight); + bool initDone = false; + + for (final utxo in mwebUtxosBox.values) { + if (utxo.address.isEmpty) { initDone = true; + continue; } - final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; - if (!mwebAddrs.contains(sUtxo.address)) continue; - final utxo = MwebUtxo( - address: sUtxo.address, - blockTime: sUtxo.blockTime, - height: sUtxo.height, - outputId: sUtxo.outputId, - value: sUtxo.value.toInt(), - ); - mwebUtxosBox.put(utxo.outputId, utxo); final status = await stub.status(StatusRequest()); var date = DateTime.now(); @@ -234,26 +228,33 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } var tx = transactionHistory.transactions.values .firstWhereOrNull((tx) => tx.outputAddresses?.contains(utxo.outputId) ?? false); - if (tx == null) - tx = ElectrumTransactionInfo(WalletType.litecoin, - id: utxo.outputId, - height: utxo.height, - amount: utxo.value.toInt(), - fee: 0, - direction: TransactionDirection.incoming, - isPending: utxo.height == 0, - date: date, - confirmations: confirmations, - inputAddresses: [], - outputAddresses: [utxo.outputId]); + + if (tx == null) { + tx = ElectrumTransactionInfo( + WalletType.litecoin, + id: utxo.outputId, + height: utxo.height, + amount: utxo.value.toInt(), + fee: 0, + direction: TransactionDirection.incoming, + isPending: utxo.height == 0, + date: date, + confirmations: confirmations, + inputAddresses: [], + outputAddresses: [utxo.outputId], + ); + } + tx.height = utxo.height; tx.isPending = utxo.height == 0; tx.confirmations = confirmations; - var isNew = transactionHistory.transactions[tx.id] == null; + bool isNew = transactionHistory.transactions[tx.id] == null; + if (!(tx.outputAddresses?.contains(utxo.address) ?? false)) { tx.outputAddresses?.add(utxo.address); isNew = true; } + if (isNew) { final addressRecord = walletAddresses.allAddresses .firstWhere((addressRecord) => addressRecord.address == utxo.address); @@ -261,13 +262,39 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); } + print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@"); transactionHistory.addOne(tx); if (initDone) { await updateUnspent(); await updateBalance(); } - lastMwebUtxosHeight = utxo.height; - print("latest mweb utxo height: $lastMwebUtxosHeight"); + + if (utxo.height > walletInfo.restoreHeight) { + walletInfo.updateRestoreHeight(utxo.height); + } + } + + await for (Utxo sUtxo in stub.utxos(req)) { + final utxo = MwebUtxo( + address: sUtxo.address, + blockTime: sUtxo.blockTime, + height: sUtxo.height, + outputId: sUtxo.outputId, + value: sUtxo.value.toInt(), + ); + + if (utxo.address.isEmpty) { + await updateUnspent(); + await updateBalance(); + initDone = true; + } + + final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; + + if (!mwebAddrs.contains(utxo.address) && utxo.address.isNotEmpty) { + continue; + } + await mwebUtxosBox.put(utxo.outputId, utxo); } } @@ -277,7 +304,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { .map(checkPendingTransaction))) .any((x) => x)); final outputIds = - mwebUtxosBox.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId); + mwebUtxosBox.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId).toList(); final stub = await CwMweb.stub(); final resp = await stub.spent(SpentRequest(outputId: outputIds)); @@ -293,6 +320,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { var input = sha256.startChunkedConversion(output); for (final outputId in spent) { final utxo = mwebUtxosBox.get(outputId); + await mwebUtxosBox.delete(outputId); if (utxo == null) continue; final addressRecord = walletAddresses.allAddresses .firstWhere((addressRecord) => addressRecord.address == utxo.address); @@ -300,7 +328,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { addressRecord.balance -= utxo.value.toInt(); amount += utxo.value.toInt(); inputAddresses.add(utxo.address); - mwebUtxosBox.delete(outputId); input.add(hex.decode(outputId)); } if (inputAddresses.isEmpty) return; @@ -317,6 +344,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { confirmations: 1, inputAddresses: inputAddresses.toList(), outputAddresses: []); + print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@2"); + transactionHistory.addOne(tx); await transactionHistory.save(); } @@ -356,9 +385,25 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { mwebUtxosBox.keys.forEach((dynamic oId) { final String outputId = oId as String; final utxo = mwebUtxosBox.get(outputId); - if (utxo == null) return; + if (utxo == null) { + return; + } + if (utxo.address.isEmpty) { + // not sure if a bug or a special case but we definitely ignore these + return; + } final addressRecord = walletAddresses.allAddresses - .firstWhere((addressRecord) => addressRecord.address == utxo.address); + .firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address); + + // print("^^^^^^^^^^^^^^^^^^"); + // print(utxo.address); + // for (var a in walletAddresses.allAddresses) { + // print(a.address); + // } + if (addressRecord == null) { + print("addressRecord is null! TODO: handle this case"); + return; + } final unspent = BitcoinUnspent( addressRecord, outputId, utxo.value.toInt(), mwebAddrs.indexOf(utxo.address)); if (unspent.vout == 0) unspent.isChange = true; @@ -378,6 +423,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unconfirmed += utxo.value.toInt(); } }); + print("confirmed: $confirmed, unconfirmed: $unconfirmed"); return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } @@ -465,53 +511,63 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future createTransaction(Object credentials) async { - final tx = await super.createTransaction(credentials) as PendingBitcoinTransaction; - final stub = await CwMweb.stub(); - final resp = await stub.create(CreateRequest( + try { + final tx = await super.createTransaction(credentials) as PendingBitcoinTransaction; + final stub = await CwMweb.stub(); + final resp = await stub.create(CreateRequest( rawTx: hex.decode(tx.hex), scanSecret: hex.decode(mwebHd.derive(0x80000000).privKey!), spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!), - feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000)); - final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); - tx.hexOverride = tx2 - .copyWith( - witnesses: tx2.inputs.asMap().entries.map((e) { - final utxo = unspentCoins - .firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex); - final key = generateECPrivate( - hd: utxo.bitcoinAddressRecord.isHidden - ? walletAddresses.sideHd - : walletAddresses.mainHd, - index: utxo.bitcoinAddressRecord.index, - network: network); - final digest = tx2.getTransactionSegwitDigit( + feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000, + )); + final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); + tx.hexOverride = tx2 + .copyWith( + witnesses: tx2.inputs.asMap().entries.map((e) { + final utxo = unspentCoins + .firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex); + final key = generateECPrivate( + hd: utxo.bitcoinAddressRecord.isHidden + ? walletAddresses.sideHd + : walletAddresses.mainHd, + index: utxo.bitcoinAddressRecord.index, + network: network); + final digest = tx2.getTransactionSegwitDigit( txInIndex: e.key, script: key.getPublic().toP2pkhAddress().toScriptPubKey(), - amount: BigInt.from(utxo.value)); - return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]); - }).toList()) - .toHex(); - tx.outputs = resp.outputId; - return tx - ..addListener((transaction) async { - final addresses = {}; - transaction.inputAddresses?.forEach((id) { - final utxo = mwebUtxosBox.get(id); - if (utxo == null) return; - final addressRecord = walletAddresses.allAddresses - .firstWhere((addressRecord) => addressRecord.address == utxo.address); - if (!addresses.contains(utxo.address)) { - addressRecord.txCount++; - addresses.add(utxo.address); - } - addressRecord.balance -= utxo.value.toInt(); - mwebUtxosBox.delete(id); + amount: BigInt.from(utxo.value), + ); + return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]); + }).toList()) + .toHex(); + tx.outputs = resp.outputId; + return tx + ..addListener((transaction) async { + final addresses = {}; + transaction.inputAddresses?.forEach((id) async { + final utxo = mwebUtxosBox.get(id); + await mwebUtxosBox.delete(id); + if (utxo == null) return; + final addressRecord = walletAddresses.allAddresses + .firstWhere((addressRecord) => addressRecord.address == utxo.address); + if (!addresses.contains(utxo.address)) { + addressRecord.txCount++; + addresses.add(utxo.address); + } + addressRecord.balance -= utxo.value.toInt(); + }); + transaction.inputAddresses?.addAll(addresses); + print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@3"); + + transactionHistory.addOne(transaction); + await updateUnspent(); + await updateBalance(); }); - transaction.inputAddresses?.addAll(addresses); - transactionHistory.addOne(transaction); - await updateUnspent(); - await updateBalance(); - }); + } catch (e, s) { + print(e); + print(s); + rethrow; + } } @override diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index f1b05c5bf..d405ad58f 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -22,7 +22,9 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialAddresses, super.initialRegularAddressIndex, super.initialChangeAddressIndex, - }) : super(walletInfo); + }) : super(walletInfo) { + topUpMweb(0); + } final HDWallet mwebHd; List mwebAddrs = []; @@ -49,13 +51,14 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with String getAddress({required int index, required HDWallet hd, BitcoinAddressType? addressType}) { if (addressType == SegwitAddresType.mweb) { topUpMweb(index); - return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index+1]; + return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1]; } return generateP2WPKHAddress(hd: hd, index: index, network: network); } @override - Future getAddressAsync({required int index, required HDWallet hd, BitcoinAddressType? addressType}) async { + Future getAddressAsync( + {required int index, required HDWallet hd, BitcoinAddressType? addressType}) async { if (addressType == SegwitAddresType.mweb) { await topUpMweb(index); } @@ -65,6 +68,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with @action @override Future getChangeAddress() async { + updateChangeAddresses(); + // this means all change addresses used will be mweb addresses!: await topUpMweb(0); return mwebAddrs[0]; } diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index ab05621ac..dfc032c24 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -103,6 +103,7 @@ class PendingBitcoinTransaction with PendingTransaction { await _commit(); } + _listeners.forEach((listener) => listener(transactionInfo())); } From 29f77aae22750e0a13e6ca36ee2efd2d2f2367c6 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 09:16:54 -0700 Subject: [PATCH 044/100] add rescan from height feature and test workflow build --- .github/workflows/pr_test_build.yml | 10 +++++ cw_bitcoin/lib/electrum_wallet.dart | 41 ++++--------------- cw_bitcoin/lib/litecoin_wallet.dart | 34 +++++++++++---- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 1 + .../lib/pending_bitcoin_transaction.dart | 15 ++++--- lib/src/screens/rescan/rescan_page.dart | 1 + lib/src/widgets/blockchain_height_widget.dart | 7 +++- .../dashboard/dashboard_view_model.dart | 1 + lib/view_model/rescan_view_model.dart | 3 ++ 9 files changed, 68 insertions(+), 45 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 841ea570d..7c8329328 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -163,6 +163,16 @@ jobs: run: | echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties + - name: build mweb + run: | + cd /opt/android/cake_wallet + git clone https://github.com/ltcmweb/mwebd + cd /opt/android/cake_wallet/mwebd + go install github.com/ltcmweb/mwebd/cmd/mwebd@latest + gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd + mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ + mv ./mwebd.aar $_ + - name: Build run: | cd /opt/android/cake_wallet diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index fd19edfc7..f9079b749 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -712,26 +712,13 @@ abstract class ElectrumWalletBase value: BigInt.from(amountLeftForChangeAndFee), )); - int estimatedSize; - if (network is BitcoinCashNetwork) { - estimatedSize = ForkedTransactionBuilder.estimateTransactionSize( - utxos: utxoDetails.utxos, - outputs: outputs, - network: network as BitcoinCashNetwork, - memo: memo, - ); - } else { - estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize( - utxos: utxoDetails.utxos, - outputs: outputs, - network: network, - memo: memo, - inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos, - vinOutpoints: utxoDetails.vinOutpoints, - ); - } - - int fee = feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize); + int fee = await calcFee( + utxos: utxoDetails.utxos, + outputs: outputs, + network: network, + memo: memo, + feeRate: feeRate, + ); if (fee == 0) { throw BitcoinTransactionNoFeeException(); @@ -741,6 +728,8 @@ abstract class ElectrumWalletBase final lastOutput = outputs.last; final amountLeftForChange = amountLeftForChangeAndFee - fee; + print(amountLeftForChangeAndFee); + if (!_isBelowDust(amountLeftForChange)) { // Here, lastOutput already is change, return the amount left without the fee to the user's address. outputs[outputs.length - 1] = @@ -1786,18 +1775,6 @@ abstract class ElectrumWalletBase await save(); } - String getChangeAddress() { - const minCountOfHiddenAddresses = 5; - final random = Random(); - var addresses = walletAddresses.allAddresses.where((addr) => addr.isHidden).toList(); - - if (addresses.length < minCountOfHiddenAddresses) { - addresses = walletAddresses.allAddresses.toList(); - } - - return addresses[random.nextInt(addresses.length)].address; - } - @override void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError; diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 5a58196e1..6a78af0f2 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -184,7 +184,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final confirmations = mwebUtxosHeight - transaction.height + 1; if (transaction.confirmations == confirmations) continue; transaction.confirmations = confirmations; - print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@4"); transactionHistory.addOne(transaction); } await transactionHistory.save(); @@ -200,6 +199,22 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { mwebUtxosBox = await CakeHive.openBox(boxName); } + @action + @override + Future rescan({ + required int height, + int? chainTip, + ScanData? scanData, + bool? doSingleScan, + bool? usingElectrs, + }) async { + await mwebUtxosBox.clear(); + mwebUtxosHeight = height; + walletInfo.restoreHeight = height; + await walletInfo.save(); + processMwebUtxos(); + } + @override Future init() async { await initMwebUtxosBox(); @@ -208,9 +223,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future processMwebUtxos() async { final stub = await CwMweb.stub(); final scanSecret = mwebHd.derive(0x80000000).privKey!; - print("SCANNING FROM HEIGHT: ${walletInfo.restoreHeight}"); - final req = - UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: walletInfo.restoreHeight); + int restoreHeight = walletInfo.restoreHeight; + print("SCANNING FROM HEIGHT: $restoreHeight"); + final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: restoreHeight); bool initDone = false; for (final utxo in mwebUtxosBox.values) { @@ -257,7 +272,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (isNew) { final addressRecord = walletAddresses.allAddresses - .firstWhere((addressRecord) => addressRecord.address == utxo.address); + .firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address); + if (addressRecord == null) { + print("addressRecord is null! TODO: handle this case 1"); + continue; + } if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) addressRecord.txCount++; addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); @@ -401,7 +420,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // print(a.address); // } if (addressRecord == null) { - print("addressRecord is null! TODO: handle this case"); + print("addressRecord is null! TODO: handle this case2"); return; } final unspent = BitcoinUnspent( @@ -423,7 +442,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unconfirmed += utxo.value.toInt(); } }); - print("confirmed: $confirmed, unconfirmed: $unconfirmed"); + // print("confirmed: $confirmed, unconfirmed: $unconfirmed"); return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } @@ -513,6 +532,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future createTransaction(Object credentials) async { try { final tx = await super.createTransaction(credentials) as PendingBitcoinTransaction; + final stub = await CwMweb.stub(); final resp = await stub.create(CreateRequest( rawTx: hex.decode(tx.hex), diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index d405ad58f..a1089f473 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -68,6 +68,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with @action @override Future getChangeAddress() async { + // super.getChangeAddress(); updateChangeAddresses(); // this means all change addresses used will be mweb addresses!: await topUpMweb(0); diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index dfc032c24..4217d35b4 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -32,8 +32,8 @@ class PendingBitcoinTransaction with PendingTransaction { final int fee; final String feeRate; final BasedUtxoNetwork? network; - final bool hasChange; final bool isSendAll; + final bool hasChange; final bool hasTaprootInputs; String? idOverride; String? hexOverride; @@ -91,19 +91,24 @@ class PendingBitcoinTransaction with PendingTransaction { } } - @override - Future commit() async { - if (network is LitecoinNetwork) try { + Future _ltcCommit() async { + try { final stub = await CwMweb.stub(); final resp = await stub.broadcast(BroadcastRequest(rawTx: BytesUtils.fromHexString(hex))); idOverride = resp.txid; } on GrpcError catch (e) { throw BitcoinTransactionCommitFailed(errorMessage: e.message); + } + } + + @override + Future commit() async { + if (network is LitecoinNetwork) { + await _ltcCommit(); } else { await _commit(); } - _listeners.forEach((listener) => listener(transactionInfo())); } diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index c59ae4ad0..9f32c9067 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -30,6 +30,7 @@ class RescanPage extends BasePage { key: _blockchainHeightWidgetKey, onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value, isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan, + isMwebScan: _rescanViewModel.isMwebScan, doSingleScan: _rescanViewModel.doSingleScan, toggleSingleScan: () => _rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan, diff --git a/lib/src/widgets/blockchain_height_widget.dart b/lib/src/widgets/blockchain_height_widget.dart index d85680cc8..1bfc7c0cd 100644 --- a/lib/src/widgets/blockchain_height_widget.dart +++ b/lib/src/widgets/blockchain_height_widget.dart @@ -16,6 +16,7 @@ class BlockchainHeightWidget extends StatefulWidget { this.onHeightOrDateEntered, this.hasDatePicker = true, this.isSilentPaymentsScan = false, + this.isMwebScan = false, this.toggleSingleScan, this.doSingleScan = false, }) : super(key: key); @@ -25,6 +26,7 @@ class BlockchainHeightWidget extends StatefulWidget { final FocusNode? focusNode; final bool hasDatePicker; final bool isSilentPaymentsScan; + final bool isMwebScan; final bool doSingleScan; final Function()? toggleSingleScan; @@ -157,7 +159,10 @@ class BlockchainHeightState extends State { if (date != null) { int height; - if (widget.isSilentPaymentsScan) { + if (widget.isMwebScan) { + throw UnimplementedError(); + // height = bitcoin!.getMwebHeightByDate(date: date); + } else if (widget.isSilentPaymentsScan) { height = bitcoin!.getHeightByDate(date: date); } else { height = monero!.getHeightByDate(date: date); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index b59dd1592..fd4f1ede2 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -303,6 +303,7 @@ abstract class DashboardViewModelBase with Store { bool get hasRescan => wallet.type == WalletType.bitcoin || wallet.type == WalletType.monero || + wallet.type == WalletType.litecoin || wallet.type == WalletType.haven; @computed diff --git a/lib/view_model/rescan_view_model.dart b/lib/view_model/rescan_view_model.dart index dcc81c0a0..3d8cf39f3 100644 --- a/lib/view_model/rescan_view_model.dart +++ b/lib/view_model/rescan_view_model.dart @@ -29,6 +29,9 @@ abstract class RescanViewModelBase with Store { @computed bool get isSilentPaymentsScan => wallet.type == WalletType.bitcoin; + @computed + bool get isMwebScan => wallet.type == WalletType.litecoin; + @action Future rescanCurrentWallet({required int restoreHeight}) async { state = RescanWalletState.rescaning; From 829efe51272e4ed8976f9dc278a616d43abee5f5 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 09:30:54 -0700 Subject: [PATCH 045/100] install go --- .github/workflows/pr_test_build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 7c8329328..331a66279 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -165,6 +165,7 @@ jobs: - name: build mweb run: | + apt-get install -y go cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd From 9bc7dc6841ebee8613f7ba29728155659da596d2 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 09:50:31 -0700 Subject: [PATCH 046/100] use sudo --- .github/workflows/pr_test_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 331a66279..ac1831718 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -165,7 +165,7 @@ jobs: - name: build mweb run: | - apt-get install -y go + sudo apt-get install -y go cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd From 57cfeaed57521874cb8b97b0ed876ff99a81decc Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 09:52:46 -0700 Subject: [PATCH 047/100] correct package name --- .github/workflows/pr_test_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index ac1831718..2d1b06775 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -165,7 +165,7 @@ jobs: - name: build mweb run: | - sudo apt-get install -y go + sudo apt-get install -y golang-go cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd From a6d604b071e6f642e7d28d4412ac2d4e8822656c Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 09:54:17 -0700 Subject: [PATCH 048/100] move building mweb higher for faster testing --- .github/workflows/pr_test_build.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 2d1b06775..3184f6432 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -87,6 +87,17 @@ jobs: cd /opt/android/cake_wallet flutter pub get + - name: build mweb + run: | + sudo apt-get install -y golang-go + cd /opt/android/cake_wallet + git clone https://github.com/ltcmweb/mwebd + cd /opt/android/cake_wallet/mwebd + go install github.com/ltcmweb/mwebd/cmd/mwebd@latest + gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd + mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ + mv ./mwebd.aar $_ + - name: Generate KeyStore run: | cd /opt/android/cake_wallet/android/app @@ -163,17 +174,6 @@ jobs: run: | echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties - - name: build mweb - run: | - sudo apt-get install -y golang-go - cd /opt/android/cake_wallet - git clone https://github.com/ltcmweb/mwebd - cd /opt/android/cake_wallet/mwebd - go install github.com/ltcmweb/mwebd/cmd/mwebd@latest - gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd - mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ - mv ./mwebd.aar $_ - - name: Build run: | cd /opt/android/cake_wallet From 61bffc11aeae3510205a6192683e8a304fae5de1 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 10:13:32 -0700 Subject: [PATCH 049/100] install fixes --- .github/workflows/pr_test_build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 3184f6432..6a43fa6e7 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -93,7 +93,8 @@ jobs: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd - go install github.com/ltcmweb/mwebd/cmd/mwebd@latest + go get github.com/ltcmweb/mwebd/cmd/mwebd@latest + go install github.com/ltcmweb/mwebd/cmd/mwebd gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ From a12580088b54fb863c256c5b0a4c09e2fab66b8f Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 10:19:23 -0700 Subject: [PATCH 050/100] install later version of go --- .github/workflows/pr_test_build.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 6a43fa6e7..3b32db9d4 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -89,11 +89,15 @@ jobs: - name: build mweb run: | - sudo apt-get install -y golang-go + # install latest go: + wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz + sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz + export PATH=$PATH:/usr/local/go/bin + # install mwebd: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd - go get github.com/ltcmweb/mwebd/cmd/mwebd@latest + go get github.com/ltcmweb/mwebd/cmd/mwebd go install github.com/ltcmweb/mwebd/cmd/mwebd gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ From 216949bb28aa0be4b62149302e38a85493288c04 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 10:33:08 -0700 Subject: [PATCH 051/100] go fixes --- .github/workflows/pr_test_build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 3b32db9d4..1756b7ad9 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -93,12 +93,16 @@ jobs: wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz export PATH=$PATH:/usr/local/go/bin + go install golang.org/x/mobile/cmd/gomobile@latest # install mwebd: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd go get github.com/ltcmweb/mwebd/cmd/mwebd go install github.com/ltcmweb/mwebd/cmd/mwebd + go get golang.org/x/mobile/cmd/gobind + go get golang.org/x/mobile/cmd/gomobile + gomobile init gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ From 27787598a5793905aad24f8cd88eddf08707553c Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 11:20:45 -0700 Subject: [PATCH 052/100] testing --- .github/workflows/pr_test_build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 1756b7ad9..192b13bb5 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -19,6 +19,9 @@ jobs: PR_NUMBER: ${{ github.event.number }} steps: + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + - name: is pr if: github.event_name == 'pull_request' run: echo "BRANCH_NAME=${GITHUB_HEAD_REF}" >> $GITHUB_ENV From 5dbdb250c231ef312994f9f14d5da7c8636dd3a0 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 11:35:49 -0700 Subject: [PATCH 053/100] testing --- .github/workflows/pr_test_build.yml | 31 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 192b13bb5..d7dffce46 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -19,9 +19,6 @@ jobs: PR_NUMBER: ${{ github.event.number }} steps: - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - - name: is pr if: github.event_name == 'pull_request' run: echo "BRANCH_NAME=${GITHUB_HEAD_REF}" >> $GITHUB_ENV @@ -90,22 +87,34 @@ jobs: cd /opt/android/cake_wallet flutter pub get + - name: Install Gomobile + run: | + go install golang.org/x/mobile/cmd/gomobile@latest + go install golang.org/x/mobile/cmd/gobind@latest + go get golang.org/x/mobile/cmd/gobind + go get golang.org/x/mobile/cmd/gomobile + gomobile init + env: + GOPROXY: https://proxy.golang.org,direct + GO111MODULE: "on" + - name: build mweb run: | - # install latest go: - wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz - sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz - export PATH=$PATH:/usr/local/go/bin - go install golang.org/x/mobile/cmd/gomobile@latest + # # install latest go: + # wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz + # sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz + # export PATH=$PATH:/usr/local/go/bin + # go install golang.org/x/mobile/cmd/gomobile@latest + # install mwebd: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd go get github.com/ltcmweb/mwebd/cmd/mwebd go install github.com/ltcmweb/mwebd/cmd/mwebd - go get golang.org/x/mobile/cmd/gobind - go get golang.org/x/mobile/cmd/gomobile - gomobile init + # go get golang.org/x/mobile/cmd/gobind + # go get golang.org/x/mobile/cmd/gomobile + # gomobile init gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ From 0a1aee7e0f56d183ff5b33c78947190faa853952 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 11:41:07 -0700 Subject: [PATCH 054/100] testing --- .github/workflows/pr_test_build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index d7dffce46..d1d7e1817 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -88,6 +88,7 @@ jobs: flutter pub get - name: Install Gomobile + uses: actions/setup-go@v3 run: | go install golang.org/x/mobile/cmd/gomobile@latest go install golang.org/x/mobile/cmd/gobind@latest From f04f17375bf21ad18a7ae6f1b869ac8f54f3a94f Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 11:44:07 -0700 Subject: [PATCH 055/100] testing --- .github/workflows/pr_test_build.yml | 62 +++++++++++++++-------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index d1d7e1817..c84d9faad 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -27,6 +27,28 @@ jobs: if: github.event_name != 'pull_request' run: echo "BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_ENV + - name: build mweb + run: | + mkdir -p /opt/android/cake_wallet + # install latest go: + wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz + sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz + export PATH=$PATH:/usr/local/go/bin + go install golang.org/x/mobile/cmd/gomobile@latest + + # install mwebd: + cd /opt/android/cake_wallet + git clone https://github.com/ltcmweb/mwebd + cd /opt/android/cake_wallet/mwebd + go get github.com/ltcmweb/mwebd/cmd/mwebd + go install github.com/ltcmweb/mwebd/cmd/mwebd + go get golang.org/x/mobile/cmd/gobind + go get golang.org/x/mobile/cmd/gomobile + gomobile init + gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd + mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ + mv ./mwebd.aar $_ + - name: Free Up GitHub Actions Ubuntu Runner Disk Space run: | sudo rm -rf /usr/share/dotnet @@ -87,38 +109,18 @@ jobs: cd /opt/android/cake_wallet flutter pub get - - name: Install Gomobile - uses: actions/setup-go@v3 - run: | - go install golang.org/x/mobile/cmd/gomobile@latest - go install golang.org/x/mobile/cmd/gobind@latest - go get golang.org/x/mobile/cmd/gobind - go get golang.org/x/mobile/cmd/gomobile - gomobile init - env: - GOPROXY: https://proxy.golang.org,direct - GO111MODULE: "on" + # - name: Install Gomobile + # run: | + # go install golang.org/x/mobile/cmd/gomobile@latest + # go install golang.org/x/mobile/cmd/gobind@latest + # go get golang.org/x/mobile/cmd/gobind + # go get golang.org/x/mobile/cmd/gomobile + # gomobile init + # env: + # GOPROXY: https://proxy.golang.org,direct + # GO111MODULE: "on" - - name: build mweb - run: | - # # install latest go: - # wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz - # sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz - # export PATH=$PATH:/usr/local/go/bin - # go install golang.org/x/mobile/cmd/gomobile@latest - # install mwebd: - cd /opt/android/cake_wallet - git clone https://github.com/ltcmweb/mwebd - cd /opt/android/cake_wallet/mwebd - go get github.com/ltcmweb/mwebd/cmd/mwebd - go install github.com/ltcmweb/mwebd/cmd/mwebd - # go get golang.org/x/mobile/cmd/gobind - # go get golang.org/x/mobile/cmd/gomobile - # gomobile init - gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd - mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ - mv ./mwebd.aar $_ - name: Generate KeyStore run: | From 9ec108ab824603c8d9e178395447c5f80bd4d810 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 11:47:58 -0700 Subject: [PATCH 056/100] testing --- .github/workflows/pr_test_build.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index c84d9faad..fa06358c8 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -34,7 +34,9 @@ jobs: wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz export PATH=$PATH:/usr/local/go/bin + export PATH=$PATH:~/go/bin go install golang.org/x/mobile/cmd/gomobile@latest + gomobile init # install mwebd: cd /opt/android/cake_wallet @@ -42,9 +44,8 @@ jobs: cd /opt/android/cake_wallet/mwebd go get github.com/ltcmweb/mwebd/cmd/mwebd go install github.com/ltcmweb/mwebd/cmd/mwebd - go get golang.org/x/mobile/cmd/gobind - go get golang.org/x/mobile/cmd/gomobile - gomobile init + # go get golang.org/x/mobile/cmd/gobind + # go get golang.org/x/mobile/cmd/gomobile gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ From bddfb2c39d4ed9dda3db8f0b3adab16d72f65c21 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 11:51:39 -0700 Subject: [PATCH 057/100] should workgit add .github/workflows/pr_test_build.yml --- .github/workflows/pr_test_build.yml | 54 +++++++++++------------------ 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index fa06358c8..6e1cf6d71 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -27,29 +27,6 @@ jobs: if: github.event_name != 'pull_request' run: echo "BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_ENV - - name: build mweb - run: | - mkdir -p /opt/android/cake_wallet - # install latest go: - wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz - sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz - export PATH=$PATH:/usr/local/go/bin - export PATH=$PATH:~/go/bin - go install golang.org/x/mobile/cmd/gomobile@latest - gomobile init - - # install mwebd: - cd /opt/android/cake_wallet - git clone https://github.com/ltcmweb/mwebd - cd /opt/android/cake_wallet/mwebd - go get github.com/ltcmweb/mwebd/cmd/mwebd - go install github.com/ltcmweb/mwebd/cmd/mwebd - # go get golang.org/x/mobile/cmd/gobind - # go get golang.org/x/mobile/cmd/gomobile - gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd - mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ - mv ./mwebd.aar $_ - - name: Free Up GitHub Actions Ubuntu Runner Disk Space run: | sudo rm -rf /usr/share/dotnet @@ -110,16 +87,27 @@ jobs: cd /opt/android/cake_wallet flutter pub get - # - name: Install Gomobile - # run: | - # go install golang.org/x/mobile/cmd/gomobile@latest - # go install golang.org/x/mobile/cmd/gobind@latest - # go get golang.org/x/mobile/cmd/gobind - # go get golang.org/x/mobile/cmd/gomobile - # gomobile init - # env: - # GOPROXY: https://proxy.golang.org,direct - # GO111MODULE: "on" + + - name: Install go and gomobile + run: | + # install go > 1.21: + wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz + sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz + export PATH=$PATH:/usr/local/go/bin + export PATH=$PATH:~/go/bin + go install golang.org/x/mobile/cmd/gomobile@latest + gomobile init + + - name: Build mwebd + run: | + cd /opt/android/cake_wallet + git clone https://github.com/ltcmweb/mwebd + cd /opt/android/cake_wallet/mwebd + go get github.com/ltcmweb/mwebd/cmd/mwebd + go install github.com/ltcmweb/mwebd/cmd/mwebd + gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd + mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ + mv ./mwebd.aar $_ From 9eb2ae132dbf1bf03b6870c433e2be5a86b930a1 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 11:59:33 -0700 Subject: [PATCH 058/100] ??? --- .github/workflows/pr_test_build.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 6e1cf6d71..1c79221a7 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -96,10 +96,9 @@ jobs: export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:~/go/bin go install golang.org/x/mobile/cmd/gomobile@latest - gomobile init - - - name: Build mwebd - run: | + gomobile init + # - name: Build mwebd + # run: | cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd From 0aafd3423cc27e5650f7b14891223d74b41c0b47 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 12:00:56 -0700 Subject: [PATCH 059/100] ??? pt.2 --- .github/workflows/pr_test_build.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 1c79221a7..32809fc37 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -96,9 +96,12 @@ jobs: export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:~/go/bin go install golang.org/x/mobile/cmd/gomobile@latest - gomobile init - # - name: Build mwebd - # run: | + gomobile init + + - name: Build mwebd + run: | + export PATH=$PATH:/usr/local/go/bin + export PATH=$PATH:~/go/bin cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd From 9a857801373662938d6adf16edf47f4c9176db96 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 12:20:15 -0700 Subject: [PATCH 060/100] should work, for real this time --- .github/workflows/pr_test_build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 32809fc37..2d3830e5c 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -100,8 +100,10 @@ jobs: - name: Build mwebd run: | + # paths are reset after each step, so we need to set them again: export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:~/go/bin + # build mwebd: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd From 7869334d4661a11737d8e9b8ff9ab76f0dfd6b71 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 28 Jun 2024 12:37:12 -0700 Subject: [PATCH 061/100] fix tx history not persisting + update build_mwebd script --- cw_bitcoin/lib/litecoin_wallet.dart | 1 + scripts/android/build_mwebd.sh | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 6a78af0f2..8fe0d2295 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -217,6 +217,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future init() async { + await transactionHistory.init(); await initMwebUtxosBox(); } diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh index 8a5e6813f..d94f06e03 100755 --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -1,6 +1,14 @@ +# install go > 1.21: +wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz +sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz +export PATH=$PATH:/usr/local/go/bin +export PATH=$PATH:~/go/bin +go install golang.org/x/mobile/cmd/gomobile@latest +gomobile init +# build mwebd: git clone https://github.com/ltcmweb/mwebd cd mwebd -go install github.com/ltcmweb/mwebd/cmd/mwebd@latest +go install github.com/ltcmweb/mwebd/cmd/mwebd gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd mkdir -p ../../../cw_mweb/android/libs/ -mv ./mwebd.aar $_ \ No newline at end of file +mv ./mwebd.aar $_ From dee77a6752296acf002d5029eafe2ca210334500 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 1 Jul 2024 09:00:41 -0700 Subject: [PATCH 062/100] updates --- cw_bitcoin/lib/litecoin_wallet.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 8fe0d2295..3e11d27f6 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -209,10 +209,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { bool? usingElectrs, }) async { await mwebUtxosBox.clear(); + transactionHistory.clear(); mwebUtxosHeight = height; walletInfo.restoreHeight = height; await walletInfo.save(); - processMwebUtxos(); + // processMwebUtxos(); + await startSync(); } @override From a1dbe3bf2b00e78332d9fac30d3d3cf40a970e1c Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 1 Jul 2024 10:05:07 -0700 Subject: [PATCH 063/100] fix some rescan and address gen issues --- cw_bitcoin/lib/litecoin_wallet.dart | 121 ++++++++++-------- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 3 +- 2 files changed, 72 insertions(+), 52 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 3e11d27f6..5b6513409 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -5,6 +5,7 @@ import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart'; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/mweb_utxo.dart'; +import 'package:cw_mweb/mwebd.pbgrpc.dart'; import 'package:fixnum/fixnum.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; @@ -211,18 +212,70 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await mwebUtxosBox.clear(); transactionHistory.clear(); mwebUtxosHeight = height; - walletInfo.restoreHeight = height; - await walletInfo.save(); + await walletInfo.updateRestoreHeight(height); // processMwebUtxos(); + print("STARTING SYNC"); await startSync(); } @override Future init() async { - await transactionHistory.init(); + await super.init(); await initMwebUtxosBox(); } + Future handleIncoming(MwebUtxo utxo, RpcClient stub) async { + final status = await stub.status(StatusRequest()); + var date = DateTime.now(); + var confirmations = 0; + if (utxo.height > 0) { + date = DateTime.fromMillisecondsSinceEpoch(utxo.blockTime * 1000); + confirmations = status.blockHeaderHeight - utxo.height + 1; + } + var tx = transactionHistory.transactions.values + .firstWhereOrNull((tx) => tx.outputAddresses?.contains(utxo.outputId) ?? false); + + if (tx == null) { + tx = ElectrumTransactionInfo( + WalletType.litecoin, + id: utxo.outputId, + height: utxo.height, + amount: utxo.value.toInt(), + fee: 0, + direction: TransactionDirection.incoming, + isPending: utxo.height == 0, + date: date, + confirmations: confirmations, + inputAddresses: [], + outputAddresses: [utxo.outputId], + ); + } + + tx.height = utxo.height; + tx.isPending = utxo.height == 0; + tx.confirmations = confirmations; + bool isNew = transactionHistory.transactions[tx.id] == null; + + if (!(tx.outputAddresses?.contains(utxo.address) ?? false)) { + tx.outputAddresses?.add(utxo.address); + isNew = true; + } + + if (isNew) { + final addressRecord = walletAddresses.allAddresses + .firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address); + if (addressRecord == null) { + print("addressRecord is null! TODO: handle this case 1"); + return; + } + if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) addressRecord.txCount++; + addressRecord.balance += utxo.value.toInt(); + addressRecord.setAsUsed(); + } + print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@"); + transactionHistory.addOne(tx); + } + Future processMwebUtxos() async { final stub = await CwMweb.stub(); final scanSecret = mwebHd.derive(0x80000000).privKey!; @@ -237,55 +290,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { continue; } - final status = await stub.status(StatusRequest()); - var date = DateTime.now(); - var confirmations = 0; - if (utxo.height > 0) { - date = DateTime.fromMillisecondsSinceEpoch(utxo.blockTime * 1000); - confirmations = status.blockHeaderHeight - utxo.height + 1; - } - var tx = transactionHistory.transactions.values - .firstWhereOrNull((tx) => tx.outputAddresses?.contains(utxo.outputId) ?? false); - - if (tx == null) { - tx = ElectrumTransactionInfo( - WalletType.litecoin, - id: utxo.outputId, - height: utxo.height, - amount: utxo.value.toInt(), - fee: 0, - direction: TransactionDirection.incoming, - isPending: utxo.height == 0, - date: date, - confirmations: confirmations, - inputAddresses: [], - outputAddresses: [utxo.outputId], - ); + if (walletInfo.restoreHeight > utxo.height) { + continue; } - tx.height = utxo.height; - tx.isPending = utxo.height == 0; - tx.confirmations = confirmations; - bool isNew = transactionHistory.transactions[tx.id] == null; + await handleIncoming(utxo, stub); - if (!(tx.outputAddresses?.contains(utxo.address) ?? false)) { - tx.outputAddresses?.add(utxo.address); - isNew = true; - } - - if (isNew) { - final addressRecord = walletAddresses.allAddresses - .firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address); - if (addressRecord == null) { - print("addressRecord is null! TODO: handle this case 1"); - continue; - } - if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) addressRecord.txCount++; - addressRecord.balance += utxo.value.toInt(); - addressRecord.setAsUsed(); - } - print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@"); - transactionHistory.addOne(tx); if (initDone) { await updateUnspent(); await updateBalance(); @@ -316,7 +326,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (!mwebAddrs.contains(utxo.address) && utxo.address.isNotEmpty) { continue; } + await mwebUtxosBox.put(utxo.outputId, utxo); + + await handleIncoming(utxo, stub); } } @@ -427,8 +440,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return; } final unspent = BitcoinUnspent( - addressRecord, outputId, utxo.value.toInt(), mwebAddrs.indexOf(utxo.address)); - if (unspent.vout == 0) unspent.isChange = true; + addressRecord, + outputId, + utxo.value.toInt(), + mwebAddrs.indexOf(utxo.address), + ); + if (unspent.vout == 0) { + unspent.isChange = true; + } unspentCoins.add(unspent); }); } diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index a1089f473..8b89fb6f9 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -69,7 +69,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with @override Future getChangeAddress() async { // super.getChangeAddress(); - updateChangeAddresses(); + // updateChangeAddresses(); + print("getChangeAddress @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); // this means all change addresses used will be mweb addresses!: await topUpMweb(0); return mwebAddrs[0]; From 4008add46e72f066bbdf666fb264a063ef19f6b3 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 9 Jul 2024 16:14:53 -0700 Subject: [PATCH 064/100] save [skip ci] --- cw_bitcoin/lib/litecoin_wallet.dart | 33 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 5b6513409..0c5078417 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -326,9 +326,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (!mwebAddrs.contains(utxo.address) && utxo.address.isNotEmpty) { continue; } - + await mwebUtxosBox.put(utxo.outputId, utxo); - + await handleIncoming(utxo, stub); } } @@ -368,17 +368,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (inputAddresses.isEmpty) return; input.close(); var digest = output.events.single; - final tx = ElectrumTransactionInfo(WalletType.litecoin, - id: digest.toString(), - height: height, - amount: amount, - fee: 0, - direction: TransactionDirection.outgoing, - isPending: false, - date: DateTime.fromMillisecondsSinceEpoch(status.blockTime * 1000), - confirmations: 1, - inputAddresses: inputAddresses.toList(), - outputAddresses: []); + final tx = ElectrumTransactionInfo( + WalletType.litecoin, + id: digest.toString(), + height: height, + amount: amount, + fee: 0, + direction: TransactionDirection.outgoing, + isPending: false, + date: DateTime.fromMillisecondsSinceEpoch(status.blockTime * 1000), + confirmations: 1, + inputAddresses: inputAddresses.toList(), + outputAddresses: [], + ); print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@2"); transactionHistory.addOne(tx); @@ -397,7 +399,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { for (final outputId in payingToOutputIds) { final spendingTx = transactionHistory.transactions.values .firstWhereOrNull((tx) => tx.inputAddresses?.contains(outputId) ?? false); - if (spendingTx != null && !spendingTx.isPending) target.add(outputId); + if (spendingTx != null && !spendingTx.isPending) { + target.add(outputId); + } } if (outputId.isEmpty) return false; final stub = await CwMweb.stub(); @@ -464,7 +468,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unconfirmed += utxo.value.toInt(); } }); - // print("confirmed: $confirmed, unconfirmed: $unconfirmed"); return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } From dfdf16a6c2b20f7c3969dcfc3f86a03a6f289455 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 10 Jul 2024 12:45:30 -0700 Subject: [PATCH 065/100] fix unconfirmed balance not updating when receiving --- cw_bitcoin/lib/litecoin_wallet.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 0c5078417..0fb8faf31 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -271,8 +271,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) addressRecord.txCount++; addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); + + // update the unconfirmed balance when a new tx is added: + await updateBalance(); } - print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@"); transactionHistory.addOne(tx); } From 4ec9d7b2e1b5fc857fd079f292c77747c2c82ffa Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 11 Jul 2024 10:54:44 -0700 Subject: [PATCH 066/100] unspent coins / coin control fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 55 +++++++++++-------- .../unspent_coins_list_view_model.dart | 19 +++++-- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 0fb8faf31..e954d0afa 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -79,6 +79,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { autorun((_) { this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; }); + CwMweb.stub().then((value) { + _stub = value; + }); } final bitcoin.HDWallet mwebHd; @@ -86,6 +89,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Timer? _syncTimer; // late int lastMwebUtxosHeight; int mwebUtxosHeight = 0; + late RpcClient _stub; static Future create( {required String mnemonic, @@ -152,12 +156,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future startSync() async { await super.startSync(); - final stub = await CwMweb.stub(); _syncTimer?.cancel(); _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { if (syncStatus is FailedSyncStatus) return; final height = await electrumClient.getCurrentBlockChainTip() ?? 0; - final resp = await stub.status(StatusRequest()); + final resp = await _stub.status(StatusRequest()); // print("stats:"); // print("???????????????????"); // print(resp.blockHeaderHeight); @@ -279,7 +282,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } Future processMwebUtxos() async { - final stub = await CwMweb.stub(); final scanSecret = mwebHd.derive(0x80000000).privKey!; int restoreHeight = walletInfo.restoreHeight; print("SCANNING FROM HEIGHT: $restoreHeight"); @@ -296,7 +298,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { continue; } - await handleIncoming(utxo, stub); + await handleIncoming(utxo, _stub); if (initDone) { await updateUnspent(); @@ -308,7 +310,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } - await for (Utxo sUtxo in stub.utxos(req)) { + await for (Utxo sUtxo in _stub.utxos(req)) { final utxo = MwebUtxo( address: sUtxo.address, blockTime: sUtxo.blockTime, @@ -331,7 +333,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await mwebUtxosBox.put(utxo.outputId, utxo); - await handleIncoming(utxo, stub); + await handleIncoming(utxo, _stub); } } @@ -343,11 +345,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final outputIds = mwebUtxosBox.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId).toList(); - final stub = await CwMweb.stub(); - final resp = await stub.spent(SpentRequest(outputId: outputIds)); + final resp = await _stub.spent(SpentRequest(outputId: outputIds)); final spent = resp.outputId; if (spent.isEmpty) return; - final status = await stub.status(StatusRequest()); + final status = await _stub.status(StatusRequest()); final height = await electrumClient.getCurrentBlockChainTip(); if (height == null || status.blockHeaderHeight != height) return; if (status.mwebUtxosHeight != height) return; @@ -405,11 +406,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { target.add(outputId); } } - if (outputId.isEmpty) return false; - final stub = await CwMweb.stub(); - final resp = await stub.spent(SpentRequest(outputId: outputId)); + if (outputId.isEmpty) { + return false; + } + final resp = await _stub.spent(SpentRequest(outputId: outputId)); if (!setEquals(resp.outputId.toSet(), target)) return false; - final status = await stub.status(StatusRequest()); + final status = await _stub.status(StatusRequest()); if (!tx.isPending) return false; tx.height = status.mwebUtxosHeight; tx.confirmations = 1; @@ -422,6 +424,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future updateUnspent() async { await super.updateUnspent(); await checkMwebUtxosSpent(); + } + + @override + @action + Future updateAllUnspents() async { + List updatedUnspentCoins = []; + + await Future.wait(walletAddresses.allAddresses.map((address) async { + updatedUnspentCoins.addAll(await fetchUnspent(address)); + })); + + // update mweb unspents: final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; mwebUtxosBox.keys.forEach((dynamic oId) { final String outputId = oId as String; @@ -436,11 +450,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final addressRecord = walletAddresses.allAddresses .firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address); - // print("^^^^^^^^^^^^^^^^^^"); - // print(utxo.address); - // for (var a in walletAddresses.allAddresses) { - // print(a.address); - // } if (addressRecord == null) { print("addressRecord is null! TODO: handle this case2"); return; @@ -454,8 +463,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (unspent.vout == 0) { unspent.isChange = true; } - unspentCoins.add(unspent); + updatedUnspentCoins.add(unspent); }); + + unspentCoins = updatedUnspentCoins; } @override @@ -524,8 +535,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final fee = utxos.sumOfUtxosValue() - preOutputSum; final txb = BitcoinTransactionBuilder(utxos: utxos, outputs: outputs, fee: fee, network: network); - final stub = await CwMweb.stub(); - final resp = await stub.create(CreateRequest( + final resp = await _stub.create(CreateRequest( rawTx: txb.buildTransaction((a, b, c, d) => '').toBytes(), scanSecret: hex.decode(mwebHd.derive(0x80000000).privKey!), spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!), @@ -560,8 +570,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { try { final tx = await super.createTransaction(credentials) as PendingBitcoinTransaction; - final stub = await CwMweb.stub(); - final resp = await stub.create(CreateRequest( + final resp = await _stub.create(CreateRequest( rawTx: hex.decode(tx.hex), scanSecret: hex.decode(mwebHd.derive(0x80000000).privKey!), spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!), diff --git a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart index e2d8469f1..5b6e6140f 100644 --- a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart +++ b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart @@ -85,11 +85,18 @@ abstract class UnspentCoinsListViewModelBase with Store { } List _getUnspents() { - if (wallet.type == WalletType.monero) return monero!.getUnspents(wallet); - if (wallet.type == WalletType.wownero) return wownero!.getUnspents(wallet); - if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type)) - return bitcoin!.getUnspents(wallet); - return List.empty(); + switch (wallet.type) { + case WalletType.monero: + return monero!.getUnspents(wallet); + case WalletType.wownero: + return wownero!.getUnspents(wallet); + case WalletType.bitcoin: + case WalletType.litecoin: + case WalletType.bitcoinCash: + return bitcoin!.getUnspents(wallet); + default: + return List.empty(); + } } @action @@ -97,7 +104,7 @@ abstract class UnspentCoinsListViewModelBase with Store { _items.clear(); List unspents = []; - _getUnspents().forEach((elem) { + _getUnspents().forEach((Unspent elem) { try { final info = getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage); From e5e2a8706ba42347a0585132a64add08b093f269 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 11 Jul 2024 11:49:14 -0700 Subject: [PATCH 067/100] coin control fixes --- cw_bitcoin/lib/electrum_wallet.dart | 8 ++++---- cw_bitcoin/lib/litecoin_wallet.dart | 30 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 11a6e514f..2cbccb8df 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1170,7 +1170,7 @@ abstract class ElectrumWalletBase await updateAllUnspents(); if (unspentCoinsInfo.isEmpty) { - unspentCoins.forEach((coin) => _addCoinInfo(coin)); + unspentCoins.forEach((coin) => addCoinInfo(coin)); return; } @@ -1190,7 +1190,7 @@ abstract class ElectrumWalletBase if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) coin.bitcoinAddressRecord.balance += coinInfo.value; } else { - _addCoinInfo(coin); + addCoinInfo(coin); } }); } @@ -1222,7 +1222,7 @@ abstract class ElectrumWalletBase if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) coin.bitcoinAddressRecord.balance += coinInfo.value; } else { - _addCoinInfo(coin); + addCoinInfo(coin); } }); } @@ -1249,7 +1249,7 @@ abstract class ElectrumWalletBase } @action - Future _addCoinInfo(BitcoinUnspent coin) async { + Future addCoinInfo(BitcoinUnspent coin) async { final newInfo = UnspentCoinsInfo( walletId: id, hash: coin.hash, diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index e954d0afa..4e5d1266f 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -481,6 +481,36 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unconfirmed += utxo.value.toInt(); } }); + + // update unspent balances: + + // reset coin balances to 0: + unspentCoins.forEach((coin) { + if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) + coin.bitcoinAddressRecord.balance = 0; + }); + + unspentCoins.forEach((coin) { + final coinInfoList = unspentCoinsInfo.values.where( + (element) => + element.walletId.contains(id) && + element.hash.contains(coin.hash) && + element.vout == coin.vout, + ); + + if (coinInfoList.isNotEmpty) { + final coinInfo = coinInfoList.first; + + coin.isFrozen = coinInfo.isFrozen; + coin.isSending = coinInfo.isSending; + coin.note = coinInfo.note; + if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) + coin.bitcoinAddressRecord.balance += coinInfo.value; + } else { + super.addCoinInfo(coin); + } + }); + return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } From d81eb0cdfc4202509519688dfcb30548e0a42462 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 11 Jul 2024 15:55:37 -0700 Subject: [PATCH 068/100] address balance and txCount fixes, try/catch electrum call --- cw_bitcoin/lib/electrum_wallet.dart | 11 +++++++++-- cw_bitcoin/lib/litecoin_wallet.dart | 18 +++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 2cbccb8df..2f49ae7b8 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1230,10 +1230,17 @@ abstract class ElectrumWalletBase @action Future> fetchUnspent(BitcoinAddressRecord address) async { - final unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); - + List> unspents = []; List updatedUnspentCoins = []; + try { + unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); + } catch (e, s) { + print(e); + print(s); + return []; + } + await Future.wait(unspents.map((unspent) async { try { final coin = BitcoinUnspent.fromJSON(address, unspent); diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 4e5d1266f..b45e7dec4 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -216,7 +216,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { transactionHistory.clear(); mwebUtxosHeight = height; await walletInfo.updateRestoreHeight(height); - // processMwebUtxos(); print("STARTING SYNC"); await startSync(); } @@ -268,10 +267,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final addressRecord = walletAddresses.allAddresses .firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address); if (addressRecord == null) { - print("addressRecord is null! TODO: handle this case 1"); return; } - if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) addressRecord.txCount++; + if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) { + addressRecord.txCount++; + } addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); @@ -288,6 +288,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: restoreHeight); bool initDone = false; + // reset address balances and tx counts: + walletAddresses.allAddresses.forEach((addressRecord) { + addressRecord.balance = 0; + addressRecord.txCount = 0; + }); + for (final utxo in mwebUtxosBox.values) { if (utxo.address.isEmpty) { initDone = true; @@ -319,6 +325,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { value: sUtxo.value.toInt(), ); + if (mwebUtxosBox.containsKey(utxo.outputId)) { + // we've already stored this utxo, skip it: + continue; + } + if (utxo.address.isEmpty) { await updateUnspent(); await updateBalance(); @@ -488,6 +499,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unspentCoins.forEach((coin) { if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) coin.bitcoinAddressRecord.balance = 0; + coin.bitcoinAddressRecord.txCount = 0; }); unspentCoins.forEach((coin) { From 720914727ed621d0e8b8352f79748ef3b2e2d6a8 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 11 Jul 2024 17:22:35 -0700 Subject: [PATCH 069/100] fix txCount for addresses --- cw_bitcoin/lib/litecoin_wallet.dart | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index b45e7dec4..81b354e7a 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -161,11 +161,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (syncStatus is FailedSyncStatus) return; final height = await electrumClient.getCurrentBlockChainTip() ?? 0; final resp = await _stub.status(StatusRequest()); - // print("stats:"); - // print("???????????????????"); - // print(resp.blockHeaderHeight); - // print(resp.mwebUtxosHeight); - // print(height); if (resp.blockHeaderHeight < height) { int h = resp.blockHeaderHeight; syncStatus = SyncingSyncStatus(height - h, h / height); @@ -288,12 +283,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: restoreHeight); bool initDone = false; - // reset address balances and tx counts: - walletAddresses.allAddresses.forEach((addressRecord) { - addressRecord.balance = 0; - addressRecord.txCount = 0; - }); - for (final utxo in mwebUtxosBox.values) { if (utxo.address.isEmpty) { initDone = true; @@ -338,7 +327,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; - if (!mwebAddrs.contains(utxo.address) && utxo.address.isNotEmpty) { + if (utxo.address.isNotEmpty && !mwebAddrs.contains(utxo.address)) { continue; } @@ -495,7 +484,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // update unspent balances: - // reset coin balances to 0: + // reset coin balances and txCount to 0: unspentCoins.forEach((coin) { if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) coin.bitcoinAddressRecord.balance = 0; @@ -523,6 +512,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } }); + // update the txCount for each address: + for (var tx in transactionHistory.transactions.values) { + if (tx.isPending) continue; + final txAddresses = tx.inputAddresses! + tx.outputAddresses!; + for (var address in txAddresses) { + final addressRecord = walletAddresses.allAddresses + .firstWhereOrNull((addressRecord) => addressRecord.address == address); + if (addressRecord == null) { + continue; + } + addressRecord.txCount++; + } + } + return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } From 8efe70fb24b7cc101941716609438757c9177c3b Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 12 Jul 2024 13:40:37 -0700 Subject: [PATCH 070/100] save [skip ci] --- .../lib/bitcoin_transaction_priority.dart | 2 +- cw_bitcoin/lib/litecoin_wallet.dart | 47 ++++--- .../dashboard/pages/transactions_page.dart | 10 +- .../dashboard/widgets/transaction_raw.dart | 116 +++++++++++------- .../widgets/unspent_coins_list_item.dart | 87 ++++++++----- .../dashboard/transaction_list_item.dart | 22 +++- 6 files changed, 181 insertions(+), 103 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_transaction_priority.dart b/cw_bitcoin/lib/bitcoin_transaction_priority.dart index 7c4dcfd5f..d1f45a545 100644 --- a/cw_bitcoin/lib/bitcoin_transaction_priority.dart +++ b/cw_bitcoin/lib/bitcoin_transaction_priority.dart @@ -87,7 +87,7 @@ class LitecoinTransactionPriority extends BitcoinTransactionPriority { } @override - String get units => 'Latoshi'; + String get units => 'Litoshi'; @override String toString() { diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 81b354e7a..4bafaff26 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -178,6 +178,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (resp.mwebUtxosHeight > mwebUtxosHeight) { mwebUtxosHeight = resp.mwebUtxosHeight; await checkMwebUtxosSpent(); + // update the confirmations for each transaction: for (final transaction in transactionHistory.transactions.values) { if (transaction.isPending) continue; final confirmations = mwebUtxosHeight - transaction.height + 1; @@ -266,6 +267,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) { addressRecord.txCount++; + print("COUNT UPDATED HERE 2!!!!! ${addressRecord.txCount}"); } addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); @@ -283,6 +285,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: restoreHeight); bool initDone = false; + // process old utxos: for (final utxo in mwebUtxosBox.values) { if (utxo.address.isEmpty) { initDone = true; @@ -305,6 +308,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } + // process new utxos as they come in: await for (Utxo sUtxo in _stub.utxos(req)) { final utxo = MwebUtxo( address: sUtxo.address, @@ -327,6 +331,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; + // don't process utxos with addresses that are not in the mwebAddrs list: if (utxo.address.isNotEmpty && !mwebAddrs.contains(utxo.address)) { continue; } @@ -362,7 +367,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (utxo == null) continue; final addressRecord = walletAddresses.allAddresses .firstWhere((addressRecord) => addressRecord.address == utxo.address); - if (!inputAddresses.contains(utxo.address)) addressRecord.txCount++; + if (!inputAddresses.contains(utxo.address)) { + addressRecord.txCount++; + print("COUNT UPDATED HERE 3!!!!! ${addressRecord.address} ${addressRecord.txCount} !!!!!!"); + } addressRecord.balance -= utxo.value.toInt(); amount += utxo.value.toInt(); inputAddresses.add(utxo.address); @@ -471,6 +479,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future fetchBalances() async { + print("FETCH BALANCES"); final balance = await super.fetchBalances(); var confirmed = balance.confirmed; var unconfirmed = balance.unconfirmed; @@ -523,6 +532,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { continue; } addressRecord.txCount++; + print("COUNT UPDATED HERE 0!!!!! ${addressRecord.address} ${addressRecord.txCount} !!!!!!"); } } @@ -644,23 +654,26 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { tx.outputs = resp.outputId; return tx ..addListener((transaction) async { - final addresses = {}; - transaction.inputAddresses?.forEach((id) async { - final utxo = mwebUtxosBox.get(id); - await mwebUtxosBox.delete(id); - if (utxo == null) return; - final addressRecord = walletAddresses.allAddresses - .firstWhere((addressRecord) => addressRecord.address == utxo.address); - if (!addresses.contains(utxo.address)) { - addressRecord.txCount++; - addresses.add(utxo.address); - } - addressRecord.balance -= utxo.value.toInt(); - }); - transaction.inputAddresses?.addAll(addresses); - print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@3"); + print("LISTENER CALLED @@@@@@@@@@@@@@@@@@"); + // final addresses = {}; + // transaction.inputAddresses?.forEach((id) async { + // final utxo = mwebUtxosBox.get(id); + // await mwebUtxosBox.delete(id); + // if (utxo == null) return; + // final addressRecord = walletAddresses.allAddresses + // .firstWhere((addressRecord) => addressRecord.address == utxo.address); + // if (!addresses.contains(utxo.address)) { + // addressRecord.txCount++; + // print("COUNT UPDATED HERE 1!!!!! ${addressRecord.address} ${addressRecord.txCount} !!!!!!"); + // addresses.add(utxo.address); + // } + // addressRecord.balance -= utxo.value.toInt(); + // }); + // transaction.inputAddresses?.addAll(addresses); + // print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@3"); - transactionHistory.addOne(transaction); + // transactionHistory.addOne(transaction); + // await transactionHistory.save(); await updateUnspent(); await updateBalance(); }); diff --git a/lib/src/screens/dashboard/pages/transactions_page.dart b/lib/src/screens/dashboard/pages/transactions_page.dart index 7c0e9cad4..6d76a3184 100644 --- a/lib/src/screens/dashboard/pages/transactions_page.dart +++ b/lib/src/screens/dashboard/pages/transactions_page.dart @@ -52,7 +52,7 @@ class TransactionsPage extends StatelessWidget { try { final uri = Uri.parse( "https://guides.cakewallet.com/docs/FAQ/why_are_my_funds_not_appearing/"); - launchUrl(uri, mode: LaunchMode.externalApplication); + launchUrl(uri, mode: LaunchMode.externalApplication); } catch (_) {} }, title: S.of(context).syncing_wallet_alert_title, @@ -83,10 +83,6 @@ class TransactionsPage extends StatelessWidget { } final transaction = item.transaction; - final transactionType = dashboardViewModel.type == WalletType.ethereum && - transaction.evmSignatureName == 'approval' - ? ' (${transaction.evmSignatureName})' - : ''; return Observer( builder: (_) => TransactionRow( @@ -101,7 +97,9 @@ class TransactionsPage extends StatelessWidget { : item.formattedFiatAmount, isPending: transaction.isPending, title: item.formattedTitle + - item.formattedStatus + ' $transactionType', + item.formattedStatus + + ' ${item.formattedType}', + tag: item.tag, ), ); } diff --git a/lib/src/screens/dashboard/widgets/transaction_raw.dart b/lib/src/screens/dashboard/widgets/transaction_raw.dart index 3a95b9f2e..7dd59bc52 100644 --- a/lib/src/screens/dashboard/widgets/transaction_raw.dart +++ b/lib/src/screens/dashboard/widgets/transaction_raw.dart @@ -5,14 +5,16 @@ import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart'; class TransactionRow extends StatelessWidget { - TransactionRow( - {required this.direction, - required this.formattedDate, - required this.formattedAmount, - required this.formattedFiatAmount, - required this.isPending, - required this.title, - required this.onTap}); + TransactionRow({ + required this.direction, + required this.formattedDate, + required this.formattedAmount, + required this.formattedFiatAmount, + required this.isPending, + required this.title, + required this.onTap, + required this.tag, + }); final VoidCallback onTap; final TransactionDirection direction; @@ -21,6 +23,7 @@ class TransactionRow extends StatelessWidget { final String formattedFiatAmount; final bool isPending; final String title; + final String tag; @override Widget build(BuildContext context) { @@ -38,48 +41,69 @@ class TransactionRow extends StatelessWidget { width: 36, decoration: BoxDecoration( shape: BoxShape.circle, - color: Theme.of(context).extension()!.rowsColor - ), - child: Image.asset( - direction == TransactionDirection.incoming - ? 'assets/images/down_arrow.png' - : 'assets/images/up_arrow.png'), + color: Theme.of(context).extension()!.rowsColor), + child: Image.asset(direction == TransactionDirection.incoming + ? 'assets/images/down_arrow.png' + : 'assets/images/up_arrow.png'), ), SizedBox(width: 12), Expanded( child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(title, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.textColor)), - Text(formattedAmount, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.textColor)) - ]), - SizedBox(height: 5), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(formattedDate, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).extension()!.dateSectionRowColor)), - Text(formattedFiatAmount, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).extension()!.dateSectionRowColor)) - ]) - ], - ) - ) + mainAxisSize: MainAxisSize.min, + children: [ + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text( + title, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.textColor), + ), + Row( + children: [ + if (tag.isNotEmpty) + Container( + height: 17, + padding: EdgeInsets.only(left: 6, right: 6), + margin: EdgeInsets.only(right: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.5)), + color: Colors.white), + alignment: Alignment.center, + child: Text( + tag, + style: TextStyle( + color: Colors.black, + fontSize: 7, + fontWeight: FontWeight.w600, + ), + ), + ), + Text( + formattedAmount, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.textColor), + ), + ], + ), + ]), + SizedBox(height: 5), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text(formattedDate, + style: TextStyle( + fontSize: 14, + color: + Theme.of(context).extension()!.dateSectionRowColor)), + Text(formattedFiatAmount, + style: TextStyle( + fontSize: 14, + color: + Theme.of(context).extension()!.dateSectionRowColor)) + ]) + ], + )) ], ), )); diff --git a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart index 60a23c99b..236d06f4e 100644 --- a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart +++ b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart @@ -103,40 +103,63 @@ class UnspentCoinsListItem extends StatelessWidget { ), maxLines: 1, ), - if (isChange) - Container( - height: 17, - padding: EdgeInsets.only(left: 6, right: 6), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(8.5)), - color: Colors.white), - alignment: Alignment.center, - child: Text( - S.of(context).unspent_change, - style: TextStyle( - color: itemColor, - fontSize: 7, - fontWeight: FontWeight.w600, + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if (isChange) + Container( + height: 17, + padding: EdgeInsets.only(left: 6, right: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.5)), + color: Colors.white), + alignment: Alignment.center, + child: Text( + S.of(context).unspent_change, + style: TextStyle( + color: itemColor, + fontSize: 7, + fontWeight: FontWeight.w600, + ), + ), ), - ), - ), - if (isSilentPayment) - Container( - height: 17, - padding: EdgeInsets.only(left: 6, right: 6), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(8.5)), - color: Colors.white), - alignment: Alignment.center, - child: Text( - S.of(context).silent_payments, - style: TextStyle( - color: itemColor, - fontSize: 7, - fontWeight: FontWeight.w600, + if (address.toLowerCase().contains("mweb")) + Container( + height: 17, + padding: EdgeInsets.only(left: 6, right: 6), + margin: EdgeInsets.only(left: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.5)), + color: Colors.white), + alignment: Alignment.center, + child: Text( + "MWEB", + style: TextStyle( + color: itemColor, + fontSize: 7, + fontWeight: FontWeight.w600, + ), + ), ), - ), - ), + if (isSilentPayment) + Container( + height: 17, + padding: EdgeInsets.only(left: 6, right: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.5)), + color: Colors.white), + alignment: Alignment.center, + child: Text( + S.of(context).silent_payments, + style: TextStyle( + color: itemColor, + fontSize: 7, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), ], ), ), diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index 176b4e58d..56c85737b 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -56,7 +56,8 @@ class TransactionListItem extends ActionListItem with Keyable { } String get formattedPendingStatus { - if (balanceViewModel.wallet.type == WalletType.monero || balanceViewModel.wallet.type == WalletType.haven) { + if (balanceViewModel.wallet.type == WalletType.monero || + balanceViewModel.wallet.type == WalletType.haven) { if (transaction.confirmations >= 0 && transaction.confirmations < 10) { return ' (${transaction.confirmations}/10)'; } @@ -79,6 +80,25 @@ class TransactionListItem extends ActionListItem with Keyable { return transaction.isPending ? S.current.pending : ''; } + String get formattedType { + if (transaction.evmSignatureName == 'approval') { + return ' (${transaction.evmSignatureName})'; + } + return ''; + } + + String get tag { + List addresses = + (transaction.inputAddresses ?? []) + (transaction.outputAddresses ?? []); + for (var address in addresses) { + if (address.toLowerCase().contains('mweb')) { + return 'MWEB'; + } + } + + return ''; + } + CryptoCurrency? get assetOfTransaction { try { if (balanceViewModel.wallet.type == WalletType.ethereum) { From a883780653d5a086d72abfe3d907e43fdf3251fa Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 15 Jul 2024 12:59:20 -0700 Subject: [PATCH 071/100] potential fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 4bafaff26..75a30d303 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -161,6 +161,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (syncStatus is FailedSyncStatus) return; final height = await electrumClient.getCurrentBlockChainTip() ?? 0; final resp = await _stub.status(StatusRequest()); + // print("height: $height"); + // print("resp.blockHeaderHeight: ${resp.blockHeaderHeight}"); + // print("resp.mwebHeaderHeight: ${resp.mwebHeaderHeight}"); + // print("resp.mwebUtxosHeight: ${resp.mwebUtxosHeight}"); if (resp.blockHeaderHeight < height) { int h = resp.blockHeaderHeight; syncStatus = SyncingSyncStatus(height - h, h / height); @@ -292,9 +296,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { continue; } - if (walletInfo.restoreHeight > utxo.height) { - continue; - } + // if (walletInfo.restoreHeight > utxo.height) { + // continue; + // } await handleIncoming(utxo, _stub); @@ -318,10 +322,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { value: sUtxo.value.toInt(), ); - if (mwebUtxosBox.containsKey(utxo.outputId)) { - // we've already stored this utxo, skip it: - continue; - } + // if (mwebUtxosBox.containsKey(utxo.outputId)) { + // // we've already stored this utxo, skip it: + // continue; + // } if (utxo.address.isEmpty) { await updateUnspent(); @@ -479,7 +483,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future fetchBalances() async { - print("FETCH BALANCES"); final balance = await super.fetchBalances(); var confirmed = balance.confirmed; var unconfirmed = balance.unconfirmed; From 45f229123fa1775a3c9c4327da6fb28bffec04f0 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 15 Jul 2024 21:56:20 -0700 Subject: [PATCH 072/100] minor fix --- cw_bitcoin/lib/litecoin_wallet.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 75a30d303..f0d810fb3 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -675,8 +675,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // transaction.inputAddresses?.addAll(addresses); // print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@3"); - // transactionHistory.addOne(transaction); - // await transactionHistory.save(); + transactionHistory.addOne(transaction); await updateUnspent(); await updateBalance(); }); From ac6dc6735668fd3426db01a48e8aed17ef3b6244 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 15 Jul 2024 21:58:11 -0700 Subject: [PATCH 073/100] minor fix - 2 --- cw_bitcoin/lib/litecoin_wallet.dart | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index f0d810fb3..36965f74d 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -657,23 +657,16 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { tx.outputs = resp.outputId; return tx ..addListener((transaction) async { - print("LISTENER CALLED @@@@@@@@@@@@@@@@@@"); - // final addresses = {}; - // transaction.inputAddresses?.forEach((id) async { - // final utxo = mwebUtxosBox.get(id); - // await mwebUtxosBox.delete(id); - // if (utxo == null) return; - // final addressRecord = walletAddresses.allAddresses - // .firstWhere((addressRecord) => addressRecord.address == utxo.address); - // if (!addresses.contains(utxo.address)) { - // addressRecord.txCount++; - // print("COUNT UPDATED HERE 1!!!!! ${addressRecord.address} ${addressRecord.txCount} !!!!!!"); - // addresses.add(utxo.address); - // } - // addressRecord.balance -= utxo.value.toInt(); - // }); - // transaction.inputAddresses?.addAll(addresses); - // print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@3"); + final addresses = {}; + transaction.inputAddresses?.forEach((id) async { + final utxo = mwebUtxosBox.get(id); + await mwebUtxosBox.delete(id); + if (utxo == null) return; + if (!addresses.contains(utxo.address)) { + addresses.add(utxo.address); + } + }); + transaction.inputAddresses?.addAll(addresses); transactionHistory.addOne(transaction); await updateUnspent(); From 5a2a207aecc779d02557b0746bf79a9260ea9e5d Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 16 Jul 2024 12:41:13 -0700 Subject: [PATCH 074/100] sync status fixes, potential fix for background state issue --- cw_bitcoin/lib/electrum_wallet.dart | 14 +++++++++----- cw_mweb/ios/Classes/CwMwebPlugin.swift | 9 +++++++-- ios/Runner/AppDelegate.swift | 8 ++++++++ lib/utils/exception_handler.dart | 2 +- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 2f49ae7b8..97d466ffe 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -408,7 +408,9 @@ abstract class ElectrumWalletBase @override Future startSync() async { try { - syncStatus = SyncronizingSyncStatus(); + if (this is! LitecoinWallet) { + syncStatus = SyncronizingSyncStatus(); + } if (hasSilentPaymentsScanning) { await _setInitialHeight(); @@ -426,10 +428,12 @@ abstract class ElectrumWalletBase await updateFeeRates(); Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); - if (alwaysScan == true) { - _setListeners(walletInfo.restoreHeight); - } else { - syncStatus = SyncedSyncStatus(); + if (this is! LitecoinWallet) { + if (alwaysScan == true) { + _setListeners(walletInfo.restoreHeight); + } else { + syncStatus = SyncedSyncStatus(); + } } } catch (e, stacktrace) { print(stacktrace); diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index fa1ac39ad..bd9fff23e 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -18,7 +18,7 @@ public class CwMwebPlugin: NSObject, FlutterPlugin { result("iOS " + UIDevice.current.systemVersion) case "start": let args = call.arguments as? [String: String] - print("args: \(args)") + // print("args: \(args)") let dataDir = args?["dataDir"] var error: NSError? @@ -27,18 +27,22 @@ public class CwMwebPlugin: NSObject, FlutterPlugin { if let server = CwMwebPlugin.server { do { - print("starting server \(CwMwebPlugin.port)") + print("starting server2 \(CwMwebPlugin.port)") try server.start(0, ret0_: &CwMwebPlugin.port) result(CwMwebPlugin.port) } catch let startError as NSError { + print("Server Start Error: \(startError.localizedDescription)") result(FlutterError(code: "Server Start Error", message: startError.localizedDescription, details: nil)) } } else if let error = error { + print("Server Creation Error: \(error.localizedDescription)") result(FlutterError(code: "Server Creation Error", message: error.localizedDescription, details: nil)) } else { + print("Unknown Error: Failed to create server") result(FlutterError(code: "Unknown Error", message: "Failed to create server", details: nil)) } } else { + print("Server already running on port: \(CwMwebPlugin.port)") // result(FlutterError(code: "Server Already Running", message: "The server is already running", details: nil)) result(CwMwebPlugin.port) } @@ -51,6 +55,7 @@ public class CwMwebPlugin: NSObject, FlutterPlugin { } deinit { + print("Stopping and cleaning up server") // Perform cleanup tasks CwMwebPlugin.server?.stop() CwMwebPlugin.server = nil diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index acdfa4346..26142c477 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -143,4 +143,12 @@ import workmanager } } + override func applicationDidBecomeActive(_ application: UIApplication) { + signal(SIGPIPE, SIG_IGN); + } + + override func applicationWillEnterForeground(_ application: UIApplication) { + signal(SIGPIPE, SIG_IGN); + } + } diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index b19b1bb7e..c5c241d87 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -81,7 +81,7 @@ class ExceptionHandler { } static void onError(FlutterErrorDetails errorDetails) async { - if (kDebugMode) { + if (kDebugMode || kProfileMode) { FlutterError.presentError(errorDetails); debugPrint(errorDetails.toString()); return; From 80d56bc4002163621959fe63a75262fb9d8a9f43 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 09:13:41 -0700 Subject: [PATCH 075/100] workflow and script updates --- .github/workflows/pr_test_build.yml | 5 +---- scripts/android/build_mwebd.sh | 9 +++++---- scripts/ios/build_mwebd.sh | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 8 deletions(-) create mode 100755 scripts/ios/build_mwebd.sh diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 630318d6e..74cefc4ec 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -109,10 +109,7 @@ jobs: # build mwebd: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd - cd /opt/android/cake_wallet/mwebd - go get github.com/ltcmweb/mwebd/cmd/mwebd - go install github.com/ltcmweb/mwebd/cmd/mwebd - gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd + gomobile bind -target=android -androidapi 21 ./mwebd mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh index d94f06e03..0bc770d51 100755 --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -7,8 +7,9 @@ go install golang.org/x/mobile/cmd/gomobile@latest gomobile init # build mwebd: git clone https://github.com/ltcmweb/mwebd -cd mwebd -go install github.com/ltcmweb/mwebd/cmd/mwebd -gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd -mkdir -p ../../../cw_mweb/android/libs/ +# go install github.com/ltcmweb/mwebd/cmd/mwebd +gomobile bind -target=android -androidapi 21 . +mkdir -p ../../cw_mweb/android/libs/ mv ./mwebd.aar $_ +# cleanup: +rm -rf mwebd \ No newline at end of file diff --git a/scripts/ios/build_mwebd.sh b/scripts/ios/build_mwebd.sh new file mode 100755 index 000000000..4aab85142 --- /dev/null +++ b/scripts/ios/build_mwebd.sh @@ -0,0 +1,15 @@ +# install go > 1.21: +wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz +sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz +export PATH=$PATH:/usr/local/go/bin +export PATH=$PATH:~/go/bin +go install golang.org/x/mobile/cmd/gomobile@latest +gomobile init +# build mwebd: +git clone https://github.com/ltcmweb/mwebd +# go install github.com/ltcmweb/mwebd/cmd/mwebd +gomobile bind -target=ios ./mwebd +mkdir -p ../../cw_mweb/android/libs/ +mv ./Mwebd.xcframework ../../ios/ +# cleanup: +rm -rf mwebd \ No newline at end of file From ef458d1daedd9daac2600e81e49f00dd4d2b4ff0 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 09:27:31 -0700 Subject: [PATCH 076/100] updates --- cw_bitcoin/lib/litecoin_wallet.dart | 9 ++++++++- cw_core/lib/wallet_base.dart | 6 ++++-- cw_mweb/ios/Classes/CwMwebPlugin.swift | 10 +++++++++- cw_mweb/lib/cw_mweb.dart | 20 ++++++++++++++++---- ios/Runner/AppDelegate.swift | 8 -------- lib/src/screens/root/root.dart | 8 ++++++++ 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 36965f74d..a42a7469b 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -87,7 +87,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final bitcoin.HDWallet mwebHd; late final Box mwebUtxosBox; Timer? _syncTimer; - // late int lastMwebUtxosHeight; int mwebUtxosHeight = 0; late RpcClient _stub; @@ -156,6 +155,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future startSync() async { await super.startSync(); + _stub = await CwMweb.stub(); _syncTimer?.cancel(); _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { if (syncStatus is FailedSyncStatus) return; @@ -197,6 +197,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { processMwebUtxos(); } + @action + @override + Future stopSync() async { + _syncTimer?.cancel(); + await CwMweb.stop(); + } + Future initMwebUtxosBox() async { final boxName = "${walletInfo.name.replaceAll(" ", "_")}_${MwebUtxo.boxName}"; diff --git a/cw_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart index a616b0bfd..fd661c267 100644 --- a/cw_core/lib/wallet_base.dart +++ b/cw_core/lib/wallet_base.dart @@ -65,11 +65,12 @@ abstract class WalletBase startSync(); + Future stopSync() async {} + Future createTransaction(Object credentials); int calculateEstimatedFee(TransactionPriority priority, int? amount); - // void fetchTransactionsAsync( // void Function(TransactionType transaction) onTransactionLoaded, // {void Function() onFinished}); @@ -90,7 +91,8 @@ abstract class WalletBase renameWalletFiles(String newWalletName); - Future signMessage(String message, {String? address = null}) => throw UnimplementedError(); + Future signMessage(String message, {String? address = null}) => + throw UnimplementedError(); bool? isTestnet; } diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index bd9fff23e..996d4184a 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -21,13 +21,21 @@ public class CwMwebPlugin: NSObject, FlutterPlugin { // print("args: \(args)") let dataDir = args?["dataDir"] var error: NSError? + + if dataDir == "stop" && CwMwebPlugin.server != nil { + print("Stopping server") + CwMwebPlugin.server?.stop() + CwMwebPlugin.server = nil + result(0) + return + } if CwMwebPlugin.server == nil { CwMwebPlugin.server = MwebdNewServer("", dataDir, "", &error) if let server = CwMwebPlugin.server { do { - print("starting server2 \(CwMwebPlugin.port)") + print("starting server \(CwMwebPlugin.port)") try server.start(0, ret0_: &CwMwebPlugin.port) result(CwMwebPlugin.port) } catch let startError as NSError { diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index f16d451d9..d19c0fc01 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -6,9 +6,21 @@ import 'mwebd.pbgrpc.dart'; class CwMweb { static Future stub() async { final appDir = await getApplicationSupportDirectory(); - return RpcClient(ClientChannel('127.0.0.1', - port: await CwMwebPlatform.instance.start(appDir.path) ?? 0, - options: const ChannelOptions( - credentials: ChannelCredentials.insecure()))); + int port = await CwMwebPlatform.instance.start(appDir.path) ?? 0; + return RpcClient( + ClientChannel('127.0.0.1', + port: port, + options: const ChannelOptions( + credentials: ChannelCredentials.insecure(), + keepAlive: ClientKeepAliveOptions(permitWithoutCalls: true), + ), channelShutdownHandler: () { + print("CHANNEL IS BEING SHUT DOWN"); + // CwMwebPlatform.instance.stop(); + }), + ); + } + + static Future stop() async { + await CwMwebPlatform.instance.start("stop"); } } diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 26142c477..acdfa4346 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -143,12 +143,4 @@ import workmanager } } - override func applicationDidBecomeActive(_ application: UIApplication) { - signal(SIGPIPE, SIG_IGN); - } - - override func applicationWillEnterForeground(_ application: UIApplication) { - signal(SIGPIPE, SIG_IGN); - } - } diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index 7ad8af4c5..58a6e7036 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/core/totp_request_details.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/view_model/link_view_model.dart'; import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart'; @@ -133,6 +134,10 @@ class RootState extends State with WidgetsBindingObserver { setState(() => _setInactive(true)); } + if (widget.appStore.wallet?.type == WalletType.litecoin) { + widget.appStore.wallet?.stopSync(); + } + break; case AppLifecycleState.resumed: widget.authService.requireAuth().then((value) { @@ -142,6 +147,9 @@ class RootState extends State with WidgetsBindingObserver { }); } }); + if (widget.appStore.wallet?.type == WalletType.litecoin) { + widget.appStore.wallet?.startSync(); + } break; default: break; From 92669a65395adc2b675964e47dfed65dae6e7732 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 09:38:51 -0700 Subject: [PATCH 077/100] expirimental optimization --- lib/src/screens/root/root.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index 58a6e7036..6dabfa8df 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -134,9 +134,9 @@ class RootState extends State with WidgetsBindingObserver { setState(() => _setInactive(true)); } - if (widget.appStore.wallet?.type == WalletType.litecoin) { - widget.appStore.wallet?.stopSync(); - } + // if (widget.appStore.wallet?.type == WalletType.litecoin) { + // widget.appStore.wallet?.stopSync(); + // } break; case AppLifecycleState.resumed: From 7c5cd1959cc5f0692262b1826a19afce0439988a Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 09:41:26 -0700 Subject: [PATCH 078/100] [skip ci] minor enhancements --- cw_bitcoin/lib/litecoin_wallet.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index a42a7469b..3fd660a28 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -690,4 +690,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future save() async { await super.save(); } + + @override + Future close() async { + await super.close(); + await mwebUtxosBox.close(); + _syncTimer?.cancel(); + } } From 8ef96789d4cdf622f0b76fde0f3cfefd4fcc0f10 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 09:51:30 -0700 Subject: [PATCH 079/100] workflow and script fixes --- .github/workflows/pr_test_build.yml | 3 ++- scripts/android/build_mwebd.sh | 5 +++-- scripts/ios/build_mwebd.sh | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 74cefc4ec..1080e4085 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -109,7 +109,8 @@ jobs: # build mwebd: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd - gomobile bind -target=android -androidapi 21 ./mwebd + cd /opt/android/cake_wallet/mwebd + gomobile bind -target=android -androidapi 21 . mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh index 0bc770d51..d3a3bf091 100755 --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -7,9 +7,10 @@ go install golang.org/x/mobile/cmd/gomobile@latest gomobile init # build mwebd: git clone https://github.com/ltcmweb/mwebd -# go install github.com/ltcmweb/mwebd/cmd/mwebd +cd mwebd gomobile bind -target=android -androidapi 21 . -mkdir -p ../../cw_mweb/android/libs/ +mkdir -p ../../../cw_mweb/android/libs/ mv ./mwebd.aar $_ # cleanup: +cd .. rm -rf mwebd \ No newline at end of file diff --git a/scripts/ios/build_mwebd.sh b/scripts/ios/build_mwebd.sh index 4aab85142..ae0834e39 100755 --- a/scripts/ios/build_mwebd.sh +++ b/scripts/ios/build_mwebd.sh @@ -7,9 +7,10 @@ go install golang.org/x/mobile/cmd/gomobile@latest gomobile init # build mwebd: git clone https://github.com/ltcmweb/mwebd -# go install github.com/ltcmweb/mwebd/cmd/mwebd +cd mwebd gomobile bind -target=ios ./mwebd -mkdir -p ../../cw_mweb/android/libs/ -mv ./Mwebd.xcframework ../../ios/ +mkdir -p ../../../cw_mweb/android/libs/ +mv ./Mwebd.xcframework ../../../ios/ # cleanup: +cd .. rm -rf mwebd \ No newline at end of file From b77afee7ecd5adc4a92f2fa83c8f8be40ef3a101 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 09:52:56 -0700 Subject: [PATCH 080/100] workflow minor cleanup [skip ci] --- .github/workflows/pr_test_build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 1080e4085..bd015107a 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -113,6 +113,8 @@ jobs: gomobile bind -target=android -androidapi 21 . mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ + cd .. + rm -rf mwebd From b38deb4e7e22be5961b7101ce42db4515e680710 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 13:45:47 -0700 Subject: [PATCH 081/100] minor code cleanup & friendlier error message on failed tx's --- cw_bitcoin/lib/litecoin_wallet.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 3fd660a28..faf1f09e0 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -32,7 +32,6 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/litecoin_network.dart'; import 'package:cw_mweb/cw_mweb.dart'; -import 'package:cw_mweb/mwebd.pb.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bip39/bip39.dart' as bip39; @@ -278,7 +277,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) { addressRecord.txCount++; - print("COUNT UPDATED HERE 2!!!!! ${addressRecord.txCount}"); + // print("COUNT UPDATED HERE 2!!!!! ${addressRecord.txCount}"); } addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); @@ -380,7 +379,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { .firstWhere((addressRecord) => addressRecord.address == utxo.address); if (!inputAddresses.contains(utxo.address)) { addressRecord.txCount++; - print("COUNT UPDATED HERE 3!!!!! ${addressRecord.address} ${addressRecord.txCount} !!!!!!"); + // print("COUNT UPDATED HERE 3!!!!! ${addressRecord.address} ${addressRecord.txCount} !!!!!!"); } addressRecord.balance -= utxo.value.toInt(); amount += utxo.value.toInt(); @@ -542,7 +541,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { continue; } addressRecord.txCount++; - print("COUNT UPDATED HERE 0!!!!! ${addressRecord.address} ${addressRecord.txCount} !!!!!!"); + // print("COUNT UPDATED HERE 0!!!!! ${addressRecord.address} ${addressRecord.txCount} !!!!!!"); } } @@ -682,6 +681,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } catch (e, s) { print(e); print(s); + if (e.toString().contains("commit failed")) { + throw Exception("Transaction commit failed (no peers responded), please try again."); + } rethrow; } } From ba4893f46bb4dd44e134df78fb21e3eaa8fddff5 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 14:03:47 -0700 Subject: [PATCH 082/100] balance when sending fix --- cw_bitcoin/lib/litecoin_wallet.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index faf1f09e0..f40bf7f30 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -668,9 +668,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final utxo = mwebUtxosBox.get(id); await mwebUtxosBox.delete(id); if (utxo == null) return; + final addressRecord = walletAddresses.allAddresses + .firstWhere((addressRecord) => addressRecord.address == utxo.address); if (!addresses.contains(utxo.address)) { addresses.add(utxo.address); } + addressRecord.balance -= utxo.value.toInt(); }); transaction.inputAddresses?.addAll(addresses); From 65509c00b426ef6bc1f47b1c72a949352b89a6d0 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 14:32:07 -0700 Subject: [PATCH 083/100] experimental --- cw_bitcoin/lib/litecoin_wallet.dart | 36 +++++++++++++++++------------ 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index f40bf7f30..505ff9d8e 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -86,6 +86,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final bitcoin.HDWallet mwebHd; late final Box mwebUtxosBox; Timer? _syncTimer; + StreamSubscription? _utxoStream; int mwebUtxosHeight = 0; late RpcClient _stub; @@ -200,6 +201,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future stopSync() async { _syncTimer?.cancel(); + _utxoStream?.cancel(); await CwMweb.stop(); } @@ -308,10 +310,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await handleIncoming(utxo, _stub); - if (initDone) { - await updateUnspent(); - await updateBalance(); - } + // if (initDone) { + // await updateUnspent(); + // await updateBalance(); + // } if (utxo.height > walletInfo.restoreHeight) { walletInfo.updateRestoreHeight(utxo.height); @@ -319,7 +321,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } // process new utxos as they come in: - await for (Utxo sUtxo in _stub.utxos(req)) { + _utxoStream?.cancel(); + _utxoStream = _stub.utxos(req).listen((Utxo sUtxo) async { final utxo = MwebUtxo( address: sUtxo.address, blockTime: sUtxo.blockTime, @@ -328,28 +331,31 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { value: sUtxo.value.toInt(), ); - // if (mwebUtxosBox.containsKey(utxo.outputId)) { - // // we've already stored this utxo, skip it: - // continue; + if (mwebUtxosBox.containsKey(utxo.outputId)) { + // we've already stored this utxo, skip it: + return; + } + + // if (utxo.address.isEmpty) { + // await updateUnspent(); + // await updateBalance(); + // initDone = true; // } - if (utxo.address.isEmpty) { - await updateUnspent(); - await updateBalance(); - initDone = true; - } + await updateUnspent(); + await updateBalance(); final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; // don't process utxos with addresses that are not in the mwebAddrs list: if (utxo.address.isNotEmpty && !mwebAddrs.contains(utxo.address)) { - continue; + return; } await mwebUtxosBox.put(utxo.outputId, utxo); await handleIncoming(utxo, _stub); - } + }); } Future checkMwebUtxosSpent() async { From adee6ffe53be4b582911f8e0448528dc50773578 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 14:58:35 -0700 Subject: [PATCH 084/100] more experiments --- cw_bitcoin/lib/electrum_wallet.dart | 7 +++---- cw_bitcoin/lib/litecoin_wallet.dart | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 97d466ffe..d1b464f88 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -416,11 +416,10 @@ abstract class ElectrumWalletBase await _setInitialHeight(); } - await _subscribeForUpdates(); - - await updateTransactions(); - if (this is! LitecoinWallet) { + await _subscribeForUpdates(); + await updateTransactions(); + await updateAllUnspents(); await updateBalance(); } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 505ff9d8e..5cfb46709 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -179,19 +179,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { syncStatus = SyncedSyncStatus(); } - if (resp.mwebUtxosHeight > mwebUtxosHeight) { - mwebUtxosHeight = resp.mwebUtxosHeight; - await checkMwebUtxosSpent(); - // update the confirmations for each transaction: - for (final transaction in transactionHistory.transactions.values) { - if (transaction.isPending) continue; - final confirmations = mwebUtxosHeight - transaction.height + 1; - if (transaction.confirmations == confirmations) continue; - transaction.confirmations = confirmations; - transactionHistory.addOne(transaction); - } - await transactionHistory.save(); - } + // if (resp.mwebUtxosHeight > mwebUtxosHeight) { + // mwebUtxosHeight = resp.mwebUtxosHeight; + // await checkMwebUtxosSpent(); + // // update the confirmations for each transaction: + // for (final transaction in transactionHistory.transactions.values) { + // if (transaction.isPending) continue; + // final confirmations = mwebUtxosHeight - transaction.height + 1; + // if (transaction.confirmations == confirmations) continue; + // transaction.confirmations = confirmations; + // transactionHistory.addOne(transaction); + // } + // await transactionHistory.save(); + // } } }); processMwebUtxos(); From a2121ef73eaf7bd0a2c38fe4f93588d0077f7c57 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 17 Jul 2024 14:59:25 -0700 Subject: [PATCH 085/100] save --- cw_bitcoin/lib/litecoin_wallet.dart | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 5cfb46709..505ff9d8e 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -179,19 +179,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { syncStatus = SyncedSyncStatus(); } - // if (resp.mwebUtxosHeight > mwebUtxosHeight) { - // mwebUtxosHeight = resp.mwebUtxosHeight; - // await checkMwebUtxosSpent(); - // // update the confirmations for each transaction: - // for (final transaction in transactionHistory.transactions.values) { - // if (transaction.isPending) continue; - // final confirmations = mwebUtxosHeight - transaction.height + 1; - // if (transaction.confirmations == confirmations) continue; - // transaction.confirmations = confirmations; - // transactionHistory.addOne(transaction); - // } - // await transactionHistory.save(); - // } + if (resp.mwebUtxosHeight > mwebUtxosHeight) { + mwebUtxosHeight = resp.mwebUtxosHeight; + await checkMwebUtxosSpent(); + // update the confirmations for each transaction: + for (final transaction in transactionHistory.transactions.values) { + if (transaction.isPending) continue; + final confirmations = mwebUtxosHeight - transaction.height + 1; + if (transaction.confirmations == confirmations) continue; + transaction.confirmations = confirmations; + transactionHistory.addOne(transaction); + } + await transactionHistory.save(); + } } }); processMwebUtxos(); From 964f66c74ad3a0569acdbb85f6a3d206bdba5e62 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 18 Jul 2024 23:37:29 -0700 Subject: [PATCH 086/100] updates --- cw_bitcoin/lib/electrum_wallet.dart | 24 ++-- cw_bitcoin/lib/litecoin_wallet.dart | 129 ++++++++++++------ .../lib/pending_bitcoin_transaction.dart | 4 +- lib/bitcoin/cw_bitcoin.dart | 12 ++ lib/entities/preferences_key.dart | 2 + .../screens/dashboard/pages/balance_page.dart | 75 +++++++++- lib/store/settings_store.dart | 22 +++ .../dashboard/dashboard_view_model.dart | 18 +++ tool/configure.dart | 4 + 9 files changed, 229 insertions(+), 61 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index d1b464f88..38fded49e 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -416,23 +416,19 @@ abstract class ElectrumWalletBase await _setInitialHeight(); } - if (this is! LitecoinWallet) { - await _subscribeForUpdates(); - await updateTransactions(); + await subscribeForUpdates(); + await updateTransactions(); - await updateAllUnspents(); - await updateBalance(); - } + await updateAllUnspents(); + await updateBalance(); await updateFeeRates(); Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); - if (this is! LitecoinWallet) { - if (alwaysScan == true) { - _setListeners(walletInfo.restoreHeight); - } else { - syncStatus = SyncedSyncStatus(); - } + if (alwaysScan == true) { + _setListeners(walletInfo.restoreHeight); + } else { + syncStatus = SyncedSyncStatus(); } } catch (e, stacktrace) { print(stacktrace); @@ -1595,7 +1591,7 @@ abstract class ElectrumWalletBase matchedAddresses.toList(), addressRecord.isHidden, (address) async { - await _subscribeForUpdates(); + await subscribeForUpdates(); return _fetchAddressHistory(address, await getCurrentChainTip()) .then((history) => history.isNotEmpty ? address.address : null); }, @@ -1684,7 +1680,7 @@ abstract class ElectrumWalletBase } } - Future _subscribeForUpdates() async { + Future subscribeForUpdates() async { final unsubscribedScriptHashes = walletAddresses.allAddresses.where( (address) => !_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)), ); diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 505ff9d8e..22fb8ebc7 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -89,6 +89,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { StreamSubscription? _utxoStream; int mwebUtxosHeight = 0; late RpcClient _stub; + late bool mwebEnabled = true; static Future create( {required String mnemonic, @@ -154,17 +155,26 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @action @override Future startSync() async { - await super.startSync(); + if (!mwebEnabled) { + syncStatus = SyncronizingSyncStatus(); + await subscribeForUpdates(); + await updateTransactions(); + syncStatus = SyncedSyncStatus(); + return; + } + + await subscribeForUpdates(); + await updateTransactions(); + await updateFeeRates(); + + Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); + _stub = await CwMweb.stub(); _syncTimer?.cancel(); _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { if (syncStatus is FailedSyncStatus) return; final height = await electrumClient.getCurrentBlockChainTip() ?? 0; final resp = await _stub.status(StatusRequest()); - // print("height: $height"); - // print("resp.blockHeaderHeight: ${resp.blockHeaderHeight}"); - // print("resp.mwebHeaderHeight: ${resp.mwebHeaderHeight}"); - // print("resp.mwebUtxosHeight: ${resp.mwebUtxosHeight}"); if (resp.blockHeaderHeight < height) { int h = resp.blockHeaderHeight; syncStatus = SyncingSyncStatus(height - h, h / height); @@ -211,6 +221,21 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { mwebUtxosBox = await CakeHive.openBox(boxName); } + @override + Future renameWalletFiles(String newWalletName) async { + // rename the hive box: + final oldBoxName = "${walletInfo.name.replaceAll(" ", "_")}_${MwebUtxo.boxName}"; + final newBoxName = "${newWalletName.replaceAll(" ", "_")}_${MwebUtxo.boxName}"; + + final oldBox = await Hive.openBox(oldBoxName); + mwebUtxosBox = await CakeHive.openBox(newBoxName); + for (final key in oldBox.keys) { + await mwebUtxosBox.put(key, oldBox.get(key)!); + } + + await super.renameWalletFiles(newWalletName); + } + @action @override Future rescan({ @@ -261,11 +286,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } - tx.height = utxo.height; - tx.isPending = utxo.height == 0; - tx.confirmations = confirmations; bool isNew = transactionHistory.transactions[tx.id] == null; + // don't update the confirmations if the tx is updated by electrum: + if (tx.confirmations == 0 || utxo.height != 0) { + tx.height = utxo.height; + tx.isPending = utxo.height == 0; + tx.confirmations = confirmations; + } + if (!(tx.outputAddresses?.contains(utxo.address) ?? false)) { tx.outputAddresses?.add(utxo.address); isNew = true; @@ -295,12 +324,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { int restoreHeight = walletInfo.restoreHeight; print("SCANNING FROM HEIGHT: $restoreHeight"); final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: restoreHeight); - bool initDone = false; // process old utxos: for (final utxo in mwebUtxosBox.values) { if (utxo.address.isEmpty) { - initDone = true; continue; } @@ -310,11 +337,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await handleIncoming(utxo, _stub); - // if (initDone) { - // await updateUnspent(); - // await updateBalance(); - // } - if (utxo.height > walletInfo.restoreHeight) { walletInfo.updateRestoreHeight(utxo.height); } @@ -459,36 +481,38 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { updatedUnspentCoins.addAll(await fetchUnspent(address)); })); - // update mweb unspents: - final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; - mwebUtxosBox.keys.forEach((dynamic oId) { - final String outputId = oId as String; - final utxo = mwebUtxosBox.get(outputId); - if (utxo == null) { - return; - } - if (utxo.address.isEmpty) { - // not sure if a bug or a special case but we definitely ignore these - return; - } - final addressRecord = walletAddresses.allAddresses - .firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address); + if (mwebEnabled) { + // update mweb unspents: + final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; + mwebUtxosBox.keys.forEach((dynamic oId) { + final String outputId = oId as String; + final utxo = mwebUtxosBox.get(outputId); + if (utxo == null) { + return; + } + if (utxo.address.isEmpty) { + // not sure if a bug or a special case but we definitely ignore these + return; + } + final addressRecord = walletAddresses.allAddresses + .firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address); - if (addressRecord == null) { - print("addressRecord is null! TODO: handle this case2"); - return; - } - final unspent = BitcoinUnspent( - addressRecord, - outputId, - utxo.value.toInt(), - mwebAddrs.indexOf(utxo.address), - ); - if (unspent.vout == 0) { - unspent.isChange = true; - } - updatedUnspentCoins.add(unspent); - }); + if (addressRecord == null) { + print("utxo contains an address that is not in the wallet: ${utxo.address}"); + return; + } + final unspent = BitcoinUnspent( + addressRecord, + outputId, + utxo.value.toInt(), + mwebAddrs.indexOf(utxo.address), + ); + if (unspent.vout == 0) { + unspent.isChange = true; + } + updatedUnspentCoins.add(unspent); + }); + } unspentCoins = updatedUnspentCoins; } @@ -638,7 +662,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future createTransaction(Object credentials) async { try { - final tx = await super.createTransaction(credentials) as PendingBitcoinTransaction; + var tx = await super.createTransaction(credentials) as PendingBitcoinTransaction; + tx.isMweb = mwebEnabled; + + if (!mwebEnabled) { + return tx; + } final resp = await _stub.create(CreateRequest( rawTx: hex.decode(tx.hex), @@ -708,4 +737,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await mwebUtxosBox.close(); _syncTimer?.cancel(); } + + void setMwebEnabled(bool enabled) { + if (!mwebEnabled && enabled) { + mwebEnabled = enabled; + startSync(); + } + mwebEnabled = enabled; + } + + bool get isMwebEnabled => mwebEnabled; } diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index 4217d35b4..df5826a3c 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -23,6 +23,7 @@ class PendingBitcoinTransaction with PendingTransaction { required this.hasChange, this.isSendAll = false, this.hasTaprootInputs = false, + this.isMweb = false, }) : _listeners = []; final WalletType type; @@ -35,6 +36,7 @@ class PendingBitcoinTransaction with PendingTransaction { final bool isSendAll; final bool hasChange; final bool hasTaprootInputs; + bool isMweb; String? idOverride; String? hexOverride; List? outputs; @@ -103,7 +105,7 @@ class PendingBitcoinTransaction with PendingTransaction { @override Future commit() async { - if (network is LitecoinNetwork) { + if (isMweb) { await _ltcCommit(); } else { await _commit(); diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index bc01b33ec..7e0264962 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -582,4 +582,16 @@ class CWBitcoin extends Bitcoin { final bitcoinWallet = wallet as ElectrumWallet; await bitcoinWallet.updateFeeRates(); } + + @override + void setMwebEnabled(Object wallet, bool enabled) { + final litecoinWallet = wallet as LitecoinWallet; + litecoinWallet.setMwebEnabled(enabled); + } + + @override + bool getMwebEnabled(Object wallet) { + final litecoinWallet = wallet as LitecoinWallet; + return litecoinWallet.isMwebEnabled; + } } diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index e1ee0ada3..2c669c3bd 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -48,6 +48,8 @@ class PreferencesKey { static const customBitcoinFeeRate = 'custom_electrum_fee_rate'; static const silentPaymentsCardDisplay = 'silentPaymentsCardDisplay'; static const silentPaymentsAlwaysScan = 'silentPaymentsAlwaysScan'; + static const mwebCardDisplay = 'mwebCardDisplay'; + static const mwebEnabled = 'mwebEnabled'; static const shouldShowReceiveWarning = 'should_show_receive_warning'; static const shouldShowYatPopup = 'should_show_yat_popup'; static const shouldShowRepWarning = 'should_show_rep_warning'; diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index 7ffcf918d..530038ee9 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -315,7 +315,73 @@ class CryptoBalanceWidget extends StatelessWidget { ), ), ), - ] + ], + if (dashboardViewModel.showMwebCard) ...[ + SizedBox(height: 10), + Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), + child: DashBoardRoundedCardWidget( + customBorder: 30, + title: "T: MWEB", + subTitle: "T: Enable MWEB", + hint: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => launchUrl( + Uri.parse( + "https://guides.cakewallet.com/docs/cryptos/bitcoin/#silent-payments"), + mode: LaunchMode.externalApplication, + ), + child: Row( + children: [ + Text( + "T: What is MWEB?", + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context) + .extension()! + .labelTextColor, + height: 1, + ), + softWrap: true, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Icon(Icons.help_outline, + size: 16, + color: Theme.of(context) + .extension()! + .labelTextColor), + ) + ], + ), + ), + Observer( + builder: (_) => StandardSwitch( + value: dashboardViewModel.mwebEnabled, + onTaped: () => _toggleMweb(context), + ), + ) + ], + ), + ], + ), + onTap: () => _toggleMweb(context), + icon: Icon( + Icons.lock, + color: + Theme.of(context).extension()!.pageTitleTextColor, + size: 50, + ), + ), + ), + ], ], ); }), @@ -355,6 +421,13 @@ class CryptoBalanceWidget extends StatelessWidget { return dashboardViewModel.setSilentPaymentsScanning(newValue); } + + + Future _toggleMweb(BuildContext context) async { + final isMwebEnabled = dashboardViewModel.mwebEnabled; + final newValue = !isMwebEnabled; + return dashboardViewModel.setMwebEnabled(newValue); + } } class BalanceRowWidget extends StatelessWidget { diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 8e16adbff..b6947b1c7 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -110,6 +110,8 @@ abstract class SettingsStoreBase with Store { required this.customBitcoinFeeRate, required this.silentPaymentsCardDisplay, required this.silentPaymentsAlwaysScan, + required this.mwebCardDisplay, + required this.mwebEnabled, TransactionPriority? initialBitcoinTransactionPriority, TransactionPriority? initialMoneroTransactionPriority, TransactionPriority? initialWowneroTransactionPriority, @@ -536,6 +538,14 @@ abstract class SettingsStoreBase with Store { (bool silentPaymentsAlwaysScan) => _sharedPreferences.setBool( PreferencesKey.silentPaymentsAlwaysScan, silentPaymentsAlwaysScan)); + reaction( + (_) => mwebCardDisplay, + (bool mwebCardDisplay) => + _sharedPreferences.setBool(PreferencesKey.mwebCardDisplay, mwebCardDisplay)); + + reaction((_) => mwebEnabled, + (bool mwebEnabled) => _sharedPreferences.setBool(PreferencesKey.mwebEnabled, mwebEnabled)); + this.nodes.observe((change) { if (change.newValue != null && change.key != null) { _saveCurrentNode(change.newValue!, change.key!); @@ -737,6 +747,12 @@ abstract class SettingsStoreBase with Store { @observable bool silentPaymentsAlwaysScan; + @observable + bool mwebCardDisplay; + + @observable + bool mwebEnabled; + final SecureStorage _secureStorage; final SharedPreferences _sharedPreferences; final BackgroundTasks _backgroundTasks; @@ -893,6 +909,8 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; final silentPaymentsAlwaysScan = sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false; + final mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; + final mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; // If no value if (pinLength == null || pinLength == 0) { @@ -1145,6 +1163,8 @@ abstract class SettingsStoreBase with Store { customBitcoinFeeRate: customBitcoinFeeRate, silentPaymentsCardDisplay: silentPaymentsCardDisplay, silentPaymentsAlwaysScan: silentPaymentsAlwaysScan, + mwebCardDisplay: mwebCardDisplay, + mwebEnabled: mwebEnabled, initialMoneroTransactionPriority: moneroTransactionPriority, initialWowneroTransactionPriority: wowneroTransactionPriority, initialBitcoinTransactionPriority: bitcoinTransactionPriority, @@ -1293,6 +1313,8 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; silentPaymentsAlwaysScan = sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false; + mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; + mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey); final bitcoinElectrumServerId = sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 28ac98978..f9729e5f5 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -329,6 +329,24 @@ abstract class DashboardViewModelBase with Store { } } + @computed + bool get hasMweb => wallet.type == WalletType.litecoin; + + @computed + bool get showMwebCard => hasMweb && settingsStore.mwebCardDisplay; + + @observable + bool mwebEnabled = false; + + @action + void setMwebEnabled(bool active) { + mwebEnabled = active; + + if (hasMweb) { + bitcoin!.setMwebEnabled(wallet, active); + } + } + BalanceViewModel balanceViewModel; AppStore appStore; diff --git a/tool/configure.dart b/tool/configure.dart index 853d06448..f2907678f 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -115,6 +115,7 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_bitcoin/litecoin_wallet_service.dart'; +import 'package:cw_bitcoin/litecoin_wallet.dart'; import 'package:cw_core/get_height_by_date.dart'; import 'package:cw_bitcoin/script_hash.dart'; import 'package:cw_bitcoin/bitcoin_hardware_wallet_service.dart'; @@ -219,6 +220,9 @@ abstract class Bitcoin { void setLedger(WalletBase wallet, Ledger ledger, LedgerDevice device); Future> getHardwareWalletAccounts(LedgerViewModel ledgerVM, {int index = 0, int limit = 5}); + + void setMwebEnabled(Object wallet, bool enabled); + bool getMwebEnabled(Object wallet); } """; From 399d0cab8b85ebdb904cb1c1947335b7e0e998b2 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 19 Jul 2024 00:06:23 -0700 Subject: [PATCH 087/100] coin control edge cases --- cw_bitcoin/lib/electrum_wallet.dart | 2 +- cw_bitcoin/lib/litecoin_wallet.dart | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 38fded49e..276d192b9 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1168,7 +1168,7 @@ abstract class ElectrumWalletBase Future updateUnspent() async { await updateAllUnspents(); - if (unspentCoinsInfo.isEmpty) { + if (unspentCoinsInfo.length != unspentCoins.length) { unspentCoins.forEach((coin) => addCoinInfo(coin)); return; } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 22fb8ebc7..684562eaf 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -205,6 +205,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } }); processMwebUtxos(); + updateUnspent(); } @action @@ -514,6 +515,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { }); } + print(unspentCoins); + print(updatedUnspentCoins); + print(updatedUnspentCoins.length); + + // print(updatedUnspentCoins[2].address); + unspentCoins = updatedUnspentCoins; } @@ -575,6 +582,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } + await updateUnspent(); + return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } From ea28698b2dee7097364b6268966735ca5df95aae Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 19 Jul 2024 09:15:28 -0700 Subject: [PATCH 088/100] remove neutrino.db if no litecoin wallets left after deleting --- cw_bitcoin/lib/litecoin_wallet.dart | 10 +++-- cw_bitcoin/lib/litecoin_wallet_service.dart | 47 ++++++++++++++------- cw_mweb/lib/cw_mweb.dart | 5 +-- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 684562eaf..3ed027f24 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -204,8 +204,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } }); - processMwebUtxos(); updateUnspent(); + // this runs in the background and processes new utxos as they come in: + processMwebUtxos(); } @action @@ -515,9 +516,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { }); } - print(unspentCoins); - print(updatedUnspentCoins); - print(updatedUnspentCoins.length); + // print(unspentCoins); + // print(updatedUnspentCoins); + // print(updatedUnspentCoins.length); // print(updatedUnspentCoins[2].address); @@ -745,6 +746,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await super.close(); await mwebUtxosBox.close(); _syncTimer?.cancel(); + _utxoStream?.cancel(); } void setMwebEnabled(bool enabled) { diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index bb51a4eaa..a41920dcd 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -12,11 +12,13 @@ import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:collection/collection.dart'; import 'package:bip39/bip39.dart' as bip39; +import 'package:path_provider/path_provider.dart'; class LitecoinWalletService extends WalletService< BitcoinNewWalletCredentials, BitcoinRestoreWalletFromSeedCredentials, - BitcoinRestoreWalletFromWIFCredentials,BitcoinNewWalletCredentials> { + BitcoinRestoreWalletFromWIFCredentials, + BitcoinNewWalletCredentials> { LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); final Box walletInfoSource; @@ -45,12 +47,14 @@ class LitecoinWalletService extends WalletService< @override Future openWallet(String name, String password) async { - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(name, getType()))!; + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!; try { final wallet = await LitecoinWalletBase.open( - password: password, name: name, walletInfo: walletInfo, + password: password, + name: name, + walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource); await wallet.init(); saveBackup(name); @@ -58,7 +62,9 @@ class LitecoinWalletService extends WalletService< } catch (_) { await restoreWalletFilesFromBackup(name); final wallet = await LitecoinWalletBase.open( - password: password, name: name, walletInfo: walletInfo, + password: password, + name: name, + walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource); await wallet.init(); return wallet; @@ -67,17 +73,25 @@ class LitecoinWalletService extends WalletService< @override Future remove(String wallet) async { - File(await pathForWalletDir(name: wallet, type: getType())) - .delete(recursive: true); - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(wallet, getType()))!; + File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!; await walletInfoSource.delete(walletInfo.key); + + // if there are no more litecoin wallets left, delete the neutrino db: + if (walletInfoSource.values.where((info) => info.type == WalletType.litecoin).isEmpty) { + final appDir = await getApplicationSupportDirectory(); + File neturinoDb = File('${appDir.path}/neutrino.db'); + if (neturinoDb.existsSync()) { + neturinoDb.deleteSync(); + } + } } @override Future rename(String currentName, String password, String newName) async { - final currentWalletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(currentName, getType()))!; + final currentWalletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!; final currentWallet = await LitecoinWalletBase.open( password: password, name: currentName, @@ -96,17 +110,18 @@ class LitecoinWalletService extends WalletService< @override Future restoreFromHardwareWallet(BitcoinNewWalletCredentials credentials) { - throw UnimplementedError("Restoring a Litecoin wallet from a hardware wallet is not yet supported!"); + throw UnimplementedError( + "Restoring a Litecoin wallet from a hardware wallet is not yet supported!"); } @override - Future restoreFromKeys( - BitcoinRestoreWalletFromWIFCredentials credentials, {bool? isTestnet}) async => + Future restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials, + {bool? isTestnet}) async => throw UnimplementedError(); @override - Future restoreFromSeed( - BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { + Future restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials, + {bool? isTestnet}) async { if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { throw LitecoinMnemonicIsIncorrectException(); } diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index d19c0fc01..765ba7911 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -13,10 +13,7 @@ class CwMweb { options: const ChannelOptions( credentials: ChannelCredentials.insecure(), keepAlive: ClientKeepAliveOptions(permitWithoutCalls: true), - ), channelShutdownHandler: () { - print("CHANNEL IS BEING SHUT DOWN"); - // CwMwebPlatform.instance.stop(); - }), + )), ); } From 3103f3c893a7680a9133ab49426c93e4903b3b51 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 19 Jul 2024 09:22:12 -0700 Subject: [PATCH 089/100] update translations --- lib/src/screens/dashboard/pages/balance_page.dart | 6 +++--- res/values/strings_ar.arb | 3 +++ res/values/strings_bg.arb | 3 +++ res/values/strings_cs.arb | 3 +++ res/values/strings_de.arb | 5 ++++- res/values/strings_en.arb | 3 +++ res/values/strings_es.arb | 3 +++ res/values/strings_fr.arb | 5 ++++- res/values/strings_ha.arb | 3 +++ res/values/strings_hi.arb | 3 +++ res/values/strings_hr.arb | 3 +++ res/values/strings_id.arb | 3 +++ res/values/strings_it.arb | 3 +++ res/values/strings_ja.arb | 3 +++ res/values/strings_ko.arb | 3 +++ res/values/strings_my.arb | 3 +++ res/values/strings_nl.arb | 3 +++ res/values/strings_pl.arb | 3 +++ res/values/strings_pt.arb | 3 +++ res/values/strings_ru.arb | 3 +++ res/values/strings_th.arb | 3 +++ res/values/strings_tl.arb | 3 +++ res/values/strings_tr.arb | 3 +++ res/values/strings_uk.arb | 3 +++ res/values/strings_ur.arb | 3 +++ res/values/strings_yo.arb | 3 +++ res/values/strings_zh.arb | 3 +++ 27 files changed, 83 insertions(+), 5 deletions(-) diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index 530038ee9..7c3db4922 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -322,8 +322,8 @@ class CryptoBalanceWidget extends StatelessWidget { padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), child: DashBoardRoundedCardWidget( customBorder: 30, - title: "T: MWEB", - subTitle: "T: Enable MWEB", + title: S.current.litecoin_mweb, + subTitle: S.current.litecoin_enable_mweb_sync, hint: Column( children: [ Row( @@ -339,7 +339,7 @@ class CryptoBalanceWidget extends StatelessWidget { child: Row( children: [ Text( - "T: What is MWEB?", + S.current.litecoin_what_is_mweb, style: TextStyle( fontSize: 12, fontFamily: 'Lato', diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index db6a4cae2..ce00e8b5c 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "يرجى التأكد", "ledger_please_enable_bluetooth": "يرجى تمكين البلوتوث للكشف عن دفتر الأستاذ الخاص بك", "light_theme": "فاتح", + "litecoin_enable_mweb_sync": "تمكين MWEB المزامنة", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "ما هو MWEB؟", "load_more": "تحميل المزيد", "loading_your_wallet": "يتم تحميل محفظتك", "login": "تسجيل الدخول", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 06132c244..c81124627 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Моля, уверете се, че сте отворили правилното приложение на вашата книга", "ledger_please_enable_bluetooth": "Моля, активирайте Bluetooth да открие вашата книга", "light_theme": "Светло", + "litecoin_enable_mweb_sync": "Активиране на MWEB Sync", + "litecoin_mweb": "Litecoin MWeb", + "litecoin_what_is_mweb": "Какво е MWEB?", "load_more": "Зареди още", "loading_your_wallet": "Зареждане на портфейл", "login": "Влизане", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 589f89fd7..b7530cd03 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Ujistěte se, že se na své knize otevřete správnou aplikaci", "ledger_please_enable_bluetooth": "Umožněte prosím Bluetooth detekovat vaši knihu", "light_theme": "Světlý", + "litecoin_enable_mweb_sync": "Povolit synchronizaci MWeb", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "Co je Mweb?", "load_more": "Načíst další", "loading_your_wallet": "Načítám peněženku", "login": "Login", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index a4b816f5b..3219028e2 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Bitte stellen Sie sicher, dass Sie die richtige App auf Ihrem Ledger geöffnet haben", "ledger_please_enable_bluetooth": "Bitte aktivieren Sie Bluetooth um sich mit Ihren Ledger zu verbinden.", "light_theme": "Hell", + "litecoin_enable_mweb_sync": "Aktivieren Sie die MWEB -Synchronisierung", + "litecoin_mweb": "Litecoin MWeb", + "litecoin_what_is_mweb": "Was ist MWeb?", "load_more": "Mehr laden", "loading_your_wallet": "Wallet wird geladen", "login": "Einloggen", @@ -876,4 +879,4 @@ "you_will_get": "Konvertieren zu", "you_will_send": "Konvertieren von", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index d39b175dd..7b2a6351e 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Please make sure you opend the right app on your ledger", "ledger_please_enable_bluetooth": "Please enable Bluetooth to detect your Ledger", "light_theme": "Light", + "litecoin_enable_mweb_sync": "Enable MWEB sync", + "litecoin_mweb": "Litecoin MWEB", + "litecoin_what_is_mweb": "What is MWEB?", "load_more": "Load more", "loading_your_wallet": "Loading your wallet", "login": "Login", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 5fba46830..93af8d398 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Por favor, asegúrese de abrir la aplicación correcta en su libro mayor.", "ledger_please_enable_bluetooth": "Habilite Bluetooth para detectar su libro mayor", "light_theme": "Ligera", + "litecoin_enable_mweb_sync": "Habilitar MWEB Sync", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "¿Qué es mweb?", "load_more": "Carga más", "loading_your_wallet": "Cargando tu billetera", "login": "Iniciar sesión", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 7289b7511..7af05b723 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Veuillez vous assurer d'ouvrir la bonne application sur votre grand livre", "ledger_please_enable_bluetooth": "Veuillez activer Bluetooth pour détecter votre grand livre", "light_theme": "Clair", + "litecoin_enable_mweb_sync": "Activer la synchronisation MWEB", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "Qu'est-ce que MWEB?", "load_more": "Charger plus", "loading_your_wallet": "Chargement de votre portefeuille (wallet)", "login": "Utilisateur", @@ -873,4 +876,4 @@ "you_will_get": "Convertir vers", "you_will_send": "Convertir depuis", "yy": "AA" -} +} \ No newline at end of file diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index c96568a76..362d95982 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Da fatan za a tabbata kun yi amfani da app ɗin dama akan dillalarku", "ledger_please_enable_bluetooth": "Da fatan za a kunna Bluetooth don gano Ledger ɗinku", "light_theme": "Haske", + "litecoin_enable_mweb_sync": "Kunna Mweb Sync", + "litecoin_mweb": "Litcoin Mweb", + "litecoin_what_is_mweb": "Menene Mweb?", "load_more": "Like more", "loading_your_wallet": "Ana loda walat ɗin ku", "login": "Shiga", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index ef740b4d6..f58f8a8af 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "कृपया सुनिश्चित करें कि आप अपने लेजर पर सही ऐप को खोलते हैं", "ledger_please_enable_bluetooth": "कृपया अपने बहीखाने का पता लगाने के लिए ब्लूटूथ को सक्षम करें", "light_theme": "रोशनी", + "litecoin_enable_mweb_sync": "MWEB सिंक सक्षम करें", + "litecoin_mweb": "लिटकोइन मेवेब", + "litecoin_what_is_mweb": "MWEB क्या है?", "load_more": "और लोड करें", "loading_your_wallet": "अपना बटुआ लोड कर रहा है", "login": "लॉग इन करें", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 295a01165..b21c068e3 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Obavezno obavezno otvorite pravu aplikaciju na knjizi", "ledger_please_enable_bluetooth": "Omogućite Bluetooth da otkrije svoju knjigu", "light_theme": "Svijetla", + "litecoin_enable_mweb_sync": "Omogući MWEB sinkronizaciju", + "litecoin_mweb": "Litecoin MWeb", + "litecoin_what_is_mweb": "Što je MWEB?", "load_more": "Učitaj više", "loading_your_wallet": "Novčanik se učitava", "login": "Prijava", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 8b28e928f..27ae35fcd 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Pastikan Anda membuka aplikasi yang tepat di buku besar Anda", "ledger_please_enable_bluetooth": "Harap aktifkan Bluetooth untuk mendeteksi buku besar Anda", "light_theme": "Terang", + "litecoin_enable_mweb_sync": "Aktifkan Sinkronisasi MWEB", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "Apa itu MWEB?", "load_more": "Muat lebih banyak", "loading_your_wallet": "Memuat dompet Anda", "login": "Masuk", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index b1bd7cd6d..bd8c148ab 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -348,6 +348,9 @@ "ledger_error_wrong_app": "Assicurati di aprire l'app giusta sul libro mastro", "ledger_please_enable_bluetooth": "Si prega di consentire al Bluetooth di rilevare il libro mastro", "light_theme": "Bianco", + "litecoin_enable_mweb_sync": "Abilita MWeb Sync", + "litecoin_mweb": "Litecoin MWeb", + "litecoin_what_is_mweb": "Cos'è MWeb?", "load_more": "Carica di più", "loading_your_wallet": "Caricamento portafoglio", "login": "Accedi", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 5803b86d5..648a82a0d 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -348,6 +348,9 @@ "ledger_error_wrong_app": "元帳に適切なアプリを開始するようにしてください", "ledger_please_enable_bluetooth": "Bluetoothが元帳を検出できるようにしてください", "light_theme": "光", + "litecoin_enable_mweb_sync": "MWEB同期を有効にします", + "litecoin_mweb": "litecoin mweb", + "litecoin_what_is_mweb": "MWEBとは何ですか?", "load_more": "もっと読み込む", "loading_your_wallet": "ウォレットをロードしています", "login": "ログイン", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 874ec2c27..e7dac598d 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "원장에서 올바른 앱을 반대하는지 확인하십시오.", "ledger_please_enable_bluetooth": "Bluetooth가 원장을 감지 할 수 있도록하십시오", "light_theme": "빛", + "litecoin_enable_mweb_sync": "mweb 동기화를 활성화합니다", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "MWEB 란 무엇입니까?", "load_more": "더로드하십시오", "loading_your_wallet": "지갑 넣기", "login": "로그인", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 890362fcd..0ca6e4dbd 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "ကျေးဇူးပြု. သင့်လက်ျာအက်ပ်ကိုသင်၏ Ledger တွင်ဖွင့်ရန်သေချာစေပါ", "ledger_please_enable_bluetooth": "သင်၏ Ledger ကိုရှာဖွေရန် Bluetooth ကိုဖွင့်ပါ", "light_theme": "အလင်း", + "litecoin_enable_mweb_sync": "mweb စည်းညှိမှုကို enable", + "litecoin_mweb": "Litecoin Mweb", + "litecoin_what_is_mweb": "MweB ဆိုတာဘာလဲ။", "load_more": "ပိုပြီး load", "loading_your_wallet": "သင့်ပိုက်ဆံအိတ်ကို ဖွင့်နေသည်။", "login": "လော့ဂ်အင်", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 25b7f6b3a..3df71b0dd 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Zorg ervoor dat u de juiste app op uw grootboek opent", "ledger_please_enable_bluetooth": "Schakel Bluetooth in staat om uw grootboek te detecteren", "light_theme": "Licht", + "litecoin_enable_mweb_sync": "MWEB SYNC inschakelen", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "Wat is Mweb?", "load_more": "Meer laden", "loading_your_wallet": "Uw portemonnee laden", "login": "Log in", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 1567a0841..7b185472f 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Upewnij się, że opisz odpowiednią aplikację na swojej księdze", "ledger_please_enable_bluetooth": "Włącz Bluetooth wykrywanie księgi", "light_theme": "Jasny", + "litecoin_enable_mweb_sync": "Włącz synchronizację MWEB", + "litecoin_mweb": "Litecoin MWEB", + "litecoin_what_is_mweb": "Co to jest MWEB?", "load_more": "Załaduj więcej", "loading_your_wallet": "Ładowanie portfela", "login": "Login", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 272c0862e..021a57194 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Por favor, certifique -se de optar pelo aplicativo certo no seu livro", "ledger_please_enable_bluetooth": "Ative o Bluetooth para detectar seu livro", "light_theme": "Luz", + "litecoin_enable_mweb_sync": "Habilite MWEB Sync", + "litecoin_mweb": "Litecoin Mweb", + "litecoin_what_is_mweb": "O que é MWeb?", "load_more": "Carregue mais", "loading_your_wallet": "Abrindo sua carteira", "login": "Login", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index a8943db97..a1732a3e4 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Пожалуйста, убедитесь, что вы предлагаете правильное приложение в своей бухгалтерской книге", "ledger_please_enable_bluetooth": "Пожалуйста, включите Bluetooth обнаружить вашу бухгалтерскую книгу", "light_theme": "Светлая", + "litecoin_enable_mweb_sync": "Включить MWEB Sync", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "Что такое MWEB?", "load_more": "Загрузи больше", "loading_your_wallet": "Загрузка кошелька", "login": "Логин", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index c4d282761..7a68b3bf2 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "โปรดตรวจสอบให้แน่ใจว่าคุณเปิดแอพที่เหมาะสมในบัญชีแยกประเภทของคุณ", "ledger_please_enable_bluetooth": "โปรดเปิดใช้งานบลูทู ธ ในการตรวจจับบัญชีแยกประเภทของคุณ", "light_theme": "สว่าง", + "litecoin_enable_mweb_sync": "เปิดใช้งานการซิงค์ MWEB", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "MWEB คืออะไร?", "load_more": "โหลดมากขึ้น", "loading_your_wallet": "กำลังโหลดกระเป๋าของคุณ", "login": "เข้าสู่ระบบ", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 775ab4c3d..e7cefbba7 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Mangyaring tiyaking pinipili mo ang tamang app sa iyong ledger", "ledger_please_enable_bluetooth": "Mangyaring paganahin ang Bluetooth upang makita ang iyong ledger", "light_theme": "Ilaw", + "litecoin_enable_mweb_sync": "Paganahin ang MWEB Sync", + "litecoin_mweb": "Litecoin Mweb", + "litecoin_what_is_mweb": "Ano ang MWEB?", "load_more": "Mag -load pa", "loading_your_wallet": "Naglo -load ng iyong pitaka", "login": "Mag log in", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 239a1aa2e..cf55feaca 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Lütfen defterinizde doğru uygulamayı açtığınızdan emin olun", "ledger_please_enable_bluetooth": "Defterinizi algılamak için lütfen Bluetooth'u etkinleştirin", "light_theme": "Aydınlık", + "litecoin_enable_mweb_sync": "MWEB senkronizasyonunu etkinleştir", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "MWEB nedir?", "load_more": "Daha fazla yükle", "loading_your_wallet": "Cüzdanın yükleniyor", "login": "Login", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 3a45752d6..08eae3f6c 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "Будь ласка, переконайтеся, що ви відкриваєте потрібну програму на своїй книзі", "ledger_please_enable_bluetooth": "Будь ласка, ввімкніть Bluetooth виявити свою книгу", "light_theme": "Світла", + "litecoin_enable_mweb_sync": "Увімкнути MWEB SYNC", + "litecoin_mweb": "Litecoin mweb", + "litecoin_what_is_mweb": "Що таке mweb?", "load_more": "Завантажити ще", "loading_your_wallet": "Завантаження гаманця", "login": "Логін", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 2ab1f927b..4a18365e1 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "براہ کرم یقینی بنائیں کہ آپ اپنے لیجر پر صحیح ایپ کو کھولتے ہیں", "ledger_please_enable_bluetooth": "براہ کرم بلوٹوتھ کو اپنے لیجر کا پتہ لگانے کے لئے اہل بنائیں", "light_theme": "روشنی", + "litecoin_enable_mweb_sync": "MWEB مطابقت پذیری کو فعال کریں", + "litecoin_mweb": "litcoin mweb", + "litecoin_what_is_mweb": "MWEB کیا ہے؟", "load_more": "مزید لوڈ کریں", "loading_your_wallet": "آپ کا بٹوہ لوڈ ہو رہا ہے۔", "login": "لاگ ان کریں", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index f9751f9f1..e46e5f92a 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -348,6 +348,9 @@ "ledger_error_wrong_app": "Jọwọ rii daju pe iwọ yoo sọ app ti o tọ loju omi rẹ", "ledger_please_enable_bluetooth": "Jọwọ jẹ ki Bluetooth lati rii iṣupọ rẹ", "light_theme": "Funfun bí eérú", + "litecoin_enable_mweb_sync": "Mu ṣiṣẹmu MweB", + "litecoin_mweb": "Livecoin mweb", + "litecoin_what_is_mweb": "Kini mweb?", "load_more": "Ẹru diẹ sii", "loading_your_wallet": "A ń ṣí àpamọ́wọ́ yín", "login": "Orúkọ", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index f3b8ee176..7cf83b226 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -347,6 +347,9 @@ "ledger_error_wrong_app": "请确保您在分类帐中操作正确的应用程序", "ledger_please_enable_bluetooth": "请启用蓝牙来检测您的分类帐", "light_theme": "艳丽", + "litecoin_enable_mweb_sync": "启用MWEB同步", + "litecoin_mweb": "Litecoin Mweb", + "litecoin_what_is_mweb": "什么是MWEB?", "load_more": "装载更多", "loading_your_wallet": "加载您的钱包", "login": "登录", From 8e6901118a7688518d99d0491bfc06465eda7b8e Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 10:08:19 -0700 Subject: [PATCH 090/100] updates --- assets/images/mweb_logo.png | Bin 0 -> 19409 bytes cw_bitcoin/lib/litecoin_wallet.dart | 7 ++- cw_bitcoin/lib/litecoin_wallet_service.dart | 44 ++++++++------- lib/bitcoin/cw_bitcoin.dart | 6 +-- lib/di.dart | 31 ++++++++--- lib/entities/preferences_key.dart | 1 + lib/router.dart | 5 ++ lib/routes.dart | 1 + .../screens/dashboard/pages/balance_page.dart | 11 ++-- .../dashboard/widgets/menu_widget.dart | 5 ++ .../desktop_settings_page.dart | 5 ++ lib/src/screens/settings/mweb_settings.dart | 51 ++++++++++++++++++ lib/src/widgets/dashboard_card_widget.dart | 2 +- lib/src/widgets/setting_actions.dart | 11 ++++ lib/store/settings_store.dart | 13 +++++ .../dashboard/dashboard_view_model.dart | 29 ++++++---- .../settings/mweb_settings_view_model.dart | 32 +++++++++++ res/values/strings_ar.arb | 4 ++ res/values/strings_bg.arb | 4 ++ res/values/strings_cs.arb | 4 ++ res/values/strings_de.arb | 4 ++ res/values/strings_en.arb | 4 ++ res/values/strings_es.arb | 4 ++ res/values/strings_fr.arb | 4 ++ res/values/strings_ha.arb | 4 ++ res/values/strings_hi.arb | 4 ++ res/values/strings_hr.arb | 4 ++ res/values/strings_id.arb | 4 ++ res/values/strings_it.arb | 4 ++ res/values/strings_ja.arb | 4 ++ res/values/strings_ko.arb | 4 ++ res/values/strings_my.arb | 4 ++ res/values/strings_nl.arb | 4 ++ res/values/strings_pl.arb | 4 ++ res/values/strings_pt.arb | 4 ++ res/values/strings_ru.arb | 4 ++ res/values/strings_th.arb | 4 ++ res/values/strings_tl.arb | 4 ++ res/values/strings_tr.arb | 4 ++ res/values/strings_uk.arb | 4 ++ res/values/strings_ur.arb | 4 ++ res/values/strings_yo.arb | 4 ++ res/values/strings_zh.arb | 4 ++ 43 files changed, 308 insertions(+), 50 deletions(-) create mode 100644 assets/images/mweb_logo.png create mode 100644 lib/src/screens/settings/mweb_settings.dart create mode 100644 lib/view_model/settings/mweb_settings_view_model.dart diff --git a/assets/images/mweb_logo.png b/assets/images/mweb_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..92317203ed66a67f86f98e22be61bf2102eb44e0 GIT binary patch literal 19409 zcmXtAbzGD0*B+%HBGMo!DKS!qh;%942%|eTQb0gL!XQNHkP_*RJp(C+q_ou72q{5e zfFdpUKKT8;fAC?u^W5jIbFOpUd!?_dL3QQE6$k`E_3(kJAp}AUT>Oz=0#jN@7Pi2D z6y6Wa{U8uVzKcJihlaen5C|9Kp{kPclXrwUEnl{2l<2p{<=_2uJ{3B_q#raCO9Ly^ zo{*AiaJiDk+<5khhMesg#`F$Z{HIUk+Km-HuV~iQ4J*31Uuwun`YHT6<-VRWQrE5O zEO&0%e1Ho-T~1WE5mA?=y;hT59K_KPcDlAHTe9@+nbBVHno+>`?V+`h^Bpst@u%je zBY8W24y)7RX3|5>10j=ih-Vv>F_Y;($5m~u$gu4yZlcxG%DY0>!(WAaM%(7YBBA;z z{g2O|LO;x(?WI;UC!6>m4Ol?VPu6@TS~eDUNY-O)DIHy)t<}Vv&%R|%2pv@`@5GE! zt~{tEOUl$+*J-DDv0E0Nyt!gyJ2l`^zE&i(HogCGn72URl{$oj+*bJ( zaR^vEK}~N22B0W#jwPlSEqNfekL&&IYrnO5OH?hiO_D0eB;vQp6 zi0{786?gN9P6|N3l)RyWtD+ln5ZK1(3ssCF2Gg$FF<$bcyLqVH7voC~ypgeEOrrBG zxE=|0beGKGq-c1P16VC;>xr&Btc{89iZ`eHlz$c3CwIq+=oi?Hy@bmF)>ZTtuEf>k zZXnA|b_^r!qs?qN;FQUD6=uPpixT>@OA2S!N4y(77;g8QF9^0^u(geZ{uU{3&@jlE zbcZbau^KVx>X<}?Q2?WKljh$z%9}=cFpT#ar({Fk-=n;5Z_+I{7DqWpgD4=8nEBR~ zA!>U$eXQ8_J@FPOlU&#A3=nvbut&c2gC9pf+$i|TlN63k3LwdM!O`1lL_!g_hzUM_ zT%{q;cLj0uqVJGTvTP~!9&P5`nS(r7m9(HAwj_FzVM|H<(#Hzve^BK|>bS&jK~HMg z^um@hP&Hq!j_Yb4b_h4s#+apa9dVPqQSq6lLsDeK!1|p?=u@gj4SqIA44pSin|iI& zAgM*ZGO=hOKUVTyU0G+DBBinRC#MurxVj28RQw``8*3={jPaYtCuDDQ&OL?+?b;%X zlt3H92d8(@K;Eqa)r2@%Yfg~5_2U5Feo zwv)`sQ!Nes9!rcMIFQbc=(6+CAq2C=tROjA+4+k7r0`frJ7z^M9L1Gh#tZ{)Oxk-@ zPC{9X9i*-0bwtJAG<$M@-T|ED-bRwpM#$%5dLpyG&>1>Jzcr%PyEu`ND-*xoCg(%Z zmuag7*mEH90lQNs#C?&+Xh)q|J}uleNPV%~GZ6~Fkq>tbCqxi7yTs*zmN-3_aN=f) zgqpu}yNrxEa4{pzzcoFU>%Ch4Hp^Dax30f46CjCrANul2^$`05^;$lbxX+Ms2OpM) z6acTC53}N=yxJ#|HYfc^XjH5aA+ySxs63E$@nE*d`4*UDP(;SeHwnMQgZ(ONVi7TB z)aC>(*EYiy9( zebbD7ys@JzV3nc0c3EfKk^%)mEUJy)?$9gVqZ3FFEydQOj})_Hk|A4*AKkJ(ui78X zJajaRgv!Q|*(3M<3cJumT$@#2AY>-@i;}_vBr}H-vx;R1V;M8FO&XB$g zD)ysiwgWcr^ME<;#e@c8MIuk0S?i`pw}TPpuflM`1&k4qjRfBgR*Ih3Sg3jskv>T% znjCN_$zzO&U2kER!32B&E9V6725hYnN0c{)qA!1+d!5XIr?JS^6P_T}&vvO}s0l*z z@M2#NI^I)wsekJJujCmcAt3&xX*A8Nmg$9_Fb)WFs-Dg`o4GOUS|s7YBi>7&URr4^ zRC&W?Fo18}gGwsYPEVL9g!xX~7j?zjsz=clXN{2E61=O{-9Y&b$DfairUGc`+=;&F z5xSzovorL?Fo3eha;tUxnI+_toVIppiY+{|iL$L$P84!~V+}lzztc~uB_h@0_lP5^ zta@Or{QSm>-2g4PZ&XXkBC6Wl@*ej@dR!5}nsRXz4PMA-t5L%}+a5qXP|Q%^P2)ZU z7CX!Ihu_5j`py%?iel0DNH?`xZ}cf4Z9$Akip#n_L8SOjauj5=1vY!`$@AhDIY36^ zPGqa!L4kInK~UPoSwbEtH$h)^JCHrXb5DEPL*De9uG%}7)DMEnw zz(&n!Jf#G%&YBmg6lfMYgcL8>>r^yen*zuLo6RThL_R0Q$AcZ}(=WoLB9Z*j7LZ#> z-q|!57>P0zLa);_6is!5Hd}j5%t1qx5sV5XGEmd~_o>FBjO^ewYP#6N>KD8Y3?APY{2)^{#ix`w}nq+Kx6T)dp(Q zMKl_3M*)z44I)cJWwUIUAw5V2Pn*|gs#n!&*&jsX0t=6vh$2#5#?p3YS$^c%%0_^5 zAp*AL+7|nz${R0*0bC@0El-z+KZJY}tg#P}lcPn1+|C@P#svjTo<~AH!RA#xxaDXN zA+!}-bD2bt&3T>TJtLP7oG{mQrOL3@dJ|E|Zp7h0xAs2ZAOx_-_3ZkAV|}99vcFwG zHvz=}A!{&O2n6QsO!C5$0$`7QzAHWXM;W4+m&-yj1?KZOPZC39uY1b!Vgo)^T0m^6 zk(7loj_lVFhxBa{JpmBN=@XBu*z@>PoXUOJ5rUW)GD_p{=^)>}C65y9YJJp;pKnXI zcFsUsQGALLvYyrLC`*e7w7n?ElS~`Hi}iE^PtlAB2;}!xYWEL39Re+$Ic(E2Y!88i zhA`>9nt4-3&KlcG#B}0L6!Gk=(Q;7q-S3hztmn3IDH(+J?zC463nqYsF%CQe)NFY7s(54)7?EnSL0Y@FhXOR{X5=d>(=cwP<`GOio$gSuwysR-I{KLiP zXW(a2{GAKQBG=JvDz#J>ABlgUtqOY&;5G!4@!MjOciA8j$TN@TjGV-?$};Tv4{(J* zu1o>Et!$@cY@xL|+G~WV$6iu*=^+tTp#(Jh=?trXZGtRos31g=iMY=thUU_0l3Z_GBOcs*FpT|})l0qVA8hMS`5hyMQXvIe|3PI@D=IDj_sC% zE)lODWCZu`9^qsN2cwt4Kh!eMj&7X$x` zt~sT3_B<}VAYNI8ssBYO*l@Ro{a99!3+6F9dppAOr~Nm1(8R^cDHPRHyr0v0-E-#i zs;`7>=?_v<282U&w~LoAmr$+Q&+c%Pj+leZl9W4&dENgHdCUGL^gcQI@wfC<`v8l9 zSCvpK(Bj@Y@J&Br(W;l}kJ>|$zQT_owFf-wQ$?YMyY(Y#_d1~=8)tX!eQ=LN{$@SP z1*ZkHCT!N%WQ-kKuHP&=yYImX?-2SceWOjO-0q@qs|kNEL!J!oSdc zN^P^#?oaE`S|4_F$X6mnW}=_gbH<3F0a-x~sOKr}<1*ur6=UYm)ET5&pPXSo^vk#9 zlxX;Ycbny)b$S?a95pbO7qF^x)#PKmc*yr(VRU&Ry)?d|2dkT$f}UMqN&P>{^*17` z$Q<Oa$wi@S5RTj(A(E$r8`j>^k8gM-oS3M$FE@?%cW*SYSR zq!s4CKU-fIXmD4=unq&`=rC6#a-wsUoyzS6mCGiZGZd5MSpOtN7%Zg3Bo|QD#{rj( zSNbXHF!GdeNiJ?=VEO20rp!Bx0QY_M5vc4Fo2AY-UsU(k7*Go+WHP8 zs=Wso=hawUX=2^fKy!ne3OR-5$~$YVrcpWCpoTu$S6EQaxR-FSfjeqM zbnI@|nvbl;pCRl>cdoIgpw(2(;;b-Gj(U>^D#2&i67bGTxP7%SQ7-Y;4daapU!aFe*Sv$RvRnvb<;4g zfTr3jOuxM0%KoqH)PyxzgXQn-%hGuYPBvHuP7TyEfKC9~CVxlrEr-ATl*EwM;rj~I&wdZkW0b@A@S#PMH4SCum*oiInopZD@Xku z`lfF?=xMKx^v8%W%Cc3SA!w}+eOQ#xXU)u?U*5*Jcy&UHGJB1(MOGLQCLTAdHcKa* zC#6t{N3`Q;{~Brw9DAhDnv>zziwe&*NsZyE5|rnOjK5H4syH zi@7B7b<4oU?FTmf6O?wnJA>#)rLOpAmfOR%)rf?4wYKq%y2X19j~=)eJe0OhF>YhHn6RWm zUcK*^^I~mPE-_D(fCps8reNX-i3u*uDqX+MtMbE6q#MPG^X{}`Y%Jj|)9tHE{F8EmXZJa`BM183t>)o5PVz&R}`Nb{2 zb6Lvu!gP7j-e-G#&k{Y5AU3xk*ZlCkRoPPpDAx?_KcSM9D6>?|z|~t2PN)_$7rjxB zw`H{MF1}{2r}J-}CB>mtUZ39(!S;ylJC}A)t8c)j+i}>(N~J@e8#1!EjwW=P_?V*F zuDz(dSciXgeJ#&8+Hzo4eTm{Z5$!?LOE_U>G`q_KQ zf2*uaj&ryJm5~zj6zEAh&xcjHw&2;)mc2F=d0h^rjFgGSpHQGb z{C0k+=Gb-_9@mo}DBY~1^ioVu4z)_-xhXK4WVq7?s2C%lH=@Fv_W7JXnEm{(&ezjT z&zH6pR75A^qRcIQtABc~Nhu?b|H~Xb<*v7m0#!QNf@sgM|MoZN(hFtj|MI-CYYQ`h z-N1Czi?^p41Ce#u_xqg*2>*Y%SF5IT`hqC|pHZ*Vlf%z_bIwy8q7zeEO;vf`1Eawq zZTseR;O0Aj2Oh0rGVd11M{ z%)ZWNEtmqRd$|&me)%_doIA30qygWVZcTySr}Jdd6su`y7XlCE(YzHqNyfOG9~JC; zi$EA(4E)Qa)_}VW#xxxId)a}%4JB~muA$RZWh1zF5PIoFr)O}c8$RgY(kQ<>n!N&F zQ@2SW_=3taQ*M%@^NgDcy4jc*Tb0w3&zWa9QkB3+nk`<-Jv&vC;(aO+rD7N2wH#6; zm2(My_ilqn0jG?}#J!)Y&t*Y%q+a*LFQ2Ah3ToSQD*!7q#Qv1Bf+q-vs(taRAbX}k zj@}=TpY5Sr-n{xcitzZJw__;qV{F>Z-Pz;lFqcKc3A zqcCHVlUGSOp_&11!oHpeBkchPgpccz`nYq?hrI!1G&Oq%dqNz2{#5s|XIo8wj5iz| z5Lh<0t(PP66{67G zT~>AH7QWzSB!~o`CdYPvEC+&cC*NJtW2@Yuzi*AHM!z!^`h0H@upW`zK%1b3jQ%pf z6NL-RreEpS*lG<&qlrDp_6}bD~TkF6rIR6=gh&2 zy5pu~lU0#O^@Mpvv@t^PnK!&^5=YV3uv%BV(WgCyF{Dyu3Wy_HXC5~ei*1X&^25XeGGMfG}Ww81muGraD#>D!0X zlgvoVM{~s&t5{LD{ulZYz!> zwpHNM*0PoIGGv^x|Cy)ec)Z@opi~>9sV2rUq1sf{`*3MNbrQx2+PTNc_ztfd2r943 z`MjM-c0)5H-ZsQ&Mg}^QToWS@g}gL`ht$Sdc2@0-ot5J1f31YLCC7rB5Tco?(!_u` zOm1}dC`Otd)L?^EI`a+N7DTO_h0;W3)%CtnidDDq;S)rhX*@LgJ|`Etm>_P7^@J)Z z7{=f~;6IzL5cny%$8J{Ss%SCKn-_D!7lXfzIPTB*&Ee_resr*+ z+=!slY1i5D*Qm22+shbH!23#livoz(@mhqH#|k|?doa^b=_E`o#!)d#3B1X}aH6`W z-kP3C!g5~Vk2>3G<%^_iXhpf{5PLsu7I!c8e{d%Knp{|`RFmY)Q!NMk9V7e@5J`Mg%C7z8A)UOyUTtXiRHp<8 zUB@h~KblBYX5E%z4g;zr7O2*~Jl#3&WDzVu=g}XaM zW=|dW3OK;y@QrDbZHwigJP3w+aN?q}&~1^2F*E)Ilv`GDPIEBh%|l*})X$HKt^j#$ zOZDD6hbt;oda3LF?%+$X}vH5MeOJ0?HC;+;E}XYXHhCwi;rJetCMQIgrU3+WNfn+87#=O2C>YlyXk;#th;vB&-94-(n~y>%OuO`PsB=19afw8VRkvJ2>9qFPkJWe&U|V%3Zg}$u zJ1u_O-v4_OoApwWOuniUk(Y`Mts?=OD_DkO(vncAGYwxqBYJen@WZp${2RDuB_+!w2U3H+njhW7&P#7= z2@}hBoMwf&Wql6^orMB@d|s(2Zoiy$O$A8lTJX1h!K9E;rG;cJ6a$-N^xw@$ ztUIY=C;VIy54o4@{?h_^DUV8IR+8}*Fok-kBHvztn`c7EedJ~$EaRyqy3TKnX-^x( z`Bl5Olo;1oEvCJFFynKov#!Hpj3Ds|Y z5mgoAzzcA=qUW7c!bF%F8rG4{()~8QU}l{LoW?L=D1w?8IdyNrIKqwstysiib>DfB zS>=>ouCbRG+5Q_hrPNep80))SY_G-WY5xUNz=5;RcvG8QdF4_kl)*5C6}=PCXI$sx zYIEx9fJhg-O!jJX-)(En?(&RI7LDh#tghnQHgi;fm!(_qg#4GVxr4EL{oVICPARb6 zATTmW$cTmuy?@YYvz_fgY2{JT*a1pOg1qb$w<*ne8iZ8}zyM$~!G9jrS&1zxzc4ky zDIN5?TrcSfM-s%xwjb!xf}NCNzY3V(uBcuAnpB(PB9kYz$arhEO%CAa9!&+Z4)siM z!BD0KKgxapHV_o3v6vQV)%>Kg@-m!`X1x={IChncItthGoc-3C0p8o4&=}3zBy+k| zLB|z$TR0F_ya)SQgGH#ZPVz2)fM+JaS}oBRh=yNDE?=;5{xV?GfFOf^^xpTWl=^=z zK*hf8yGGuvl{s*X1ILgU_!WDq%QjWH&&}`mwBy9u(=Djg34JNiRTSva1mgv{DZbUU z%{%7Q0L;Y}&WRV3ue!Vp0<12b&@C~O?x~L-w9Byd71SUQ`k=Jv7?`V0Ua&tj0@R>9I*r9e&W1Y87ks31}MwOfj^41 ztAd5(C=bj&*SY0cD|R=$atBKFfQNS?P)#@xeDd zwf0k+UK_BM|IX%_?bUKdj=o9=jg1-fX*jnrlfA&ky((-&?4mABH71VROkZ~|eE&Gb zAS1L-CwjI)Q+TD5upJFQnn!LukvR&S0B5>BjLRkxdMMal0&91~Q|B$~gg8o#Kc5{Z zZ@yzP7=xdqK=10*AASZSiwfD{;1#m3x7_~7>~SfQlSb`{JCoO$g=;BtsR_UQH=M9z zG%(n8#C?*1DM;zNC4R)e8iMJpYn8JxLHuPt&CH+GXO+_vNHrtD*Lqug{?06f4l!tU z+5-I@^Q4EmCmwOT$+1wKvI5|YIN{Cj1sc*K81Ek#h`(zjn$;P59=`V-z~=D(IWzH~ zS|?XCtXIdsUF-$2G6 zp2>q%iOXjT*2tOczjFZ}3-`%ZcWDu!%xiauD$0Kna?1s*S1tUBvC{vdkU_nLXrT(b87^E=*ib~3!=eI`V239?;bFm(4X zsNln;cEh5%haq&k9xXpL<4Xi1y<3iSR4) zwl|rOA)Vn`@3{Vzxg_({ive|=2@@dl+Sf{iQRkTH#@^*g({8b)=iDCudel6) zx&JzY@LchV=qe3jGaYGzyw528ML5BX#EM z+;xpO*4XfL@jFUQb8+5UZb=GG@!i1G)0DLYTR2)|2PQCnT&AC$YU4*OpWxjhwwPwP z@+-x_3t2#l4<0f)6qplew+yd7Y-HNBiYFZiHpu*^%~_|PDH8IRQ8(!|YpaaXpyWS` zHpX<@=T$Ev(qJQ&bI!LdHnF%wUkbqg%eJmjDYQp;5eV*t%6zdm8$8+?f37c%0p$BN zKFr?99M+4*y$2m}I?w)xy5k|n9+U#uDZX?X!SGiE3e#<$DrJnAn1W;&$sVoEC8N@{$(`W&^1q-W5qCD z0u69PBIWAclwMX@jDY^)N6>q}Kt#}Wk3>qU<{R=>hnz|A_nrg>F&#d0PgxrTX=h-%;334 zJ2LfDC`zS3;h!8Qzrva`R;P*sDw&O#Fu>F1_=hs}Cflz62LA(h`i}9g^ukmWvxYef z20)k_dn|v(6u$HMC&Sm&EoEe_ZKZM~a>n;Zm0ZIkM9KyDLGNj`eBX)1-9baU%8!8- zF_5Z-GPNNzfkFv$&-UCoe}h>Bi2(5`RgBTG6u_$HxgAZm_a00f7CJnV~YPH9?1cc}l?P z%i_Rl*Gu~C>mZ7w*@RRvIc++YjQY%y96+-$5U)ZgD2d;vLk#$3pZwJQEpY7pP5!^O zkPAq>=$(;o-aX%pjNSck{D)$>lCL#b5e=;kOG+}W5(x)O5@69-a`aH%^s%ak@{T-c zAAGh{K{koExv=4Ev-c?9au!ujam*NHQ-SIe7M!iNpwre_H^u9n);wxVhlZk--!l}5 zObdpjbAMQoluiG0p%KB^uvd_STi28tU8OU^e)Z1~HIyLfMszLx2dH4TuRG z?^)QC&TkV`hLfpHw_;^9i@E9fXL5Wk(vVV{VEYE4;gLjQWwk3B; ztU_HtJd6_qa817oydir)gy8^Zh=IW~d%X>0$kjL)y})0+Cg_r9pJMivWh48#doN*! zul2#P&7-rdoY)5YFes!%4nNv-L9hP0*6k%kQV8Hu+K#VH*u4!ppO!^Ja zjxN{Q@TLQsIrY(SD?=g?>yQh0llBR_X&h&X5LHO3&`0M zKV+~;3OQz$CRYRT12KmygUmJItXq1t=nf>5pX-n5SBdfF@G@-L&6zo0gwm~vuXj?x z0hl*ixv5xL{xu;Byeij3VC?#f2=^*TQw}vJgI6f;rF{f31Q0!{S@*_2<@7d_Yq~T1 z>5M+KaVBy1W1{}RM-$=Bn7I%22OWYOGd+b(f7ATSZ@mGcPZ;LXyv^?SyNBnYgV<+G zJquk8Ee9|c5dP@xp`BwknpbBZ7;c77Q*`W7Yuz)T*IEU_y3IVNQ7uT65%GJS#q`eH zVC&u|4e5LUTV)&-bN&2rJjSX@2w00*2$~WxF9NApHY}N)5yqfqO;!V}?k`%H`1+mB zw!TK)DB-(2;}5b_%2#FoyYlhXf8NQ+nFn)9Fg0nqU7}h;7yz|RLg)7Dct~|8}P&p_6m^MG}Bpl#?=*cv|BzAgx z#@!)@{9_HzO8e^=S1VJ0vE`PfO}$r?pJ03wzh2f*%9iqltRU~km=3Kv8)v|Z_LODmjLN;>L;B}vNH}0h$^AMasMp``O;F+KOJ>>Pm}08 zHxEa$D&ih%lsb{vL{*iM2Wjm;3)lZb@!q|dZ(@hLJZ+b&AdM=Nz-L2-AG`nMmUj8` zznF@yUC0Fl7Vohn0s7j;rA{gfkyRQ>Glq3!i$FjPck8FX_d{zgE-^?~wLW;c=EqoQ zGq_F<{I4!e~vFf2LvP^iXPF$ArenK7bSt2@n*p2l-o z&OaODlhTzEP4jZ7KpwxEbj`B*oM96))hCvJd*%gxJyl`&o4)2(fcLHoL)=u<0*Yf4 zwQuW@{JizU$FaV)6S6=MQ6ACGox!~lq}X?>DVrh*-ZgVJtQ#^bXQkpXy=6dwq+nD- zu4Sf0e^g~S2dQOpN5WEP3<$b}G~#v5?Zv67I(!IgXa+r^O7pY=to~~{^0Xzcw{o6L zGY#34u!LE?^w*@_1wdkq-B|jfFs74?x2%R!;SLPwLwgW+P7N4!m^Q^Zz%5NIo_l*G zA`3?tOI52Er=D)iy<-%gO7KEnc9Hn`DU0e--MnsvJ>`T@ueUIE`+MeFx1hKLM&0*{ z2cBDNQA(lH>KD0{DLQOVaCMs*C9Vv|Y&@-v?Bb0(vGqd0@h_rlbA5udIH7>Rav;-< zgaF!5Bm^&FC`BkbV6o)_vJgS^X_Sj8RsdxNqKi{hn_tpt&REXwbug(L zP$}=dU#;L!dj)-Y6KCOGgl;lWfDY!Ig7$1H98eZ(-9Ghnc-#@>U-oS$}|xEw3V z7-iyFC~cr;A7~9yp&aS>-)9BU+tofI$iy$K3RzHvF}Mp3Qa50BDzG=WaucU1Ja3c2 zdp;a~L6VA3;4tYQL@2ve&!AyNQ(r&cLz#mxqJK}p0`q@l0mbf<|>`LDU zaFuIo9&vugF5?dw5TSa?7KiPZcc=iu(kt*v@E~(yv=(ru8grpsXr;Ao@PD)PX({9v zZj(5v8O^?4z=7|XbA=gF-Q;{a$b9%QJ}=SZ@6%0Vj7@Hp4moqy{;fB|rd8w#u~slc z6P{}O-KH>%-CXs)zx)$FmWu7fC5cLpE-X8*#o%PS#=`5f6%}GMHOft%5i$4b`_0uM zwNtg9q9w@&b+FM~Kp}h3AYl~0-TL@`<}hACuko6MeHn-~y-~VB%^23mP0zKMyN;uI zK6db<%~N_A>*N;!hF)*PWr$Lqo!QxxU4dLteA5%9=gM&vkl6{8Q!c~N3_GCNPWF77 zGuq|@f2py)yof;mUg})`a0|kl^@wEaQ617aqWHv+`ctg3F zkAV;Fk5=SFRh1(-3`KSC4*gIyUbz?SC@U~4X;>CXm?EfrvgWY7J4FtxKDaZ?Z+&0r za84Filow#s3FS!YTGPQ=N-&&=W-Zicqr8fOqUGTomp!FUC?p1ZE6$T1QK*@&7S3hU z24c?rJvh|`R5Kv^z#JL=60ArBh8n;KiP*ym05mANhK6CxeFqGn(^P^7-rp#BkD;-jq2H_UBRv_WGr&;TRZV zcI}gGeJKu%(Fs!EgZO5Z1&lokKt*_ z&WPB2t2GJCc}o9yI;Tv5uFb1ZT^nf;~N%S}a|v!`zQjxqx_Dv=|U3 zhyn#Tx?|fA+zrTveOS6x6&UD!^*>lEo1TtlDfDD;Khk>Zcl-Putc7A)%{zWNHe^GG zR5*>No$LuHm#ouJ&cy<5saL!OpV@L}KngP^6F?Yh2zKN6W^y;3{5tr?9!3}VyqiH1L!^}o-qiP4e$?XY&| z-YF@ZvhNpcz_DVk29yu-teD|APoX zl*kBt9dlA=2Fwa$)!)NPZhMXRdOUQE#)pDX<8Aa$nWQhQ(~#h|qS)tK%5(~@fxNPV zghuiU#Dk^9aX}7LjI^}GL$`~tZz$k&cR+%a;$LRNcyvcdKn!td*F3a37%j}{djj3;9cJv;<3rn1TgxvMxYJURN5){`a5 zaT51t)kv&VQ9$o+OF0&Xqlx_`^s_Tg*5?P4vq73>8fJHGf-M9x2) z;>Mh_K6L0nitfs^xlbb)*=H|!P||#WhjkL#qv7s;A1w#ldCzk|;Wb?J-nLZ!Ykd3w zpS#8u7L9KsM^C)Bg61@$VX)?)?+j!)n~pa+HK#E!e7g?EZ|*}K*nfq%UTBtq9F9cY zxc4AG!F8i!``1E0*M+H38S)|KqMej2ka46Hye9`9j4++Oo(l5dF}L&ekM{USs-~5J z&7Sih)!_m%`VYpDl(|%DP=X}a(Yu)D=CGk4LuH=XZLOxF+Zts|@!hB0j2}l6hvAQ8 zL3-wE@0tCMbj+W$y${jqO>^nqT7pSvu4o64r6Qffq;gvNZ&ZOdXH7xQOdtqJi3E8V zw`z!hOcl$6xB z_M%x>pc;xXcv?$$tHK`ds!ZMdm%~&D87Ek=f*ShF1X*&O)l!paYA^~rLtqGc2lCXir zM_>rm`LlHtQUr1|v9^H82OTpC^i{zMeb#%N%+yayW^GxuHA>_DHF>jA8CimN+rD^& z19G>gCSS}y-3ZxuxPyTaKhFgG|HD$y%kPwtqw<%}HqPW=s@=d(JA(@vOD8lmh;wHM z0m9n>+@C4U*n<6}y6$!qL{{Y%-TM#av3OlD6)Ca4z_oL#1d^c~O+d%X%&_U~4W?kX z7DQ-vjcHEI^lGW(?{4t@Bd>&P-Ea{e&Lrhd?Dh8eQkQU$R#{aJlnUc)Bie zEr2;{e*fb*)=C8=CG~B9>IT^|Aq(;A&s`^z*4GZ}o@iD#5w>j*; z4sF0=$!`4i-qmX^+BnVQ3$*5$Ai4NUd_mq6wbjMxs&TS`RV28 z9M@Af8<+dX-zq>#-Tdeuke&YN5wD>AIU!Nu{DE&6NCR)t8ufa3?rYc|occR<`nz?t z-*!!_9%-&rVoBhyo3QRai&Q1D7#Ad$FN4Rp2DOgyu9D$pQ` zV|m&|C{b1>_=Tdxn;fAf{X)PHS|(DxIzI6WC{e{MY(XgGM4WSa9`Y{58_6?aonb~9 z^n@Y*|lN2qBfEMM{*RS4DqBM0{Yte;<+E zfZcD^3cmi~y)V3jTNo|uL%<7YiT%3!p4YQb89s5FbpiUO3yBy`+LBW6?*<-9EXN~C z!Kv;0LeLB$EWgE_zBTn$=t?(sD>!&i8pEq=#3Y$yhF}exDxH0;G?t1_r7`hVP)`lm zWl1^xB_H$Q+^qHt@H*V-r;fgW3{7ajut%ObK{W!+!ls)ZOVyZgDDdF`G(FjiE_R#}=-5`qxV(6GGcMA}c!QtSTzVi1GRcySYQbf7tPU=#)Wrb404dp* zZ?cW}4vi3|-qr?_mZdSj>r1C#ESKOWa8poD2A_mO2Evt!{L|oX`628DJDYkjCcXuX zUk8dU@F4k>)db!Sis(hZ7+^&@H|?P?B6~8}g2*bSsy3rJsCKVo=>c`kOBffM1YQ{Lc)Obdv>LkHGwbnKIN-01pl< zq;}F{fF~ON^GV?yJIf^t8%Mx{b+@E4?sc;nA{!gRzRffHS|g#_nTB8=@aThP!*5*&Ur6qnWX{R#A z?#fTaIo70rj_a3&gSTsfe8!%rsmD7=W3NPy({6YA=^vVqSLVz%6TQty! zBe*udfZ&C6UV{Q-x|1f@%x|NIc_rP(i0RURx>vKpXl1i-)3^b|)*BiVi(6lv+|(kM zB9SSdt3+JrWM05AKsYkuq-F-~hN7chqUf%fdZ6eN3*WW0zqqiRD4U<-w={~4KUkF> zHd}>?eY;j7H(jUyD-bEI|ACrMv=1tYRX6|R01`M^G+D^h!*jd8GZTZ{veDcwde(7; zd~I|QBaK!S7Ut6lKI*ylg=_ZfES>lMw*Zje!l}kd(8d$d6Fx)_F>f2q8b|vBx127U zYiqsCNec@kYS@*K?;83vXF!Uhqw~nWLU>YR?{%V8jjaZ5we^3~zoj9X3Qt{r+8U?k z%oHGj*JX8eAYJxSh8h>HvrEQRV_`xzTcU2O7q4r2i-}iUQ2s0CWr<5C^k@COKMUs| z8^6G(H4LFqHcxAj=YR6s#9806^HQQg&Bp2LN9)mekASQB{Y!60C1&G_Gu@|F!EjUX z9TKrOd4mO2zUQ9nPZ0|S3{dwb{_N@v zPb8&PYq^SN;|(@-|EXkg<`?go;;Sn7u`ptiQx@LY9^ie)M^lqydfEP*^b>M1Mx$yA zDnv14dC$lVz9#ecDS*M5nx52lBGyJ6 zs4lm~KSBprgGdqOSqBvIUR1}bz6@<f9A2ru28Tc>JuZG1z<6L-Zi9?VDjm$r2jUE!YLSCLku1Ssr4Y$PRz+1O zT}BeYb|y9L4>>KB!Db<~(qT6yI2mw-M`PDW6knHTn1|%KMm=vi~?IBWBUTjQjACNQ z8_kwy+Nq(a&2lfbM`KA4Nc^KoD7mnM>;JwP?1SG5DF6#v@b%UWHOkH}t`;<=jl2*D zk7O)kbgBf3yVCU`1kz)B`=TJ_e+8SYMV)&cT1Fkf+pjKR*8ifX(H92t1sqPbR4;VK zXPte(7UnX0={Ck#=9cRG!8a6b*~id49%2Y~;NsyF^QIu1UpIh!c-JHAyU|7c+8TVF zSQ4jyEtQ7EG8H7>Q3KT8UPjHg^(fF|JC!_WVTI&B;RK!8W31TndK7ZcrKwGv_$uU< z@##v?vGCxtv%d{i2m zW!v%$d_BPjq6wcyFXCBJE@y6$gy&15KSF6PQ$i+|e8DTYB`cc-fxaH7#Th4K3o1Z5 zm)Wa#sg51nN(5QY?6#NHubUfs*6j#UOcY_nKeKI#LUlU^@RLA3I@e)elj7OCtx}vl zA^BQ)V0Mme%ZsM$dMOsjD4A>=p?|6F8fbFCzO@UVGZ8@y-SDh7V&LPO>QCFoh!!8V zKy&c-F794;KNP?Qa4{xlrd+J0HVX2&>j+tULvVJYn!zt)4){9I95eL{uh~c^hMeqz zmV>#-)J+EbYmjXdckou(q=OzZiqa|oLL!mW@kAsL4j*e&0h=iW27bxtG0V6ou)*+)TMrUY^D!7VL*g&q5e2%^5qR1k1i2p|DL8t}(6n)RE( zr?)c^cD9j7Vj9oCQXg&DAU!IjI*P)|#AEk810^A)$@t*M5sm@yJ+~Lhc1vB}Wyq}v{cIKn{9xCUH;E1k)Q~qa1u&IJC4j37R10n z7!Kn2uyiQHHoJ}-UCakj>r%A24Fw8}`O z!8+(gH>aNAtfxVc2%@L)`=a#9-GI0ds6g@pKv4*Y0V91ET5!$!K7$=0gFMzQg{H(J z3qZTa1iuIVjtJpj>Uxwu9Na3MWnjxL^ZysW20-~o%O8bOu`OIwZePZ#O#omwLTv?9 zY%4LD&Ywr+_SM+W1psWJZRtB)E@l@_TKQCLD-rYc+Me4_V?P%Fu%X+SQ7hC|LdCX1 zlj*#Dl=5!u=K%m7(YEj%MN1!*5Nsth#?c{^kLhZPy*pAd>IzMch=FFlp04ijW z*hs0^j#Ts-SyTogKLjYvtI$96DxG3Bu*z%~!bbb)63Fh4b zklKe>69V@VYC|L8e;MJ{!ZH9JV}Tf*6Kv_lWIAuh zv;6Ap0&tJZXgyG+rBi|}y@`coXLc+DAhcE5ljUM|>V(h-!Is_`3kHBOUJOC7We}>j z0HdW!%U}ds2BmrnFe<9F3`($NaH_We)l;P<3Bi_RVsgTXxwimS`4n?$RcRTVU`tZw z-U3v^Tw0P7Y{|l8IzMWf=2Z0-phzF0^}t+Ok`ipm#$-Bw9^v+#s{&Aj>u5W;aJk5V zU`uu;)A>OJT#L&9^3=8+oGur$6W52F2)1M^MuQz|PXNe@#bIPiuqA7;xU8+-zICMl zlDdh-VcHJJmS9T}v?l;0qwRoV2(}c(>|20AeT-=b+72j!U`uggdO{m+e|Gf%dSTiD zMG|Z&mdzaiI(HKl+B2t?5(%~xFZyrW58)TCCcsW^V;#e(O%^DbU`sXZUIwreeY8|V zu%#-|e|v2HEx?m5V|Ua7%`I$7!AReQHco(Cq{$;BOuT+I)W{uB;W!S6KEMN!In`IaDj>lw2Yo$ z3j!`sHh~s`U<+#|)A{R|foqBakUN1E?htI@9>#?kHF9NWzd=+I5{_n$8pvoB68Y)7z#J?X;*I&c~72Io#D>`t(Sjzk~s zVN_f6;Q~)-+YQbl&}uh;4hXi;gJ@GYGA<0b$9442YBzw63AWIqsN${`o2lZ$c(v8p zbJGo=UxFCCMMJQVFX-raiJX7QJpc@rT^vyy61Y0PCkPCxbjp#bJBN&uHu!RB%xzL+co56)J3*`}Pp>S&<_rSt4 z*omH5sDxk(6XJS*h0l(x3g8y)v~~tt@>pz>;eY$5^P~4qGe${ z=&I&YyX&=n*$Y=2>L=L3NJTYw6?9c|sliQDWL0Hhqy$@F)th^;_9N(0g4I1Y4jJcdELt<44%#A(s(kuT*1!t_Ze3Z|;QMm+{Z~b;o7B z65IayS#K=R7r_=7T(mK~j5Xf-R7Zn63MKt(D=qg=*Qk ziQw8+V7~~kKu!c(AbV>q4KMHcQR(g8W&9i0!|N9<1VHWtTcCKcUGU4bYHt%Q4vjX4 z@2uJdnpId8T!EShw!mmaZ}0Q?eb@ha6^xs;|KGC$&dcT6KYv*Zt<73+ZL|siV? initialRegularAddressIndex, Map? initialChangeAddressIndex, int? initialMwebHeight, + bool? alwaysScan, }) : mwebHd = bitcoin.HDWallet.fromSeed(seedBytes, network: litecoinNetwork).derivePath("m/1000'"), super( @@ -65,6 +66,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { seedBytes: seedBytes, currency: CryptoCurrency.ltc, ) { + mwebEnabled = alwaysScan ?? false; walletAddresses = LitecoinWalletAddresses( walletInfo, initialAddresses: initialAddresses, @@ -89,7 +91,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { StreamSubscription? _utxoStream; int mwebUtxosHeight = 0; late RpcClient _stub; - late bool mwebEnabled = true; + late bool mwebEnabled; static Future create( {required String mnemonic, @@ -135,6 +137,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box unspentCoinsInfo, required String password, + required bool alwaysScan, }) async { final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, LitecoinNetwork.mainnet); @@ -149,6 +152,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialRegularAddressIndex: snp.regularAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex, addressPageType: snp.addressPageType, + alwaysScan: alwaysScan, ); } @@ -757,5 +761,4 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { mwebEnabled = enabled; } - bool get isMwebEnabled => mwebEnabled; } diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index a41920dcd..12013fb63 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -19,10 +19,11 @@ class LitecoinWalletService extends WalletService< BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials, BitcoinNewWalletCredentials> { - LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); + LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan); final Box walletInfoSource; final Box unspentCoinsInfoSource; + final bool alwaysScan; @override WalletType getType() => WalletType.litecoin; @@ -30,11 +31,12 @@ class LitecoinWalletService extends WalletService< @override Future create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async { final wallet = await LitecoinWalletBase.create( - mnemonic: await generateElectrumMnemonic(), - password: credentials.password!, - passphrase: credentials.passphrase, - walletInfo: credentials.walletInfo!, - unspentCoinsInfo: unspentCoinsInfoSource); + mnemonic: await generateElectrumMnemonic(), + password: credentials.password!, + passphrase: credentials.passphrase, + walletInfo: credentials.walletInfo!, + unspentCoinsInfo: unspentCoinsInfoSource, + ); await wallet.save(); await wallet.init(); @@ -52,20 +54,24 @@ class LitecoinWalletService extends WalletService< try { final wallet = await LitecoinWalletBase.open( - password: password, - name: name, - walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + password: password, + name: name, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + alwaysScan: alwaysScan, + ); await wallet.init(); saveBackup(name); return wallet; } catch (_) { await restoreWalletFilesFromBackup(name); final wallet = await LitecoinWalletBase.open( - password: password, - name: name, - walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + password: password, + name: name, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + alwaysScan: alwaysScan, + ); await wallet.init(); return wallet; } @@ -93,10 +99,12 @@ class LitecoinWalletService extends WalletService< final currentWalletInfo = walletInfoSource.values .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!; final currentWallet = await LitecoinWalletBase.open( - password: password, - name: currentName, - walletInfo: currentWalletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + password: password, + name: currentName, + walletInfo: currentWalletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + alwaysScan: alwaysScan, + ); await currentWallet.renameWalletFiles(newName); await saveBackup(newName); diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 167a585c4..210120ba0 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -208,8 +208,8 @@ class CWBitcoin extends Bitcoin { } WalletService createLitecoinWalletService( - Box walletInfoSource, Box unspentCoinSource) { - return LitecoinWalletService(walletInfoSource, unspentCoinSource); + Box walletInfoSource, Box unspentCoinSource, bool alwaysScan) { + return LitecoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan); } @override @@ -592,6 +592,6 @@ class CWBitcoin extends Bitcoin { @override bool getMwebEnabled(Object wallet) { final litecoinWallet = wallet as LitecoinWallet; - return litecoinWallet.isMwebEnabled; + return litecoinWallet.mwebEnabled; } } diff --git a/lib/di.dart b/lib/di.dart index 1462370fc..8f617fe41 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -93,6 +93,7 @@ import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settin import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart'; import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart'; +import 'package:cake_wallet/src/screens/settings/mweb_settings.dart'; import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; import 'package:cake_wallet/src/screens/settings/security_backup_page.dart'; @@ -139,6 +140,7 @@ import 'package:cake_wallet/view_model/seed_type_view_model.dart'; import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart'; import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart'; import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/mweb_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart'; @@ -559,7 +561,8 @@ Future setup({ getIt.registerFactory( () => Modify2FAPage(setup2FAViewModel: getIt.get())); - getIt.registerFactory(() => DesktopSettingsPage(getIt.get())); + getIt.registerFactory( + () => DesktopSettingsPage(getIt.get())); getIt.registerFactoryParam( (pageOption, _) => ReceiveOptionViewModel(getIt.get().wallet!, pageOption)); @@ -678,7 +681,9 @@ Future setup({ getIt.registerFactory(() { final wallet = getIt.get().wallet!; - if (wallet.type == WalletType.monero || wallet.type == WalletType.wownero || wallet.type == WalletType.haven) { + if (wallet.type == WalletType.monero || + wallet.type == WalletType.wownero || + wallet.type == WalletType.haven) { return MoneroAccountListViewModel(wallet); } throw Exception( @@ -738,6 +743,9 @@ Future setup({ getIt.registerFactory(() => SilentPaymentsSettingsViewModel(getIt.get(), getIt.get().wallet!)); + getIt.registerFactory( + () => MwebSettingsViewModel(getIt.get(), getIt.get().wallet!)); + getIt.registerFactory(() { return PrivacySettingsViewModel(getIt.get(), getIt.get().wallet!); }); @@ -802,6 +810,8 @@ Future setup({ getIt.registerFactory( () => SilentPaymentsSettingsPage(getIt.get())); + getIt.registerFactory(() => MwebSettingsPage(getIt.get())); + getIt.registerFactory(() => OtherSettingsPage(getIt.get())); getIt.registerFactory(() => NanoChangeRepPage( @@ -895,7 +905,11 @@ Future setup({ getIt.get().silentPaymentsAlwaysScan, ); case WalletType.litecoin: - return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource); + return bitcoin!.createLitecoinWalletService( + _walletInfoSource, + _unspentCoinsInfoSource, + getIt.get().mwebAlwaysScan, + ); case WalletType.ethereum: return ethereum!.createEthereumWalletService(_walletInfoSource); case WalletType.bitcoinCash: @@ -1089,7 +1103,8 @@ Future setup({ getIt.registerFactory( () => CakePayService(getIt.get(), getIt.get())); - getIt.registerFactory(() => CakePayCardsListViewModel(cakePayService: getIt.get())); + getIt.registerFactory( + () => CakePayCardsListViewModel(cakePayService: getIt.get())); getIt.registerFactory(() => CakePayAuthViewModel(cakePayService: getIt.get())); @@ -1121,12 +1136,12 @@ Future setup({ getIt.registerFactoryParam, void>((List args, _) { final vendor = args.first as CakePayVendor; - return CakePayBuyCardPage(getIt.get(param1: vendor), - getIt.get()); + return CakePayBuyCardPage( + getIt.get(param1: vendor), getIt.get()); }); - getIt.registerFactoryParam, void>( - (List args, _) { + getIt + .registerFactoryParam, void>((List args, _) { final paymentCredential = args.first as PaymentCredential; final card = args[1] as CakePayCard; return CakePayBuyCardDetailPage( diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 2c669c3bd..5511ba84a 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -50,6 +50,7 @@ class PreferencesKey { static const silentPaymentsAlwaysScan = 'silentPaymentsAlwaysScan'; static const mwebCardDisplay = 'mwebCardDisplay'; static const mwebEnabled = 'mwebEnabled'; + static const mwebAlwaysScan = 'mwebAlwaysScan'; static const shouldShowReceiveWarning = 'should_show_receive_warning'; static const shouldShowYatPopup = 'should_show_yat_popup'; static const shouldShowRepWarning = 'should_show_rep_warning'; diff --git a/lib/router.dart b/lib/router.dart index c09664cef..4094b4d56 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -63,6 +63,7 @@ import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settin import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart'; import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart'; +import 'package:cake_wallet/src/screens/settings/mweb_settings.dart'; import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; import 'package:cake_wallet/src/screens/settings/security_backup_page.dart'; @@ -363,6 +364,10 @@ Route createRoute(RouteSettings settings) { return CupertinoPageRoute( fullscreenDialog: true, builder: (_) => getIt.get()); + case Routes.mwebSettings: + return CupertinoPageRoute( + fullscreenDialog: true, builder: (_) => getIt.get()); + case Routes.connectionSync: return CupertinoPageRoute( fullscreenDialog: true, builder: (_) => getIt.get()); diff --git a/lib/routes.dart b/lib/routes.dart index 78a93bee7..ec8a8f338 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -73,6 +73,7 @@ class Routes { static const cakePayAccountPage = '/cake_pay_account_page'; static const webViewPage = '/web_view_page'; static const silentPaymentsSettings = '/silent_payments_settings'; + static const mwebSettings = '/mweb_settings'; static const connectionSync = '/connection_sync_page'; static const securityBackupPage = '/security_and_backup_page'; static const privacyPage = '/privacy_page'; diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index c190a3bde..3c516e02d 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -333,7 +333,7 @@ class CryptoBalanceWidget extends StatelessWidget { behavior: HitTestBehavior.opaque, onTap: () => launchUrl( Uri.parse( - "https://guides.cakewallet.com/docs/cryptos/bitcoin/#silent-payments"), + "https://guides.cakewallet.com/docs/cryptos/litecoin/#mweb"), mode: LaunchMode.externalApplication, ), child: Row( @@ -373,8 +373,8 @@ class CryptoBalanceWidget extends StatelessWidget { ], ), onTap: () => _toggleMweb(context), - icon: Icon( - Icons.lock, + icon: ImageIcon( + AssetImage('assets/images/mweb_logo.png'), color: Theme.of(context).extension()!.pageTitleTextColor, size: 50, @@ -422,11 +422,8 @@ class CryptoBalanceWidget extends StatelessWidget { return dashboardViewModel.setSilentPaymentsScanning(newValue); } - Future _toggleMweb(BuildContext context) async { - final isMwebEnabled = dashboardViewModel.mwebEnabled; - final newValue = !isMwebEnabled; - return dashboardViewModel.setMwebEnabled(newValue); + return dashboardViewModel.setMwebEnabled(!dashboardViewModel.mwebEnabled); } } diff --git a/lib/src/screens/dashboard/widgets/menu_widget.dart b/lib/src/screens/dashboard/widgets/menu_widget.dart index 78d8abc95..2ebbab978 100644 --- a/lib/src/screens/dashboard/widgets/menu_widget.dart +++ b/lib/src/screens/dashboard/widgets/menu_widget.dart @@ -188,6 +188,11 @@ class MenuWidgetState extends State { return Container(); } + if (!widget.dashboardViewModel.hasMweb && + item.name(context) == S.current.litecoin_mweb_settings) { + return const SizedBox(); + } + final isLastTile = index == itemCount - 1; return SettingActionButton( diff --git a/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart b/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart index 611b2acb7..90729d4c7 100644 --- a/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart +++ b/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart @@ -60,6 +60,11 @@ class _DesktopSettingsPageState extends State { return Container(); } + if (!widget.dashboardViewModel.hasMweb && + item.name(context) == S.of(context).litecoin_mweb_settings) { + return const SizedBox(); + } + final isLastTile = index == itemCount - 1; return SettingActionButton( isLastTile: isLastTile, diff --git a/lib/src/screens/settings/mweb_settings.dart b/lib/src/screens/settings/mweb_settings.dart new file mode 100644 index 000000000..88dc00f7c --- /dev/null +++ b/lib/src/screens/settings/mweb_settings.dart @@ -0,0 +1,51 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; +import 'package:cake_wallet/view_model/settings/mweb_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/silent_payments_settings_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class MwebSettingsPage extends BasePage { + MwebSettingsPage(this._mwebSettingsViewModel); + + @override + String get title => S.current.litecoin_mweb_settings; + + final MwebSettingsViewModel _mwebSettingsViewModel; + + @override + Widget body(BuildContext context) { + return SingleChildScrollView( + child: Observer(builder: (_) { + return Container( + padding: EdgeInsets.only(top: 10), + child: Column( + children: [ + SettingsSwitcherCell( + title: S.current.litecoin_mweb_display_card, + value: _mwebSettingsViewModel.mwebCardDisplay, + onValueChange: (_, bool value) { + _mwebSettingsViewModel.setMwebCardDisplay(value); + }, + ), + SettingsSwitcherCell( + title: S.current.litecoin_mweb_always_scan, + value: _mwebSettingsViewModel.mwebAlwaysScan, + onValueChange: (_, bool value) { + _mwebSettingsViewModel.setMwebAlwaysScan(value); + }, + ), + SettingsCellWithArrow( + title: S.current.litecoin_mweb_scanning, + handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.rescan), + ), + ], + ), + ); + }), + ); + } +} diff --git a/lib/src/widgets/dashboard_card_widget.dart b/lib/src/widgets/dashboard_card_widget.dart index 5a8ca14a4..5bcd4b40c 100644 --- a/lib/src/widgets/dashboard_card_widget.dart +++ b/lib/src/widgets/dashboard_card_widget.dart @@ -22,7 +22,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget { final String subTitle; final Widget? hint; final SvgPicture? svgPicture; - final Icon? icon; + final Widget? icon; final double? customBorder; @override diff --git a/lib/src/widgets/setting_actions.dart b/lib/src/widgets/setting_actions.dart index 272ed57c2..62c4685b3 100644 --- a/lib/src/widgets/setting_actions.dart +++ b/lib/src/widgets/setting_actions.dart @@ -18,6 +18,7 @@ class SettingActions { walletSettingAction, addressBookSettingAction, silentPaymentsSettingAction, + litecoinMwebSettingAction, securityBackupSettingAction, privacySettingAction, displaySettingAction, @@ -30,6 +31,7 @@ class SettingActions { walletSettingAction, addressBookSettingAction, silentPaymentsSettingAction, + litecoinMwebSettingAction, securityBackupSettingAction, privacySettingAction, displaySettingAction, @@ -46,6 +48,15 @@ class SettingActions { }, ); + static SettingActions litecoinMwebSettingAction = SettingActions._( + name: (context) => S.current.litecoin_mweb_settings, + image: 'assets/images/mweb_logo.png', + onTap: (BuildContext context) { + Navigator.pop(context); + Navigator.of(context).pushNamed(Routes.mwebSettings); + }, + ); + static SettingActions connectionSettingAction = SettingActions._( name: (context) => S.of(context).connection_sync, image: 'assets/images/nodes_menu.png', diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 0992451be..404b47181 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -110,6 +110,7 @@ abstract class SettingsStoreBase with Store { required this.customBitcoinFeeRate, required this.silentPaymentsCardDisplay, required this.silentPaymentsAlwaysScan, + required this.mwebAlwaysScan, required this.mwebCardDisplay, required this.mwebEnabled, TransactionPriority? initialBitcoinTransactionPriority, @@ -538,6 +539,11 @@ abstract class SettingsStoreBase with Store { (bool silentPaymentsAlwaysScan) => _sharedPreferences.setBool( PreferencesKey.silentPaymentsAlwaysScan, silentPaymentsAlwaysScan)); + reaction( + (_) => mwebAlwaysScan, + (bool mwebAlwaysScan) => + _sharedPreferences.setBool(PreferencesKey.mwebAlwaysScan, mwebAlwaysScan)); + reaction( (_) => mwebCardDisplay, (bool mwebCardDisplay) => @@ -747,6 +753,9 @@ abstract class SettingsStoreBase with Store { @observable bool silentPaymentsAlwaysScan; + @observable + bool mwebAlwaysScan; + @observable bool mwebCardDisplay; @@ -909,6 +918,8 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; final silentPaymentsAlwaysScan = sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false; + final mwebAlwaysScan = + sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false; final mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; final mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; @@ -1163,6 +1174,7 @@ abstract class SettingsStoreBase with Store { customBitcoinFeeRate: customBitcoinFeeRate, silentPaymentsCardDisplay: silentPaymentsCardDisplay, silentPaymentsAlwaysScan: silentPaymentsAlwaysScan, + mwebAlwaysScan: mwebAlwaysScan, mwebCardDisplay: mwebCardDisplay, mwebEnabled: mwebEnabled, initialMoneroTransactionPriority: moneroTransactionPriority, @@ -1315,6 +1327,7 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; silentPaymentsAlwaysScan = sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false; + mwebAlwaysScan = sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false; mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 64e554b1d..b11213741 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -181,7 +181,8 @@ abstract class DashboardViewModelBase with Store { final _accountTransactions = _wallet.transactionHistory.transactions.values .where((tx) => - wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id) + wow.wownero!.getTransactionInfoAccountId(tx) == + wow.wownero!.getCurrentAccount(wallet).id) .toList(); final sortedTransactions = [..._accountTransactions]; @@ -239,6 +240,10 @@ abstract class DashboardViewModelBase with Store { silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet); }); } + + if (hasMweb) { + mwebScanningActive = bitcoin!.getMwebEnabled(wallet); + } } @observable @@ -364,15 +369,15 @@ abstract class DashboardViewModelBase with Store { bool get showMwebCard => hasMweb && settingsStore.mwebCardDisplay; @observable - bool mwebEnabled = false; + bool mwebScanningActive = false; @action void setMwebEnabled(bool active) { - mwebEnabled = active; - - if (hasMweb) { - bitcoin!.setMwebEnabled(wallet, active); + if (!hasMweb) { + return; } + + bitcoin!.setMwebEnabled(wallet, active); } BalanceViewModel balanceViewModel; @@ -552,7 +557,8 @@ abstract class DashboardViewModelBase with Store { } if (wallet.type == WalletType.wownero) { - return wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id; + return wow.wownero!.getTransactionInfoAccountId(tx) == + wow.wownero!.getCurrentAccount(wallet).id; } return true; @@ -577,8 +583,8 @@ abstract class DashboardViewModelBase with Store { .getTransactionHistory(wallet) .transactions .values - .where( - (tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id) + .where((tx) => + monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id) .toList(); transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem( @@ -590,8 +596,9 @@ abstract class DashboardViewModelBase with Store { .getTransactionHistory(wallet) .transactions .values - .where( - (tx) => wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id) + .where((tx) => + wow.wownero!.getTransactionInfoAccountId(tx) == + wow.wownero!.getCurrentAccount(wallet).id) .toList(); transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem( diff --git a/lib/view_model/settings/mweb_settings_view_model.dart b/lib/view_model/settings/mweb_settings_view_model.dart new file mode 100644 index 000000000..343947d00 --- /dev/null +++ b/lib/view_model/settings/mweb_settings_view_model.dart @@ -0,0 +1,32 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:mobx/mobx.dart'; + +part 'mweb_settings_view_model.g.dart'; + +class MwebSettingsViewModel = MwebSettingsViewModelBase with _$MwebSettingsViewModel; + +abstract class MwebSettingsViewModelBase with Store { + MwebSettingsViewModelBase(this._settingsStore, this._wallet); + + final SettingsStore _settingsStore; + final WalletBase _wallet; + + @computed + bool get mwebCardDisplay => _settingsStore.mwebCardDisplay; + + @computed + bool get mwebAlwaysScan => _settingsStore.mwebAlwaysScan; + + @action + void setMwebCardDisplay(bool value) { + _settingsStore.mwebCardDisplay = value; + } + + @action + void setMwebAlwaysScan(bool value) { + _settingsStore.mwebAlwaysScan = value; + bitcoin!.setMwebEnabled(_wallet, value); + } +} diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index b2b8f8d45..6114ce670 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -350,6 +350,10 @@ "light_theme": "فاتح", "litecoin_enable_mweb_sync": "تمكين MWEB المزامنة", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "اضبط MWEB دائمًا على المسح الضوئي", + "litecoin_mweb_display_card": "عرض بطاقة mweb", + "litecoin_mweb_scanning": "MWEB المسح الضوئي", + "litecoin_mweb_settings": "إعدادات Litecoin MWEB", "litecoin_what_is_mweb": "ما هو MWEB؟", "load_more": "تحميل المزيد", "loading_your_wallet": "يتم تحميل محفظتك", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index b5429dea0..e55fddbb2 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -350,6 +350,10 @@ "light_theme": "Светло", "litecoin_enable_mweb_sync": "Активиране на MWEB Sync", "litecoin_mweb": "Litecoin MWeb", + "litecoin_mweb_always_scan": "Задайте MWeb винаги сканиране", + "litecoin_mweb_display_card": "Показване на MWEB карта", + "litecoin_mweb_scanning": "Сканиране на MWEB", + "litecoin_mweb_settings": "Настройки на Litecoin MWeb", "litecoin_what_is_mweb": "Какво е MWEB?", "load_more": "Зареди още", "loading_your_wallet": "Зареждане на портфейл", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 7790bce73..f55ce02b5 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -350,6 +350,10 @@ "light_theme": "Světlý", "litecoin_enable_mweb_sync": "Povolit synchronizaci MWeb", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "Nastavit MWeb vždy skenování", + "litecoin_mweb_display_card": "Zobrazit kartu MWeb", + "litecoin_mweb_scanning": "Skenování mWeb", + "litecoin_mweb_settings": "Nastavení litecoin mWeb", "litecoin_what_is_mweb": "Co je Mweb?", "load_more": "Načíst další", "loading_your_wallet": "Načítám peněženku", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index ee6367eff..e9441bf72 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -350,6 +350,10 @@ "light_theme": "Hell", "litecoin_enable_mweb_sync": "Aktivieren Sie die MWEB -Synchronisierung", "litecoin_mweb": "Litecoin MWeb", + "litecoin_mweb_always_scan": "Setzen Sie MWeb immer scannen", + "litecoin_mweb_display_card": "MWEB -Karte anzeigen", + "litecoin_mweb_scanning": "MWEB Scanning", + "litecoin_mweb_settings": "Litecoin MWeb -Einstellungen", "litecoin_what_is_mweb": "Was ist MWeb?", "load_more": "Mehr laden", "loading_your_wallet": "Wallet wird geladen", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index a1cb8f80a..9bb807645 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -350,6 +350,10 @@ "light_theme": "Light", "litecoin_enable_mweb_sync": "Enable MWEB sync", "litecoin_mweb": "Litecoin MWEB", + "litecoin_mweb_always_scan": "Set MWEB always scanning", + "litecoin_mweb_display_card": "Show MWEB card", + "litecoin_mweb_scanning": "MWEB Scanning", + "litecoin_mweb_settings": "MWEB settings", "litecoin_what_is_mweb": "What is MWEB?", "load_more": "Load more", "loading_your_wallet": "Loading your wallet", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 2843be33a..f3085677c 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -350,6 +350,10 @@ "light_theme": "Ligera", "litecoin_enable_mweb_sync": "Habilitar MWEB Sync", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "Establecer mweb siempre escaneo", + "litecoin_mweb_display_card": "Mostrar tarjeta MWEB", + "litecoin_mweb_scanning": "Escaneo mweb", + "litecoin_mweb_settings": "Configuración de litecoin mweb", "litecoin_what_is_mweb": "¿Qué es mweb?", "load_more": "Carga más", "loading_your_wallet": "Cargando tu billetera", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index bd0b1b6b0..48c1c9031 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -350,6 +350,10 @@ "light_theme": "Clair", "litecoin_enable_mweb_sync": "Activer la synchronisation MWEB", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "Définir MWEB Score Scanning", + "litecoin_mweb_display_card": "Afficher la carte MWeb", + "litecoin_mweb_scanning": "Scann mweb", + "litecoin_mweb_settings": "Paramètres litecoin mweb", "litecoin_what_is_mweb": "Qu'est-ce que MWEB?", "load_more": "Charger plus", "loading_your_wallet": "Chargement de votre portefeuille (wallet)", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 9ea2404bc..1a228e84d 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -350,6 +350,10 @@ "light_theme": "Haske", "litecoin_enable_mweb_sync": "Kunna Mweb Sync", "litecoin_mweb": "Litcoin Mweb", + "litecoin_mweb_always_scan": "Saita Mweb koyaushe", + "litecoin_mweb_display_card": "Nuna katin Mweb", + "litecoin_mweb_scanning": "Mweb scanning", + "litecoin_mweb_settings": "Saitunan Litcoin Mweb", "litecoin_what_is_mweb": "Menene Mweb?", "load_more": "Like more", "loading_your_wallet": "Ana loda walat ɗin ku", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index d8ae7530c..0cd16ff4b 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -350,6 +350,10 @@ "light_theme": "रोशनी", "litecoin_enable_mweb_sync": "MWEB सिंक सक्षम करें", "litecoin_mweb": "लिटकोइन मेवेब", + "litecoin_mweb_always_scan": "MWEB हमेशा स्कैनिंग सेट करें", + "litecoin_mweb_display_card": "MWEB कार्ड दिखाएं", + "litecoin_mweb_scanning": "MWEB स्कैनिंग", + "litecoin_mweb_settings": "Litecoin MWEB सेटिंग्स", "litecoin_what_is_mweb": "MWEB क्या है?", "load_more": "और लोड करें", "loading_your_wallet": "अपना बटुआ लोड कर रहा है", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 15d989657..f6770f6d6 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -350,6 +350,10 @@ "light_theme": "Svijetla", "litecoin_enable_mweb_sync": "Omogući MWEB sinkronizaciju", "litecoin_mweb": "Litecoin MWeb", + "litecoin_mweb_always_scan": "Postavite MWeb uvijek skeniranje", + "litecoin_mweb_display_card": "Prikaži MWeb karticu", + "litecoin_mweb_scanning": "MWEB skeniranje", + "litecoin_mweb_settings": "Litecoin MWeb postavke", "litecoin_what_is_mweb": "Što je MWEB?", "load_more": "Učitaj više", "loading_your_wallet": "Novčanik se učitava", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 6dcb89289..fa4e83276 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -350,6 +350,10 @@ "light_theme": "Terang", "litecoin_enable_mweb_sync": "Aktifkan Sinkronisasi MWEB", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "Atur mWeb selalu memindai", + "litecoin_mweb_display_card": "Tunjukkan kartu mWeb", + "litecoin_mweb_scanning": "Pemindaian MWEB", + "litecoin_mweb_settings": "Pengaturan Litecoin MWEB", "litecoin_what_is_mweb": "Apa itu MWEB?", "load_more": "Muat lebih banyak", "loading_your_wallet": "Memuat dompet Anda", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 157582562..d79c50bea 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -351,6 +351,10 @@ "light_theme": "Bianco", "litecoin_enable_mweb_sync": "Abilita MWeb Sync", "litecoin_mweb": "Litecoin MWeb", + "litecoin_mweb_always_scan": "Imposta MWeb per scansionare sempre", + "litecoin_mweb_display_card": "Mostra la scheda MWeb", + "litecoin_mweb_scanning": "Scansione MWeb", + "litecoin_mweb_settings": "Impostazioni MWeb Litecoin", "litecoin_what_is_mweb": "Cos'è MWeb?", "load_more": "Carica di più", "loading_your_wallet": "Caricamento portafoglio", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index fa6d48aa0..2a8f5c764 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -351,6 +351,10 @@ "light_theme": "光", "litecoin_enable_mweb_sync": "MWEB同期を有効にします", "litecoin_mweb": "litecoin mweb", + "litecoin_mweb_always_scan": "MWEBを常にスキャンします", + "litecoin_mweb_display_card": "MWEBカードを表示します", + "litecoin_mweb_scanning": "MWEBスキャン", + "litecoin_mweb_settings": "Litecoin MWEB設定", "litecoin_what_is_mweb": "MWEBとは何ですか?", "load_more": "もっと読み込む", "loading_your_wallet": "ウォレットをロードしています", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 7d8d44b0d..ee7f2f9c5 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -350,6 +350,10 @@ "light_theme": "빛", "litecoin_enable_mweb_sync": "mweb 동기화를 활성화합니다", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "mweb는 항상 스캔을 설정합니다", + "litecoin_mweb_display_card": "mweb 카드를 보여주십시오", + "litecoin_mweb_scanning": "mweb 스캔", + "litecoin_mweb_settings": "Litecoin mweb 설정", "litecoin_what_is_mweb": "MWEB 란 무엇입니까?", "load_more": "더로드하십시오", "loading_your_wallet": "지갑 넣기", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index e2e7b816a..6106b0557 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -350,6 +350,10 @@ "light_theme": "အလင်း", "litecoin_enable_mweb_sync": "mweb စည်းညှိမှုကို enable", "litecoin_mweb": "Litecoin Mweb", + "litecoin_mweb_always_scan": "Mweb အမြဲစကင်ဖတ်စစ်ဆေးပါ", + "litecoin_mweb_display_card": "MweB ကဒ်ကိုပြပါ", + "litecoin_mweb_scanning": "mweb scanning", + "litecoin_mweb_settings": "Litecoin Mweb ချိန်ညှိချက်များ", "litecoin_what_is_mweb": "MweB ဆိုတာဘာလဲ။", "load_more": "ပိုပြီး load", "loading_your_wallet": "သင့်ပိုက်ဆံအိတ်ကို ဖွင့်နေသည်။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 2c5f4a0b0..74c6794f7 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -350,6 +350,10 @@ "light_theme": "Licht", "litecoin_enable_mweb_sync": "MWEB SYNC inschakelen", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "Stel mweb altijd op scannen", + "litecoin_mweb_display_card": "Toon MWEB -kaart", + "litecoin_mweb_scanning": "MWEB -scanning", + "litecoin_mweb_settings": "Litecoin mweb -instellingen", "litecoin_what_is_mweb": "Wat is Mweb?", "load_more": "Meer laden", "loading_your_wallet": "Uw portemonnee laden", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 4a7698002..9f37d04a0 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -350,6 +350,10 @@ "light_theme": "Jasny", "litecoin_enable_mweb_sync": "Włącz synchronizację MWEB", "litecoin_mweb": "Litecoin MWEB", + "litecoin_mweb_always_scan": "Ustaw MWEB zawsze skanowanie", + "litecoin_mweb_display_card": "Pokaż kartę MWEB", + "litecoin_mweb_scanning": "Skanowanie MWEB", + "litecoin_mweb_settings": "Ustawienia MWEB Litecoin", "litecoin_what_is_mweb": "Co to jest MWEB?", "load_more": "Załaduj więcej", "loading_your_wallet": "Ładowanie portfela", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 2d41ea1d6..c3dd95bad 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -350,6 +350,10 @@ "light_theme": "Luz", "litecoin_enable_mweb_sync": "Habilite MWEB Sync", "litecoin_mweb": "Litecoin Mweb", + "litecoin_mweb_always_scan": "Definir mweb sempre digitalizando", + "litecoin_mweb_display_card": "Mostre o cartão MWEB", + "litecoin_mweb_scanning": "MWEB Scanning", + "litecoin_mweb_settings": "Configurações do Litecoin MWEB", "litecoin_what_is_mweb": "O que é MWeb?", "load_more": "Carregue mais", "loading_your_wallet": "Abrindo sua carteira", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 968140c18..d3a887b7f 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -350,6 +350,10 @@ "light_theme": "Светлая", "litecoin_enable_mweb_sync": "Включить MWEB Sync", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "Установить MWEB всегда сканирование", + "litecoin_mweb_display_card": "Показать карту MWEB", + "litecoin_mweb_scanning": "MWEB сканирование", + "litecoin_mweb_settings": "Litecoin MWEB Settings", "litecoin_what_is_mweb": "Что такое MWEB?", "load_more": "Загрузи больше", "loading_your_wallet": "Загрузка кошелька", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 516c5484e..c615f1a07 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -350,6 +350,10 @@ "light_theme": "สว่าง", "litecoin_enable_mweb_sync": "เปิดใช้งานการซิงค์ MWEB", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "ตั้งค่าการสแกน MWEB เสมอ", + "litecoin_mweb_display_card": "แสดงการ์ด mweb", + "litecoin_mweb_scanning": "การสแกน MWEB", + "litecoin_mweb_settings": "การตั้งค่า Litecoin Mweb", "litecoin_what_is_mweb": "MWEB คืออะไร?", "load_more": "โหลดมากขึ้น", "loading_your_wallet": "กำลังโหลดกระเป๋าของคุณ", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 9d05d4fd8..c4c6bb2c6 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -350,6 +350,10 @@ "light_theme": "Ilaw", "litecoin_enable_mweb_sync": "Paganahin ang MWEB Sync", "litecoin_mweb": "Litecoin Mweb", + "litecoin_mweb_always_scan": "Itakda ang MWeb na laging nag -scan", + "litecoin_mweb_display_card": "Ipakita ang MWEB Card", + "litecoin_mweb_scanning": "Pag -scan ng Mweb", + "litecoin_mweb_settings": "Mga Setting ng Litecoin MWeb", "litecoin_what_is_mweb": "Ano ang MWEB?", "load_more": "Mag -load pa", "loading_your_wallet": "Naglo -load ng iyong pitaka", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index f87f2f6b3..60b51424d 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -350,6 +350,10 @@ "light_theme": "Aydınlık", "litecoin_enable_mweb_sync": "MWEB senkronizasyonunu etkinleştir", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "MWEB'i her zaman taramayı ayarlayın", + "litecoin_mweb_display_card": "MWEB kartını göster", + "litecoin_mweb_scanning": "MWEB taraması", + "litecoin_mweb_settings": "Litecoin mweb ayarları", "litecoin_what_is_mweb": "MWEB nedir?", "load_more": "Daha fazla yükle", "loading_your_wallet": "Cüzdanın yükleniyor", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index dfbc7b83c..7ae1c0da2 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -350,6 +350,10 @@ "light_theme": "Світла", "litecoin_enable_mweb_sync": "Увімкнути MWEB SYNC", "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb_always_scan": "Встановити mweb завжди сканувати", + "litecoin_mweb_display_card": "Показати карту MWeb", + "litecoin_mweb_scanning": "Сканування Mweb", + "litecoin_mweb_settings": "Налаштування Litecoin MWEB", "litecoin_what_is_mweb": "Що таке mweb?", "load_more": "Завантажити ще", "loading_your_wallet": "Завантаження гаманця", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index ca52cccc9..672f3a980 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -350,6 +350,10 @@ "light_theme": "روشنی", "litecoin_enable_mweb_sync": "MWEB مطابقت پذیری کو فعال کریں", "litecoin_mweb": "litcoin mweb", + "litecoin_mweb_always_scan": "MWEB ہمیشہ اسکیننگ سیٹ کریں", + "litecoin_mweb_display_card": "MWEB کارڈ دکھائیں", + "litecoin_mweb_scanning": "MWEB اسکیننگ", + "litecoin_mweb_settings": "litcoin mweb کی ترتیبات", "litecoin_what_is_mweb": "MWEB کیا ہے؟", "load_more": "مزید لوڈ کریں", "loading_your_wallet": "آپ کا بٹوہ لوڈ ہو رہا ہے۔", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 95384b1c3..d4fe70afd 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -351,6 +351,10 @@ "light_theme": "Funfun bí eérú", "litecoin_enable_mweb_sync": "Mu ṣiṣẹmu MweB", "litecoin_mweb": "Livecoin mweb", + "litecoin_mweb_always_scan": "Ṣeto mweb nigbagbogbo n ṣayẹwo", + "litecoin_mweb_display_card": "Fihan kaadi Mweb", + "litecoin_mweb_scanning": "Mweb scanning", + "litecoin_mweb_settings": "Awọn eto idile Mwein mweb", "litecoin_what_is_mweb": "Kini mweb?", "load_more": "Ẹru diẹ sii", "loading_your_wallet": "A ń ṣí àpamọ́wọ́ yín", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index c1d559daa..c904d6cd3 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -350,6 +350,10 @@ "light_theme": "艳丽", "litecoin_enable_mweb_sync": "启用MWEB同步", "litecoin_mweb": "Litecoin Mweb", + "litecoin_mweb_always_scan": "设置MWEB总是扫描", + "litecoin_mweb_display_card": "显示MWEB卡", + "litecoin_mweb_scanning": "MWEB扫描", + "litecoin_mweb_settings": "Litecoin MWEB设置", "litecoin_what_is_mweb": "什么是MWEB?", "load_more": "装载更多", "loading_your_wallet": "加载您的钱包", From 381830d5afec192c665d90329e662ad7b68bcdad Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 10:55:47 -0700 Subject: [PATCH 091/100] minor fix --- lib/src/screens/dashboard/pages/balance_page.dart | 4 ++-- lib/view_model/dashboard/dashboard_view_model.dart | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index 3c516e02d..ac08de33e 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -364,7 +364,7 @@ class CryptoBalanceWidget extends StatelessWidget { ), Observer( builder: (_) => StandardSwitch( - value: dashboardViewModel.mwebEnabled, + value: dashboardViewModel.mwebScanningActive, onTaped: () => _toggleMweb(context), ), ) @@ -423,7 +423,7 @@ class CryptoBalanceWidget extends StatelessWidget { } Future _toggleMweb(BuildContext context) async { - return dashboardViewModel.setMwebEnabled(!dashboardViewModel.mwebEnabled); + return dashboardViewModel.setMwebScanningActive(!dashboardViewModel.mwebScanningActive); } } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index b11213741..f144bbaec 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -372,11 +372,12 @@ abstract class DashboardViewModelBase with Store { bool mwebScanningActive = false; @action - void setMwebEnabled(bool active) { + void setMwebScanningActive(bool active) { if (!hasMweb) { return; } + mwebScanningActive = active; bitcoin!.setMwebEnabled(wallet, active); } From c127743a2d5ed1c10728c2ff068a74a521cc94b8 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 11:21:04 -0700 Subject: [PATCH 092/100] [skip ci] update translations + minor fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 11 ++++++----- res/values/strings_ar.arb | 2 +- res/values/strings_bg.arb | 2 +- res/values/strings_cs.arb | 2 +- res/values/strings_de.arb | 4 ++-- res/values/strings_es.arb | 2 +- res/values/strings_fr.arb | 2 +- res/values/strings_ha.arb | 2 +- res/values/strings_hi.arb | 2 +- res/values/strings_hr.arb | 2 +- res/values/strings_id.arb | 2 +- res/values/strings_it.arb | 2 +- res/values/strings_ja.arb | 2 +- res/values/strings_ko.arb | 2 +- res/values/strings_my.arb | 2 +- res/values/strings_nl.arb | 2 +- res/values/strings_pl.arb | 2 +- res/values/strings_pt.arb | 2 +- res/values/strings_ru.arb | 2 +- res/values/strings_th.arb | 2 +- res/values/strings_tl.arb | 2 +- res/values/strings_tr.arb | 2 +- res/values/strings_uk.arb | 2 +- res/values/strings_ur.arb | 2 +- res/values/strings_yo.arb | 2 +- res/values/strings_zh.arb | 2 +- 26 files changed, 32 insertions(+), 31 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 4d0beae45..90e621a64 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -754,11 +754,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } void setMwebEnabled(bool enabled) { - if (!mwebEnabled && enabled) { - mwebEnabled = enabled; - startSync(); + if (mwebEnabled == enabled) { + return; } - mwebEnabled = enabled; - } + mwebEnabled = enabled; + stopSync(); + startSync(); + } } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 6114ce670..27a0ae789 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "اضبط MWEB دائمًا على المسح الضوئي", "litecoin_mweb_display_card": "عرض بطاقة mweb", "litecoin_mweb_scanning": "MWEB المسح الضوئي", - "litecoin_mweb_settings": "إعدادات Litecoin MWEB", + "litecoin_mweb_settings": "إعدادات MWEB", "litecoin_what_is_mweb": "ما هو MWEB؟", "load_more": "تحميل المزيد", "loading_your_wallet": "يتم تحميل محفظتك", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index e55fddbb2..825175429 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Задайте MWeb винаги сканиране", "litecoin_mweb_display_card": "Показване на MWEB карта", "litecoin_mweb_scanning": "Сканиране на MWEB", - "litecoin_mweb_settings": "Настройки на Litecoin MWeb", + "litecoin_mweb_settings": "Настройки на MWEB", "litecoin_what_is_mweb": "Какво е MWEB?", "load_more": "Зареди още", "loading_your_wallet": "Зареждане на портфейл", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index f55ce02b5..edaecbfd8 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Nastavit MWeb vždy skenování", "litecoin_mweb_display_card": "Zobrazit kartu MWeb", "litecoin_mweb_scanning": "Skenování mWeb", - "litecoin_mweb_settings": "Nastavení litecoin mWeb", + "litecoin_mweb_settings": "Nastavení mWeb", "litecoin_what_is_mweb": "Co je Mweb?", "load_more": "Načíst další", "loading_your_wallet": "Načítám peněženku", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index e9441bf72..fa092c34e 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Setzen Sie MWeb immer scannen", "litecoin_mweb_display_card": "MWEB -Karte anzeigen", "litecoin_mweb_scanning": "MWEB Scanning", - "litecoin_mweb_settings": "Litecoin MWeb -Einstellungen", + "litecoin_mweb_settings": "MWEB -Einstellungen", "litecoin_what_is_mweb": "Was ist MWeb?", "load_more": "Mehr laden", "loading_your_wallet": "Wallet wird geladen", @@ -456,8 +456,8 @@ "placeholder_transactions": "Ihre Transaktionen werden hier angezeigt", "please_fill_totp": "Bitte geben Sie den 8-stelligen Code ein, der auf Ihrem anderen Gerät vorhanden ist", "please_make_selection": "Bitte treffen Sie unten eine Auswahl zum Erstellen oder Wiederherstellen Ihrer Wallet.", - "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.", "please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.", + "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.", "please_select": "Bitte auswählen:", "please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.", "please_try_to_connect_to_another_node": "Bitte versuchen Sie, sich mit einem anderen Knoten zu verbinden", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index f3085677c..a7cf90fc7 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Establecer mweb siempre escaneo", "litecoin_mweb_display_card": "Mostrar tarjeta MWEB", "litecoin_mweb_scanning": "Escaneo mweb", - "litecoin_mweb_settings": "Configuración de litecoin mweb", + "litecoin_mweb_settings": "Configuración de MWEB", "litecoin_what_is_mweb": "¿Qué es mweb?", "load_more": "Carga más", "loading_your_wallet": "Cargando tu billetera", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 48c1c9031..055715004 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Définir MWEB Score Scanning", "litecoin_mweb_display_card": "Afficher la carte MWeb", "litecoin_mweb_scanning": "Scann mweb", - "litecoin_mweb_settings": "Paramètres litecoin mweb", + "litecoin_mweb_settings": "Paramètres MWEB", "litecoin_what_is_mweb": "Qu'est-ce que MWEB?", "load_more": "Charger plus", "loading_your_wallet": "Chargement de votre portefeuille (wallet)", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 1a228e84d..19d692195 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Saita Mweb koyaushe", "litecoin_mweb_display_card": "Nuna katin Mweb", "litecoin_mweb_scanning": "Mweb scanning", - "litecoin_mweb_settings": "Saitunan Litcoin Mweb", + "litecoin_mweb_settings": "Saitunan Mweb", "litecoin_what_is_mweb": "Menene Mweb?", "load_more": "Like more", "loading_your_wallet": "Ana loda walat ɗin ku", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 0cd16ff4b..f1e24caf5 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "MWEB हमेशा स्कैनिंग सेट करें", "litecoin_mweb_display_card": "MWEB कार्ड दिखाएं", "litecoin_mweb_scanning": "MWEB स्कैनिंग", - "litecoin_mweb_settings": "Litecoin MWEB सेटिंग्स", + "litecoin_mweb_settings": "MWEB सेटिंग्स", "litecoin_what_is_mweb": "MWEB क्या है?", "load_more": "और लोड करें", "loading_your_wallet": "अपना बटुआ लोड कर रहा है", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index f6770f6d6..a48dc9b5a 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Postavite MWeb uvijek skeniranje", "litecoin_mweb_display_card": "Prikaži MWeb karticu", "litecoin_mweb_scanning": "MWEB skeniranje", - "litecoin_mweb_settings": "Litecoin MWeb postavke", + "litecoin_mweb_settings": "Postavke MWEB -a", "litecoin_what_is_mweb": "Što je MWEB?", "load_more": "Učitaj više", "loading_your_wallet": "Novčanik se učitava", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index fa4e83276..85b110b0d 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Atur mWeb selalu memindai", "litecoin_mweb_display_card": "Tunjukkan kartu mWeb", "litecoin_mweb_scanning": "Pemindaian MWEB", - "litecoin_mweb_settings": "Pengaturan Litecoin MWEB", + "litecoin_mweb_settings": "Pengaturan MWEB", "litecoin_what_is_mweb": "Apa itu MWEB?", "load_more": "Muat lebih banyak", "loading_your_wallet": "Memuat dompet Anda", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index d79c50bea..fb75d8774 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -354,7 +354,7 @@ "litecoin_mweb_always_scan": "Imposta MWeb per scansionare sempre", "litecoin_mweb_display_card": "Mostra la scheda MWeb", "litecoin_mweb_scanning": "Scansione MWeb", - "litecoin_mweb_settings": "Impostazioni MWeb Litecoin", + "litecoin_mweb_settings": "Impostazioni MWeb", "litecoin_what_is_mweb": "Cos'è MWeb?", "load_more": "Carica di più", "loading_your_wallet": "Caricamento portafoglio", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 2a8f5c764..00d544400 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -354,7 +354,7 @@ "litecoin_mweb_always_scan": "MWEBを常にスキャンします", "litecoin_mweb_display_card": "MWEBカードを表示します", "litecoin_mweb_scanning": "MWEBスキャン", - "litecoin_mweb_settings": "Litecoin MWEB設定", + "litecoin_mweb_settings": "MWEB設定", "litecoin_what_is_mweb": "MWEBとは何ですか?", "load_more": "もっと読み込む", "loading_your_wallet": "ウォレットをロードしています", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index ee7f2f9c5..6268b60ea 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "mweb는 항상 스캔을 설정합니다", "litecoin_mweb_display_card": "mweb 카드를 보여주십시오", "litecoin_mweb_scanning": "mweb 스캔", - "litecoin_mweb_settings": "Litecoin mweb 설정", + "litecoin_mweb_settings": "mweb 설정", "litecoin_what_is_mweb": "MWEB 란 무엇입니까?", "load_more": "더로드하십시오", "loading_your_wallet": "지갑 넣기", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 6106b0557..e2a8b1127 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Mweb အမြဲစကင်ဖတ်စစ်ဆေးပါ", "litecoin_mweb_display_card": "MweB ကဒ်ကိုပြပါ", "litecoin_mweb_scanning": "mweb scanning", - "litecoin_mweb_settings": "Litecoin Mweb ချိန်ညှိချက်များ", + "litecoin_mweb_settings": "Mweb ဆက်တင်များ", "litecoin_what_is_mweb": "MweB ဆိုတာဘာလဲ။", "load_more": "ပိုပြီး load", "loading_your_wallet": "သင့်ပိုက်ဆံအိတ်ကို ဖွင့်နေသည်။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 74c6794f7..c1953c129 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Stel mweb altijd op scannen", "litecoin_mweb_display_card": "Toon MWEB -kaart", "litecoin_mweb_scanning": "MWEB -scanning", - "litecoin_mweb_settings": "Litecoin mweb -instellingen", + "litecoin_mweb_settings": "MWEB -instellingen", "litecoin_what_is_mweb": "Wat is Mweb?", "load_more": "Meer laden", "loading_your_wallet": "Uw portemonnee laden", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 9f37d04a0..9b9482e36 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Ustaw MWEB zawsze skanowanie", "litecoin_mweb_display_card": "Pokaż kartę MWEB", "litecoin_mweb_scanning": "Skanowanie MWEB", - "litecoin_mweb_settings": "Ustawienia MWEB Litecoin", + "litecoin_mweb_settings": "Ustawienia MWEB", "litecoin_what_is_mweb": "Co to jest MWEB?", "load_more": "Załaduj więcej", "loading_your_wallet": "Ładowanie portfela", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index c3dd95bad..3fe816c72 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Definir mweb sempre digitalizando", "litecoin_mweb_display_card": "Mostre o cartão MWEB", "litecoin_mweb_scanning": "MWEB Scanning", - "litecoin_mweb_settings": "Configurações do Litecoin MWEB", + "litecoin_mweb_settings": "Configurações do MWEB", "litecoin_what_is_mweb": "O que é MWeb?", "load_more": "Carregue mais", "loading_your_wallet": "Abrindo sua carteira", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index d3a887b7f..c986b81a0 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Установить MWEB всегда сканирование", "litecoin_mweb_display_card": "Показать карту MWEB", "litecoin_mweb_scanning": "MWEB сканирование", - "litecoin_mweb_settings": "Litecoin MWEB Settings", + "litecoin_mweb_settings": "Настройки MWEB", "litecoin_what_is_mweb": "Что такое MWEB?", "load_more": "Загрузи больше", "loading_your_wallet": "Загрузка кошелька", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index c615f1a07..f2f41d825 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "ตั้งค่าการสแกน MWEB เสมอ", "litecoin_mweb_display_card": "แสดงการ์ด mweb", "litecoin_mweb_scanning": "การสแกน MWEB", - "litecoin_mweb_settings": "การตั้งค่า Litecoin Mweb", + "litecoin_mweb_settings": "การตั้งค่า MWEB", "litecoin_what_is_mweb": "MWEB คืออะไร?", "load_more": "โหลดมากขึ้น", "loading_your_wallet": "กำลังโหลดกระเป๋าของคุณ", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index c4c6bb2c6..2dd1bf6fc 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Itakda ang MWeb na laging nag -scan", "litecoin_mweb_display_card": "Ipakita ang MWEB Card", "litecoin_mweb_scanning": "Pag -scan ng Mweb", - "litecoin_mweb_settings": "Mga Setting ng Litecoin MWeb", + "litecoin_mweb_settings": "Mga Setting ng Mweb", "litecoin_what_is_mweb": "Ano ang MWEB?", "load_more": "Mag -load pa", "loading_your_wallet": "Naglo -load ng iyong pitaka", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 60b51424d..146a480d5 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "MWEB'i her zaman taramayı ayarlayın", "litecoin_mweb_display_card": "MWEB kartını göster", "litecoin_mweb_scanning": "MWEB taraması", - "litecoin_mweb_settings": "Litecoin mweb ayarları", + "litecoin_mweb_settings": "MWEB ayarları", "litecoin_what_is_mweb": "MWEB nedir?", "load_more": "Daha fazla yükle", "loading_your_wallet": "Cüzdanın yükleniyor", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 7ae1c0da2..025f71389 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "Встановити mweb завжди сканувати", "litecoin_mweb_display_card": "Показати карту MWeb", "litecoin_mweb_scanning": "Сканування Mweb", - "litecoin_mweb_settings": "Налаштування Litecoin MWEB", + "litecoin_mweb_settings": "Налаштування MWEB", "litecoin_what_is_mweb": "Що таке mweb?", "load_more": "Завантажити ще", "loading_your_wallet": "Завантаження гаманця", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 672f3a980..95a2301d1 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "MWEB ہمیشہ اسکیننگ سیٹ کریں", "litecoin_mweb_display_card": "MWEB کارڈ دکھائیں", "litecoin_mweb_scanning": "MWEB اسکیننگ", - "litecoin_mweb_settings": "litcoin mweb کی ترتیبات", + "litecoin_mweb_settings": "MWEB کی ترتیبات", "litecoin_what_is_mweb": "MWEB کیا ہے؟", "load_more": "مزید لوڈ کریں", "loading_your_wallet": "آپ کا بٹوہ لوڈ ہو رہا ہے۔", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index d4fe70afd..e593a866c 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -354,7 +354,7 @@ "litecoin_mweb_always_scan": "Ṣeto mweb nigbagbogbo n ṣayẹwo", "litecoin_mweb_display_card": "Fihan kaadi Mweb", "litecoin_mweb_scanning": "Mweb scanning", - "litecoin_mweb_settings": "Awọn eto idile Mwein mweb", + "litecoin_mweb_settings": "Awọn eto Mweb", "litecoin_what_is_mweb": "Kini mweb?", "load_more": "Ẹru diẹ sii", "loading_your_wallet": "A ń ṣí àpamọ́wọ́ yín", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index c904d6cd3..943023c5c 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -353,7 +353,7 @@ "litecoin_mweb_always_scan": "设置MWEB总是扫描", "litecoin_mweb_display_card": "显示MWEB卡", "litecoin_mweb_scanning": "MWEB扫描", - "litecoin_mweb_settings": "Litecoin MWEB设置", + "litecoin_mweb_settings": "MWEB设置", "litecoin_what_is_mweb": "什么是MWEB?", "load_more": "装载更多", "loading_your_wallet": "加载您的钱包", From 6946db6344f13c8659d14d644e62c21f723eecf7 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 12:32:57 -0700 Subject: [PATCH 093/100] state fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 56 +++++++++++++------ cw_bitcoin/lib/litecoin_wallet_addresses.dart | 2 +- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 90e621a64..c526bb0c0 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -209,6 +209,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } }); updateUnspent(); + fetchBalances(); // this runs in the background and processes new utxos as they come in: processMwebUtxos(); } @@ -255,6 +256,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { transactionHistory.clear(); mwebUtxosHeight = height; await walletInfo.updateRestoreHeight(height); + + // reset coin balances and txCount to 0: + unspentCoins.forEach((coin) { + if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) + coin.bitcoinAddressRecord.balance = 0; + coin.bitcoinAddressRecord.txCount = 0; + }); + + for (var addressRecord in walletAddresses.allAddresses) { + addressRecord.balance = 0; + addressRecord.txCount = 0; + } + print("STARTING SYNC"); await startSync(); } @@ -312,17 +326,25 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (addressRecord == null) { return; } - if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) { + + // if our address isn't in the inputs, update the txCount: + final inputAddresses = tx.inputAddresses ?? []; + if (!inputAddresses.contains(utxo.address)) { addressRecord.txCount++; - // print("COUNT UPDATED HERE 2!!!!! ${addressRecord.txCount}"); } + addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); + } + transactionHistory.addOne(tx); + + if (isNew) { // update the unconfirmed balance when a new tx is added: + // we do this after adding the tx to the history so that sub address balances are updated correctly + // (since that calculation is based on the tx history) await updateBalance(); } - transactionHistory.addOne(tx); } Future processMwebUtxos() async { @@ -344,7 +366,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await handleIncoming(utxo, _stub); if (utxo.height > walletInfo.restoreHeight) { - walletInfo.updateRestoreHeight(utxo.height); + await walletInfo.updateRestoreHeight(utxo.height); } } @@ -519,13 +541,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { updatedUnspentCoins.add(unspent); }); } - - // print(unspentCoins); - // print(updatedUnspentCoins); - // print(updatedUnspentCoins.length); - - // print(updatedUnspentCoins[2].address); - + unspentCoins = updatedUnspentCoins; } @@ -545,11 +561,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // update unspent balances: // reset coin balances and txCount to 0: - unspentCoins.forEach((coin) { - if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) - coin.bitcoinAddressRecord.balance = 0; - coin.bitcoinAddressRecord.txCount = 0; - }); + // unspentCoins.forEach((coin) { + // if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) + // coin.bitcoinAddressRecord.balance = 0; + // coin.bitcoinAddressRecord.txCount = 0; + // }); + for (var addressRecord in walletAddresses.allAddresses) { + addressRecord.balance = 0; + addressRecord.txCount = 0; + } unspentCoins.forEach((coin) { final coinInfoList = unspentCoinsInfo.values.where( @@ -572,7 +592,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } }); - // update the txCount for each address: + // update the txCount for each address using the tx history, since we can't rely on mwebd + // to have an accurate count, we should just keep it in sync with what we know from the tx history: for (var tx in transactionHistory.transactions.values) { if (tx.isPending) continue; final txAddresses = tx.inputAddresses! + tx.outputAddresses!; @@ -583,7 +604,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { continue; } addressRecord.txCount++; - // print("COUNT UPDATED HERE 0!!!!! ${addressRecord.address} ${addressRecord.txCount} !!!!!!"); } } diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 8b89fb6f9..36bb569c6 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -70,7 +70,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Future getChangeAddress() async { // super.getChangeAddress(); // updateChangeAddresses(); - print("getChangeAddress @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + // print("getChangeAddress @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); // this means all change addresses used will be mweb addresses!: await topUpMweb(0); return mwebAddrs[0]; From eab8a87ddcd6e516b1c84cb54496cbb3ebd4fc62 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 13:22:48 -0700 Subject: [PATCH 094/100] configure fix --- tool/configure.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/configure.dart b/tool/configure.dart index f2907678f..021113125 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -182,7 +182,7 @@ abstract class Bitcoin { Future updateUnspents(Object wallet); WalletService createBitcoinWalletService( Box walletInfoSource, Box unspentCoinSource, bool alwaysScan); - WalletService createLitecoinWalletService(Box walletInfoSource, Box unspentCoinSource); + WalletService createLitecoinWalletService(Box walletInfoSource, Box unspentCoinSource, bool alwaysScan); TransactionPriority getBitcoinTransactionPriorityMedium(); TransactionPriority getBitcoinTransactionPriorityCustom(); TransactionPriority getLitecoinTransactionPriorityMedium(); From 8f7716fab5d2072a296cddf8bd0f796bf9039a0f Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 14:56:59 -0700 Subject: [PATCH 095/100] ui updates --- lib/entities/preferences_key.dart | 1 + .../screens/dashboard/pages/balance_page.dart | 16 +++++++++++++++- .../desktop_settings_page.dart | 2 +- lib/src/widgets/setting_actions.dart | 2 +- lib/store/settings_store.dart | 18 +++++++++++++++--- .../dashboard/dashboard_view_model.dart | 7 +++++++ res/values/strings_ar.arb | 3 ++- res/values/strings_bg.arb | 3 ++- res/values/strings_cs.arb | 3 ++- res/values/strings_de.arb | 3 ++- res/values/strings_en.arb | 3 ++- res/values/strings_es.arb | 3 ++- res/values/strings_fr.arb | 3 ++- res/values/strings_ha.arb | 3 ++- res/values/strings_hi.arb | 3 ++- res/values/strings_hr.arb | 3 ++- res/values/strings_id.arb | 3 ++- res/values/strings_it.arb | 3 ++- res/values/strings_ja.arb | 3 ++- res/values/strings_ko.arb | 3 ++- res/values/strings_my.arb | 3 ++- res/values/strings_nl.arb | 3 ++- res/values/strings_pl.arb | 3 ++- res/values/strings_pt.arb | 3 ++- res/values/strings_ru.arb | 3 ++- res/values/strings_th.arb | 3 ++- res/values/strings_tl.arb | 3 ++- res/values/strings_tr.arb | 3 ++- res/values/strings_uk.arb | 3 ++- res/values/strings_ur.arb | 3 ++- res/values/strings_yo.arb | 3 ++- res/values/strings_zh.arb | 3 ++- 32 files changed, 92 insertions(+), 32 deletions(-) diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 5511ba84a..20e55053d 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -50,6 +50,7 @@ class PreferencesKey { static const silentPaymentsAlwaysScan = 'silentPaymentsAlwaysScan'; static const mwebCardDisplay = 'mwebCardDisplay'; static const mwebEnabled = 'mwebEnabled'; + static const hasEnabledMwebBefore = 'hasEnabledMwebBefore'; static const mwebAlwaysScan = 'mwebAlwaysScan'; static const shouldShowReceiveWarning = 'should_show_receive_warning'; static const shouldShowYatPopup = 'should_show_yat_popup'; diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index ac08de33e..e9c113d8e 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart'; @@ -24,6 +25,7 @@ import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:mobx/mobx.dart'; import 'package:url_launcher/url_launcher.dart'; class BalancePage extends StatelessWidget { @@ -423,7 +425,19 @@ class CryptoBalanceWidget extends StatelessWidget { } Future _toggleMweb(BuildContext context) async { - return dashboardViewModel.setMwebScanningActive(!dashboardViewModel.mwebScanningActive); + if (!dashboardViewModel.hasEnabledMwebBefore) { + await showPopUp( + context: context, + builder: (BuildContext context) => AlertWithOneAction( + alertTitle: S.of(context).warning, + alertContent: S.current.litecoin_mweb_warning, + buttonText: S.of(context).ok, + buttonAction: () { + Navigator.of(context).pop(); + }, + )); + } + dashboardViewModel.setMwebScanningActive(!dashboardViewModel.mwebScanningActive); } } diff --git a/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart b/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart index 90729d4c7..79f74065a 100644 --- a/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart +++ b/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart @@ -62,7 +62,7 @@ class _DesktopSettingsPageState extends State { if (!widget.dashboardViewModel.hasMweb && item.name(context) == S.of(context).litecoin_mweb_settings) { - return const SizedBox(); + return Container(); } final isLastTile = index == itemCount - 1; diff --git a/lib/src/widgets/setting_actions.dart b/lib/src/widgets/setting_actions.dart index 62c4685b3..a8a9558d5 100644 --- a/lib/src/widgets/setting_actions.dart +++ b/lib/src/widgets/setting_actions.dart @@ -50,7 +50,7 @@ class SettingActions { static SettingActions litecoinMwebSettingAction = SettingActions._( name: (context) => S.current.litecoin_mweb_settings, - image: 'assets/images/mweb_logo.png', + image: 'assets/images/bitcoin_menu.png', onTap: (BuildContext context) { Navigator.pop(context); Navigator.of(context).pushNamed(Routes.mwebSettings); diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 404b47181..84b0dac2b 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -113,6 +113,7 @@ abstract class SettingsStoreBase with Store { required this.mwebAlwaysScan, required this.mwebCardDisplay, required this.mwebEnabled, + required this.hasEnabledMwebBefore, TransactionPriority? initialBitcoinTransactionPriority, TransactionPriority? initialMoneroTransactionPriority, TransactionPriority? initialWowneroTransactionPriority, @@ -552,6 +553,11 @@ abstract class SettingsStoreBase with Store { reaction((_) => mwebEnabled, (bool mwebEnabled) => _sharedPreferences.setBool(PreferencesKey.mwebEnabled, mwebEnabled)); + reaction( + (_) => hasEnabledMwebBefore, + (bool hasEnabledMwebBefore) => + _sharedPreferences.setBool(PreferencesKey.hasEnabledMwebBefore, hasEnabledMwebBefore)); + this.nodes.observe((change) { if (change.newValue != null && change.key != null) { _saveCurrentNode(change.newValue!, change.key!); @@ -762,6 +768,9 @@ abstract class SettingsStoreBase with Store { @observable bool mwebEnabled; + @observable + bool hasEnabledMwebBefore; + final SecureStorage _secureStorage; final SharedPreferences _sharedPreferences; final BackgroundTasks _backgroundTasks; @@ -918,10 +927,11 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; final silentPaymentsAlwaysScan = sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false; - final mwebAlwaysScan = - sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false; + final mwebAlwaysScan = sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false; final mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; - final mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; + final mwebEnabled = sharedPreferences.getBool(PreferencesKey.hasEnabledMwebBefore) ?? false; + final hasEnabledMwebBefore = + sharedPreferences.getBool(PreferencesKey.hasEnabledMwebBefore) ?? false; // If no value if (pinLength == null || pinLength == 0) { @@ -1177,6 +1187,7 @@ abstract class SettingsStoreBase with Store { mwebAlwaysScan: mwebAlwaysScan, mwebCardDisplay: mwebCardDisplay, mwebEnabled: mwebEnabled, + hasEnabledMwebBefore: hasEnabledMwebBefore, initialMoneroTransactionPriority: moneroTransactionPriority, initialWowneroTransactionPriority: wowneroTransactionPriority, initialBitcoinTransactionPriority: bitcoinTransactionPriority, @@ -1330,6 +1341,7 @@ abstract class SettingsStoreBase with Store { mwebAlwaysScan = sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false; mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; + hasEnabledMwebBefore = sharedPreferences.getBool(PreferencesKey.hasEnabledMwebBefore) ?? false; final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey); final bitcoinElectrumServerId = sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index f144bbaec..d34d0bfe3 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -371,12 +371,19 @@ abstract class DashboardViewModelBase with Store { @observable bool mwebScanningActive = false; + @computed + bool get hasEnabledMwebBefore => !settingsStore.disableBuy && hasBuyProviders; + @action void setMwebScanningActive(bool active) { if (!hasMweb) { return; } + if (active) { + settingsStore.hasEnabledMwebBefore = true; + } + mwebScanningActive = active; bitcoin!.setMwebEnabled(wallet, active); } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 27a0ae789..1f3c5e123 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "يرجى تمكين البلوتوث للكشف عن دفتر الأستاذ الخاص بك", "light_theme": "فاتح", "litecoin_enable_mweb_sync": "تمكين MWEB المزامنة", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "mweb", "litecoin_mweb_always_scan": "اضبط MWEB دائمًا على المسح الضوئي", "litecoin_mweb_display_card": "عرض بطاقة mweb", "litecoin_mweb_scanning": "MWEB المسح الضوئي", "litecoin_mweb_settings": "إعدادات MWEB", + "litecoin_mweb_warning": "سيقوم استخدام MWEB في البداية بتنزيل ~ 600 ميجابايت من البيانات ، وقد يستغرق ما يصل إلى 30 دقيقة حسب سرعة الشبكة. سيتم تنزيل هذه البيانات الأولية مرة واحدة فقط وستكون متاحة لجميع محافظ Litecoin", "litecoin_what_is_mweb": "ما هو MWEB؟", "load_more": "تحميل المزيد", "loading_your_wallet": "يتم تحميل محفظتك", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 825175429..816b768ff 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Моля, активирайте Bluetooth да открие вашата книга", "light_theme": "Светло", "litecoin_enable_mweb_sync": "Активиране на MWEB Sync", - "litecoin_mweb": "Litecoin MWeb", + "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Задайте MWeb винаги сканиране", "litecoin_mweb_display_card": "Показване на MWEB карта", "litecoin_mweb_scanning": "Сканиране на MWEB", "litecoin_mweb_settings": "Настройки на MWEB", + "litecoin_mweb_warning": "Използването на MWEB първоначално ще изтегли ~ 600MB данни и може да отнеме до 30 минути в зависимост от скоростта на мрежата. Тези първоначални данни ще изтеглят само веднъж и ще бъдат достъпни за всички портфейли Litecoin", "litecoin_what_is_mweb": "Какво е MWEB?", "load_more": "Зареди още", "loading_your_wallet": "Зареждане на портфейл", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index edaecbfd8..af0fe73fe 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Umožněte prosím Bluetooth detekovat vaši knihu", "light_theme": "Světlý", "litecoin_enable_mweb_sync": "Povolit synchronizaci MWeb", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "MWeb", "litecoin_mweb_always_scan": "Nastavit MWeb vždy skenování", "litecoin_mweb_display_card": "Zobrazit kartu MWeb", "litecoin_mweb_scanning": "Skenování mWeb", "litecoin_mweb_settings": "Nastavení mWeb", + "litecoin_mweb_warning": "Pomocí MWeb zpočátku stahuje ~ 600 MB dat a může trvat až 30 minut v závislosti na rychlosti sítě. Tato počáteční data si stáhnou pouze jednou a budou k dispozici pro všechny litecoinové peněženky", "litecoin_what_is_mweb": "Co je Mweb?", "load_more": "Načíst další", "loading_your_wallet": "Načítám peněženku", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index fa092c34e..669ace926 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Bitte aktivieren Sie Bluetooth um sich mit Ihren Ledger zu verbinden.", "light_theme": "Hell", "litecoin_enable_mweb_sync": "Aktivieren Sie die MWEB -Synchronisierung", - "litecoin_mweb": "Litecoin MWeb", + "litecoin_mweb": "MWeb", "litecoin_mweb_always_scan": "Setzen Sie MWeb immer scannen", "litecoin_mweb_display_card": "MWEB -Karte anzeigen", "litecoin_mweb_scanning": "MWEB Scanning", "litecoin_mweb_settings": "MWEB -Einstellungen", + "litecoin_mweb_warning": "Durch die Verwendung von MWEB wird zunächst ~ 600 MB Daten heruntergeladen und kann je nach Netzwerkgeschwindigkeit bis zu 30 Minuten dauern. Diese ersten Daten werden nur einmal heruntergeladen und für alle Litecoin -Brieftaschen verfügbar", "litecoin_what_is_mweb": "Was ist MWeb?", "load_more": "Mehr laden", "loading_your_wallet": "Wallet wird geladen", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 9bb807645..f5aa56ac3 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Please enable Bluetooth to detect your Ledger", "light_theme": "Light", "litecoin_enable_mweb_sync": "Enable MWEB sync", - "litecoin_mweb": "Litecoin MWEB", + "litecoin_mweb": "MWEB", "litecoin_mweb_always_scan": "Set MWEB always scanning", "litecoin_mweb_display_card": "Show MWEB card", "litecoin_mweb_scanning": "MWEB Scanning", "litecoin_mweb_settings": "MWEB settings", + "litecoin_mweb_warning": "Using MWEB will initially download ~600MB of data, and may take up to 30 minutes depending on network speed. This initial data will only download once and be available for all Litecoin wallets", "litecoin_what_is_mweb": "What is MWEB?", "load_more": "Load more", "loading_your_wallet": "Loading your wallet", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index a7cf90fc7..5dcef8298 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Habilite Bluetooth para detectar su libro mayor", "light_theme": "Ligera", "litecoin_enable_mweb_sync": "Habilitar MWEB Sync", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Establecer mweb siempre escaneo", "litecoin_mweb_display_card": "Mostrar tarjeta MWEB", "litecoin_mweb_scanning": "Escaneo mweb", "litecoin_mweb_settings": "Configuración de MWEB", + "litecoin_mweb_warning": "El uso de MWEB inicialmente descargará ~ 600 MB de datos, y puede tomar hasta 30 minutos según la velocidad de la red. Estos datos iniciales solo se descargarán una vez y estarán disponibles para todas las billeteras de Litecoin", "litecoin_what_is_mweb": "¿Qué es mweb?", "load_more": "Carga más", "loading_your_wallet": "Cargando tu billetera", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 055715004..0836b6d19 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Veuillez activer Bluetooth pour détecter votre grand livre", "light_theme": "Clair", "litecoin_enable_mweb_sync": "Activer la synchronisation MWEB", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Définir MWEB Score Scanning", "litecoin_mweb_display_card": "Afficher la carte MWeb", "litecoin_mweb_scanning": "Scann mweb", "litecoin_mweb_settings": "Paramètres MWEB", + "litecoin_mweb_warning": "L'utilisation de MWEB téléchargera initialement ~ 600 Mo de données et peut prendre jusqu'à 30 minutes en fonction de la vitesse du réseau. Ces données initiales ne téléchargeront qu'une seule fois et seront disponibles pour tous les portefeuilles litecoin", "litecoin_what_is_mweb": "Qu'est-ce que MWEB?", "load_more": "Charger plus", "loading_your_wallet": "Chargement de votre portefeuille (wallet)", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 19d692195..6c524e850 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Da fatan za a kunna Bluetooth don gano Ledger ɗinku", "light_theme": "Haske", "litecoin_enable_mweb_sync": "Kunna Mweb Sync", - "litecoin_mweb": "Litcoin Mweb", + "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Saita Mweb koyaushe", "litecoin_mweb_display_card": "Nuna katin Mweb", "litecoin_mweb_scanning": "Mweb scanning", "litecoin_mweb_settings": "Saitunan Mweb", + "litecoin_mweb_warning": "Amfani da Mweb zai fara saukewa ~ 600MB na bayanai, kuma yana iya ɗaukar minti 30 dangane da saurin cibiyar sadarwa. Wannan bayanan farko zai saika saukarwa sau ɗaya kawai kuma a samu don duk wuraren shakatawa", "litecoin_what_is_mweb": "Menene Mweb?", "load_more": "Like more", "loading_your_wallet": "Ana loda walat ɗin ku", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index f1e24caf5..97656a8ec 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "कृपया अपने बहीखाने का पता लगाने के लिए ब्लूटूथ को सक्षम करें", "light_theme": "रोशनी", "litecoin_enable_mweb_sync": "MWEB सिंक सक्षम करें", - "litecoin_mweb": "लिटकोइन मेवेब", + "litecoin_mweb": "मावली", "litecoin_mweb_always_scan": "MWEB हमेशा स्कैनिंग सेट करें", "litecoin_mweb_display_card": "MWEB कार्ड दिखाएं", "litecoin_mweb_scanning": "MWEB स्कैनिंग", "litecoin_mweb_settings": "MWEB सेटिंग्स", + "litecoin_mweb_warning": "MWEB का उपयोग शुरू में ~ 600MB डेटा डाउनलोड करेगा, और नेटवर्क की गति के आधार पर 30 मिनट तक का समय लग सकता है। यह प्रारंभिक डेटा केवल एक बार डाउनलोड करेगा और सभी लिटकोइन वॉलेट के लिए उपलब्ध होगा", "litecoin_what_is_mweb": "MWEB क्या है?", "load_more": "और लोड करें", "loading_your_wallet": "अपना बटुआ लोड कर रहा है", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index a48dc9b5a..49988a669 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Omogućite Bluetooth da otkrije svoju knjigu", "light_theme": "Svijetla", "litecoin_enable_mweb_sync": "Omogući MWEB sinkronizaciju", - "litecoin_mweb": "Litecoin MWeb", + "litecoin_mweb": "MWeb", "litecoin_mweb_always_scan": "Postavite MWeb uvijek skeniranje", "litecoin_mweb_display_card": "Prikaži MWeb karticu", "litecoin_mweb_scanning": "MWEB skeniranje", "litecoin_mweb_settings": "Postavke MWEB -a", + "litecoin_mweb_warning": "Korištenje MWEB -a u početku će preuzeti ~ 600MB podataka, a može potrajati do 30 minuta, ovisno o brzini mreže. Ovi početni podaci preuzet će samo jednom i biti dostupni za sve Litecoin novčanike", "litecoin_what_is_mweb": "Što je MWEB?", "load_more": "Učitaj više", "loading_your_wallet": "Novčanik se učitava", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 85b110b0d..ef49e5c42 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Harap aktifkan Bluetooth untuk mendeteksi buku besar Anda", "light_theme": "Terang", "litecoin_enable_mweb_sync": "Aktifkan Sinkronisasi MWEB", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Atur mWeb selalu memindai", "litecoin_mweb_display_card": "Tunjukkan kartu mWeb", "litecoin_mweb_scanning": "Pemindaian MWEB", "litecoin_mweb_settings": "Pengaturan MWEB", + "litecoin_mweb_warning": "Menggunakan MWEB pada awalnya akan mengunduh ~ 600MB data, dan dapat memakan waktu hingga 30 menit tergantung pada kecepatan jaringan. Data awal ini hanya akan mengunduh sekali dan tersedia untuk semua dompet litecoin", "litecoin_what_is_mweb": "Apa itu MWEB?", "load_more": "Muat lebih banyak", "loading_your_wallet": "Memuat dompet Anda", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index fb75d8774..5dee2e6bd 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -350,11 +350,12 @@ "ledger_please_enable_bluetooth": "Si prega di consentire al Bluetooth di rilevare il libro mastro", "light_theme": "Bianco", "litecoin_enable_mweb_sync": "Abilita MWeb Sync", - "litecoin_mweb": "Litecoin MWeb", + "litecoin_mweb": "MWeb", "litecoin_mweb_always_scan": "Imposta MWeb per scansionare sempre", "litecoin_mweb_display_card": "Mostra la scheda MWeb", "litecoin_mweb_scanning": "Scansione MWeb", "litecoin_mweb_settings": "Impostazioni MWeb", + "litecoin_mweb_warning": "L'uso di MWeb inizialmente scaricherà ~ 600 MB di dati e potrebbe richiedere fino a 30 minuti a seconda della velocità di rete. Questi dati iniziali scaricheranno solo una volta e saranno disponibili per tutti i portafogli Litecoin", "litecoin_what_is_mweb": "Cos'è MWeb?", "load_more": "Carica di più", "loading_your_wallet": "Caricamento portafoglio", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 00d544400..c02ffcca7 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -350,11 +350,12 @@ "ledger_please_enable_bluetooth": "Bluetoothが元帳を検出できるようにしてください", "light_theme": "光", "litecoin_enable_mweb_sync": "MWEB同期を有効にします", - "litecoin_mweb": "litecoin mweb", + "litecoin_mweb": "mweb", "litecoin_mweb_always_scan": "MWEBを常にスキャンします", "litecoin_mweb_display_card": "MWEBカードを表示します", "litecoin_mweb_scanning": "MWEBスキャン", "litecoin_mweb_settings": "MWEB設定", + "litecoin_mweb_warning": "MWEBを使用すると、最初は〜600MBのデータをダウンロードし、ネットワーク速度に応じて最大30分かかる場合があります。この最初のデータは一度だけダウンロードされ、すべてのLitecoinウォレットで利用可能になります", "litecoin_what_is_mweb": "MWEBとは何ですか?", "load_more": "もっと読み込む", "loading_your_wallet": "ウォレットをロードしています", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 6268b60ea..9df0cc463 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Bluetooth가 원장을 감지 할 수 있도록하십시오", "light_theme": "빛", "litecoin_enable_mweb_sync": "mweb 동기화를 활성화합니다", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "mweb", "litecoin_mweb_always_scan": "mweb는 항상 스캔을 설정합니다", "litecoin_mweb_display_card": "mweb 카드를 보여주십시오", "litecoin_mweb_scanning": "mweb 스캔", "litecoin_mweb_settings": "mweb 설정", + "litecoin_mweb_warning": "MWEB를 사용하면 처음에는 ~ 600MB의 데이터를 다운로드하며 네트워크 속도에 따라 최대 30 분이 소요될 수 있습니다. 이 초기 데이터는 한 번만 다운로드하여 모든 조명 지갑에 사용할 수 있습니다.", "litecoin_what_is_mweb": "MWEB 란 무엇입니까?", "load_more": "더로드하십시오", "loading_your_wallet": "지갑 넣기", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index e2a8b1127..b03040721 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "သင်၏ Ledger ကိုရှာဖွေရန် Bluetooth ကိုဖွင့်ပါ", "light_theme": "အလင်း", "litecoin_enable_mweb_sync": "mweb စည်းညှိမှုကို enable", - "litecoin_mweb": "Litecoin Mweb", + "litecoin_mweb": "မင်္ဂလာပါ", "litecoin_mweb_always_scan": "Mweb အမြဲစကင်ဖတ်စစ်ဆေးပါ", "litecoin_mweb_display_card": "MweB ကဒ်ကိုပြပါ", "litecoin_mweb_scanning": "mweb scanning", "litecoin_mweb_settings": "Mweb ဆက်တင်များ", + "litecoin_mweb_warning": "MweB ကိုအသုံးပြုခြင်းသည်အစပိုင်းတွင် ~ 600MB ဒေတာများကို download လုပ်ပြီးကွန်ယက်အမြန်နှုန်းပေါ် မူတည်. မိနစ် 30 အထိကြာနိုင်သည်။ ဤကန ဦး ဒေတာကိုတစ်ကြိမ်သာ download လုပ်ပြီး litecoin Walkets အားလုံးအတွက်ရနိုင်သည်", "litecoin_what_is_mweb": "MweB ဆိုတာဘာလဲ။", "load_more": "ပိုပြီး load", "loading_your_wallet": "သင့်ပိုက်ဆံအိတ်ကို ဖွင့်နေသည်။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index c1953c129..2572c53ef 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Schakel Bluetooth in staat om uw grootboek te detecteren", "light_theme": "Licht", "litecoin_enable_mweb_sync": "MWEB SYNC inschakelen", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Stel mweb altijd op scannen", "litecoin_mweb_display_card": "Toon MWEB -kaart", "litecoin_mweb_scanning": "MWEB -scanning", "litecoin_mweb_settings": "MWEB -instellingen", + "litecoin_mweb_warning": "Het gebruik van MWeb downloadt in eerste instantie ~ 600 MB aan gegevens en kan tot 30 minuten duren, afhankelijk van de netwerksnelheid. Deze eerste gegevens worden slechts eenmaal gedownload en zijn beschikbaar voor alle Litecoin -portefeuilles", "litecoin_what_is_mweb": "Wat is Mweb?", "load_more": "Meer laden", "loading_your_wallet": "Uw portemonnee laden", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 9b9482e36..096fdeb95 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Włącz Bluetooth wykrywanie księgi", "light_theme": "Jasny", "litecoin_enable_mweb_sync": "Włącz synchronizację MWEB", - "litecoin_mweb": "Litecoin MWEB", + "litecoin_mweb": "MWEB", "litecoin_mweb_always_scan": "Ustaw MWEB zawsze skanowanie", "litecoin_mweb_display_card": "Pokaż kartę MWEB", "litecoin_mweb_scanning": "Skanowanie MWEB", "litecoin_mweb_settings": "Ustawienia MWEB", + "litecoin_mweb_warning": "Korzystanie z MWEB początkowo pobiera ~ 600 MB danych i może potrwać do 30 minut w zależności od prędkości sieci. Te początkowe dane pobierają tylko raz i będą dostępne dla wszystkich portfeli Litecoin", "litecoin_what_is_mweb": "Co to jest MWEB?", "load_more": "Załaduj więcej", "loading_your_wallet": "Ładowanie portfela", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 3fe816c72..290fedb95 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Ative o Bluetooth para detectar seu livro", "light_theme": "Luz", "litecoin_enable_mweb_sync": "Habilite MWEB Sync", - "litecoin_mweb": "Litecoin Mweb", + "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Definir mweb sempre digitalizando", "litecoin_mweb_display_card": "Mostre o cartão MWEB", "litecoin_mweb_scanning": "MWEB Scanning", "litecoin_mweb_settings": "Configurações do MWEB", + "litecoin_mweb_warning": "O uso do MWEB baixará inicialmente ~ 600 MB de dados e pode levar até 30 minutos, dependendo da velocidade da rede. Esses dados iniciais serão baixados apenas uma vez e estarão disponíveis para todas as carteiras Litecoin", "litecoin_what_is_mweb": "O que é MWeb?", "load_more": "Carregue mais", "loading_your_wallet": "Abrindo sua carteira", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index c986b81a0..0b75ff460 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Пожалуйста, включите Bluetooth обнаружить вашу бухгалтерскую книгу", "light_theme": "Светлая", "litecoin_enable_mweb_sync": "Включить MWEB Sync", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "Мвеб", "litecoin_mweb_always_scan": "Установить MWEB всегда сканирование", "litecoin_mweb_display_card": "Показать карту MWEB", "litecoin_mweb_scanning": "MWEB сканирование", "litecoin_mweb_settings": "Настройки MWEB", + "litecoin_mweb_warning": "Использование MWEB изначально загрузит ~ 600 МБ данных и может занять до 30 минут в зависимости от скорости сети. Эти начальные данные будут загружаться только один раз и будут доступны для всех кошельков Litecoin", "litecoin_what_is_mweb": "Что такое MWEB?", "load_more": "Загрузи больше", "loading_your_wallet": "Загрузка кошелька", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index f2f41d825..9eb9795d1 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "โปรดเปิดใช้งานบลูทู ธ ในการตรวจจับบัญชีแยกประเภทของคุณ", "light_theme": "สว่าง", "litecoin_enable_mweb_sync": "เปิดใช้งานการซิงค์ MWEB", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "mweb", "litecoin_mweb_always_scan": "ตั้งค่าการสแกน MWEB เสมอ", "litecoin_mweb_display_card": "แสดงการ์ด mweb", "litecoin_mweb_scanning": "การสแกน MWEB", "litecoin_mweb_settings": "การตั้งค่า MWEB", + "litecoin_mweb_warning": "การใช้ MWEB จะดาวน์โหลดข้อมูล ~ 600MB ในขั้นต้นและอาจใช้เวลาสูงสุด 30 นาทีขึ้นอยู่กับความเร็วเครือข่าย ข้อมูลเริ่มต้นนี้จะดาวน์โหลดได้เพียงครั้งเดียวและพร้อมใช้งานสำหรับกระเป๋าเงินทั้งหมดของ Litecoin", "litecoin_what_is_mweb": "MWEB คืออะไร?", "load_more": "โหลดมากขึ้น", "loading_your_wallet": "กำลังโหลดกระเป๋าของคุณ", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 2dd1bf6fc..d5903aaa6 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Mangyaring paganahin ang Bluetooth upang makita ang iyong ledger", "light_theme": "Ilaw", "litecoin_enable_mweb_sync": "Paganahin ang MWEB Sync", - "litecoin_mweb": "Litecoin Mweb", + "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Itakda ang MWeb na laging nag -scan", "litecoin_mweb_display_card": "Ipakita ang MWEB Card", "litecoin_mweb_scanning": "Pag -scan ng Mweb", "litecoin_mweb_settings": "Mga Setting ng Mweb", + "litecoin_mweb_warning": "Ang paggamit ng MWEB ay unang i -download ang ~ 600MB ng data, at maaaring tumagal ng hanggang sa 30 minuto depende sa bilis ng network. Ang paunang data na ito ay mag -download lamang ng isang beses at magagamit para sa lahat ng mga wallets ng Litecoin", "litecoin_what_is_mweb": "Ano ang MWEB?", "load_more": "Mag -load pa", "loading_your_wallet": "Naglo -load ng iyong pitaka", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 146a480d5..da457effc 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Defterinizi algılamak için lütfen Bluetooth'u etkinleştirin", "light_theme": "Aydınlık", "litecoin_enable_mweb_sync": "MWEB senkronizasyonunu etkinleştir", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "MWEB'i her zaman taramayı ayarlayın", "litecoin_mweb_display_card": "MWEB kartını göster", "litecoin_mweb_scanning": "MWEB taraması", "litecoin_mweb_settings": "MWEB ayarları", + "litecoin_mweb_warning": "MWEB kullanmak başlangıçta ~ 600MB veri indirir ve ağ hızına bağlı olarak 30 dakikaya kadar sürebilir. Bu ilk veriler yalnızca bir kez indirilecek ve tüm Litecoin cüzdanları için kullanılabilir olacak", "litecoin_what_is_mweb": "MWEB nedir?", "load_more": "Daha fazla yükle", "loading_your_wallet": "Cüzdanın yükleniyor", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 025f71389..43d896e48 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "Будь ласка, ввімкніть Bluetooth виявити свою книгу", "light_theme": "Світла", "litecoin_enable_mweb_sync": "Увімкнути MWEB SYNC", - "litecoin_mweb": "Litecoin mweb", + "litecoin_mweb": "Мвеб", "litecoin_mweb_always_scan": "Встановити mweb завжди сканувати", "litecoin_mweb_display_card": "Показати карту MWeb", "litecoin_mweb_scanning": "Сканування Mweb", "litecoin_mweb_settings": "Налаштування MWEB", + "litecoin_mweb_warning": "Використання MWEB спочатку завантажить ~ 600 Мб даних і може зайняти до 30 хвилин залежно від швидкості мережі. Ці початкові дані завантажуються лише один раз і будуть доступні для всіх гаманців Litecoin", "litecoin_what_is_mweb": "Що таке mweb?", "load_more": "Завантажити ще", "loading_your_wallet": "Завантаження гаманця", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 95a2301d1..68ef1a529 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "براہ کرم بلوٹوتھ کو اپنے لیجر کا پتہ لگانے کے لئے اہل بنائیں", "light_theme": "روشنی", "litecoin_enable_mweb_sync": "MWEB مطابقت پذیری کو فعال کریں", - "litecoin_mweb": "litcoin mweb", + "litecoin_mweb": "MWEB", "litecoin_mweb_always_scan": "MWEB ہمیشہ اسکیننگ سیٹ کریں", "litecoin_mweb_display_card": "MWEB کارڈ دکھائیں", "litecoin_mweb_scanning": "MWEB اسکیننگ", "litecoin_mweb_settings": "MWEB کی ترتیبات", + "litecoin_mweb_warning": "MWEB کا استعمال ابتدائی طور پر m 600mb ڈیٹا ڈاؤن لوڈ کرے گا ، اور نیٹ ورک کی رفتار کے لحاظ سے 30 منٹ تک کا وقت لگ سکتا ہے۔ یہ ابتدائی اعداد و شمار صرف ایک بار ڈاؤن لوڈ کریں گے اور تمام لیٹیکوئن بٹوے کے لئے دستیاب ہوں گے", "litecoin_what_is_mweb": "MWEB کیا ہے؟", "load_more": "مزید لوڈ کریں", "loading_your_wallet": "آپ کا بٹوہ لوڈ ہو رہا ہے۔", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index e593a866c..9d3ec2549 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -350,11 +350,12 @@ "ledger_please_enable_bluetooth": "Jọwọ jẹ ki Bluetooth lati rii iṣupọ rẹ", "light_theme": "Funfun bí eérú", "litecoin_enable_mweb_sync": "Mu ṣiṣẹmu MweB", - "litecoin_mweb": "Livecoin mweb", + "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Ṣeto mweb nigbagbogbo n ṣayẹwo", "litecoin_mweb_display_card": "Fihan kaadi Mweb", "litecoin_mweb_scanning": "Mweb scanning", "litecoin_mweb_settings": "Awọn eto Mweb", + "litecoin_mweb_warning": "Lilo Mweb yoo wa lakoko igbasilẹ ~ 600MB ti data, o le gba to iṣẹju 30 da lori iyara nẹtiwọọki. Awọn data akọkọ yii yoo ṣe igbasilẹ lẹẹkan si ki o wa fun gbogbo awọn Wolinkun LiveCooin", "litecoin_what_is_mweb": "Kini mweb?", "load_more": "Ẹru diẹ sii", "loading_your_wallet": "A ń ṣí àpamọ́wọ́ yín", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 943023c5c..d6b481978 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -349,11 +349,12 @@ "ledger_please_enable_bluetooth": "请启用蓝牙来检测您的分类帐", "light_theme": "艳丽", "litecoin_enable_mweb_sync": "启用MWEB同步", - "litecoin_mweb": "Litecoin Mweb", + "litecoin_mweb": "MWEB", "litecoin_mweb_always_scan": "设置MWEB总是扫描", "litecoin_mweb_display_card": "显示MWEB卡", "litecoin_mweb_scanning": "MWEB扫描", "litecoin_mweb_settings": "MWEB设置", + "litecoin_mweb_warning": "使用MWEB最初将下载约600MB的数据,并且最多可能需要30分钟的时间,具体取决于网络速度。此初始数据只能下载一次,并适用于所有莱特币钱包", "litecoin_what_is_mweb": "什么是MWEB?", "load_more": "装载更多", "loading_your_wallet": "加载您的钱包", From bdc55064c9bf2606f2499ee877a5504ebfea9357 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 15:11:21 -0700 Subject: [PATCH 096/100] translation fixes --- res/values/strings_ar.arb | 2 +- res/values/strings_bg.arb | 2 +- res/values/strings_cs.arb | 2 +- res/values/strings_de.arb | 2 +- res/values/strings_en.arb | 2 +- res/values/strings_es.arb | 2 +- res/values/strings_fr.arb | 2 +- res/values/strings_ha.arb | 2 +- res/values/strings_hi.arb | 2 +- res/values/strings_hr.arb | 2 +- res/values/strings_id.arb | 2 +- res/values/strings_it.arb | 2 +- res/values/strings_ja.arb | 2 +- res/values/strings_ko.arb | 2 +- res/values/strings_my.arb | 2 +- res/values/strings_nl.arb | 2 +- res/values/strings_pl.arb | 2 +- res/values/strings_pt.arb | 2 +- res/values/strings_ru.arb | 2 +- res/values/strings_th.arb | 2 +- res/values/strings_tl.arb | 2 +- res/values/strings_tr.arb | 2 +- res/values/strings_uk.arb | 2 +- res/values/strings_ur.arb | 2 +- res/values/strings_yo.arb | 2 +- res/values/strings_zh.arb | 2 +- 26 files changed, 26 insertions(+), 26 deletions(-) diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 1f3c5e123..bf3b7d4de 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "يرجى التأكد", "ledger_please_enable_bluetooth": "يرجى تمكين البلوتوث للكشف عن دفتر الأستاذ الخاص بك", "light_theme": "فاتح", - "litecoin_enable_mweb_sync": "تمكين MWEB المزامنة", + "litecoin_enable_mweb_sync": "تمكين MWEB المسح الضوئي", "litecoin_mweb": "mweb", "litecoin_mweb_always_scan": "اضبط MWEB دائمًا على المسح الضوئي", "litecoin_mweb_display_card": "عرض بطاقة mweb", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 816b768ff..6bca795f8 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Моля, уверете се, че сте отворили правилното приложение на вашата книга", "ledger_please_enable_bluetooth": "Моля, активирайте Bluetooth да открие вашата книга", "light_theme": "Светло", - "litecoin_enable_mweb_sync": "Активиране на MWEB Sync", + "litecoin_enable_mweb_sync": "Активирайте сканирането на MWeb", "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Задайте MWeb винаги сканиране", "litecoin_mweb_display_card": "Показване на MWEB карта", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index af0fe73fe..e40c6e8c3 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Ujistěte se, že se na své knize otevřete správnou aplikaci", "ledger_please_enable_bluetooth": "Umožněte prosím Bluetooth detekovat vaši knihu", "light_theme": "Světlý", - "litecoin_enable_mweb_sync": "Povolit synchronizaci MWeb", + "litecoin_enable_mweb_sync": "Povolit skenování MWeb", "litecoin_mweb": "MWeb", "litecoin_mweb_always_scan": "Nastavit MWeb vždy skenování", "litecoin_mweb_display_card": "Zobrazit kartu MWeb", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 669ace926..293c59ad2 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Bitte stellen Sie sicher, dass Sie die richtige App auf Ihrem Ledger geöffnet haben", "ledger_please_enable_bluetooth": "Bitte aktivieren Sie Bluetooth um sich mit Ihren Ledger zu verbinden.", "light_theme": "Hell", - "litecoin_enable_mweb_sync": "Aktivieren Sie die MWEB -Synchronisierung", + "litecoin_enable_mweb_sync": "Aktivieren Sie das MWEB -Scannen", "litecoin_mweb": "MWeb", "litecoin_mweb_always_scan": "Setzen Sie MWeb immer scannen", "litecoin_mweb_display_card": "MWEB -Karte anzeigen", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index f5aa56ac3..10938e7a4 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Please make sure you opend the right app on your ledger", "ledger_please_enable_bluetooth": "Please enable Bluetooth to detect your Ledger", "light_theme": "Light", - "litecoin_enable_mweb_sync": "Enable MWEB sync", + "litecoin_enable_mweb_sync": "Enable MWEB scanning", "litecoin_mweb": "MWEB", "litecoin_mweb_always_scan": "Set MWEB always scanning", "litecoin_mweb_display_card": "Show MWEB card", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 5dcef8298..a5eafac06 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Por favor, asegúrese de abrir la aplicación correcta en su libro mayor.", "ledger_please_enable_bluetooth": "Habilite Bluetooth para detectar su libro mayor", "light_theme": "Ligera", - "litecoin_enable_mweb_sync": "Habilitar MWEB Sync", + "litecoin_enable_mweb_sync": "Habilitar el escaneo mweb", "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Establecer mweb siempre escaneo", "litecoin_mweb_display_card": "Mostrar tarjeta MWEB", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 0836b6d19..348eb905f 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Veuillez vous assurer d'ouvrir la bonne application sur votre grand livre", "ledger_please_enable_bluetooth": "Veuillez activer Bluetooth pour détecter votre grand livre", "light_theme": "Clair", - "litecoin_enable_mweb_sync": "Activer la synchronisation MWEB", + "litecoin_enable_mweb_sync": "Activer la numérisation MWEB", "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Définir MWEB Score Scanning", "litecoin_mweb_display_card": "Afficher la carte MWeb", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 6c524e850..d435dfcd1 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Da fatan za a tabbata kun yi amfani da app ɗin dama akan dillalarku", "ledger_please_enable_bluetooth": "Da fatan za a kunna Bluetooth don gano Ledger ɗinku", "light_theme": "Haske", - "litecoin_enable_mweb_sync": "Kunna Mweb Sync", + "litecoin_enable_mweb_sync": "Kunna binciken Mweb", "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Saita Mweb koyaushe", "litecoin_mweb_display_card": "Nuna katin Mweb", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 97656a8ec..54c774dac 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "कृपया सुनिश्चित करें कि आप अपने लेजर पर सही ऐप को खोलते हैं", "ledger_please_enable_bluetooth": "कृपया अपने बहीखाने का पता लगाने के लिए ब्लूटूथ को सक्षम करें", "light_theme": "रोशनी", - "litecoin_enable_mweb_sync": "MWEB सिंक सक्षम करें", + "litecoin_enable_mweb_sync": "MWEB स्कैनिंग सक्षम करें", "litecoin_mweb": "मावली", "litecoin_mweb_always_scan": "MWEB हमेशा स्कैनिंग सेट करें", "litecoin_mweb_display_card": "MWEB कार्ड दिखाएं", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 49988a669..bd8645c70 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Obavezno obavezno otvorite pravu aplikaciju na knjizi", "ledger_please_enable_bluetooth": "Omogućite Bluetooth da otkrije svoju knjigu", "light_theme": "Svijetla", - "litecoin_enable_mweb_sync": "Omogući MWEB sinkronizaciju", + "litecoin_enable_mweb_sync": "Omogućite MWEB skeniranje", "litecoin_mweb": "MWeb", "litecoin_mweb_always_scan": "Postavite MWeb uvijek skeniranje", "litecoin_mweb_display_card": "Prikaži MWeb karticu", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index ef49e5c42..6bfe5f4fe 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Pastikan Anda membuka aplikasi yang tepat di buku besar Anda", "ledger_please_enable_bluetooth": "Harap aktifkan Bluetooth untuk mendeteksi buku besar Anda", "light_theme": "Terang", - "litecoin_enable_mweb_sync": "Aktifkan Sinkronisasi MWEB", + "litecoin_enable_mweb_sync": "Aktifkan pemindaian MWEB", "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Atur mWeb selalu memindai", "litecoin_mweb_display_card": "Tunjukkan kartu mWeb", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 5dee2e6bd..949a78a3e 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -349,7 +349,7 @@ "ledger_error_wrong_app": "Assicurati di aprire l'app giusta sul libro mastro", "ledger_please_enable_bluetooth": "Si prega di consentire al Bluetooth di rilevare il libro mastro", "light_theme": "Bianco", - "litecoin_enable_mweb_sync": "Abilita MWeb Sync", + "litecoin_enable_mweb_sync": "Abilita la scansione MWeb", "litecoin_mweb": "MWeb", "litecoin_mweb_always_scan": "Imposta MWeb per scansionare sempre", "litecoin_mweb_display_card": "Mostra la scheda MWeb", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index c02ffcca7..8e3351154 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -349,7 +349,7 @@ "ledger_error_wrong_app": "元帳に適切なアプリを開始するようにしてください", "ledger_please_enable_bluetooth": "Bluetoothが元帳を検出できるようにしてください", "light_theme": "光", - "litecoin_enable_mweb_sync": "MWEB同期を有効にします", + "litecoin_enable_mweb_sync": "MWEBスキャンを有効にします", "litecoin_mweb": "mweb", "litecoin_mweb_always_scan": "MWEBを常にスキャンします", "litecoin_mweb_display_card": "MWEBカードを表示します", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 9df0cc463..8137827df 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "원장에서 올바른 앱을 반대하는지 확인하십시오.", "ledger_please_enable_bluetooth": "Bluetooth가 원장을 감지 할 수 있도록하십시오", "light_theme": "빛", - "litecoin_enable_mweb_sync": "mweb 동기화를 활성화합니다", + "litecoin_enable_mweb_sync": "mweb 스캔을 활성화합니다", "litecoin_mweb": "mweb", "litecoin_mweb_always_scan": "mweb는 항상 스캔을 설정합니다", "litecoin_mweb_display_card": "mweb 카드를 보여주십시오", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index b03040721..e69ecea52 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "ကျေးဇူးပြု. သင့်လက်ျာအက်ပ်ကိုသင်၏ Ledger တွင်ဖွင့်ရန်သေချာစေပါ", "ledger_please_enable_bluetooth": "သင်၏ Ledger ကိုရှာဖွေရန် Bluetooth ကိုဖွင့်ပါ", "light_theme": "အလင်း", - "litecoin_enable_mweb_sync": "mweb စည်းညှိမှုကို enable", + "litecoin_enable_mweb_sync": "mweb scanning ဖွင့်ပါ", "litecoin_mweb": "မင်္ဂလာပါ", "litecoin_mweb_always_scan": "Mweb အမြဲစကင်ဖတ်စစ်ဆေးပါ", "litecoin_mweb_display_card": "MweB ကဒ်ကိုပြပါ", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 2572c53ef..d03f55a1b 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Zorg ervoor dat u de juiste app op uw grootboek opent", "ledger_please_enable_bluetooth": "Schakel Bluetooth in staat om uw grootboek te detecteren", "light_theme": "Licht", - "litecoin_enable_mweb_sync": "MWEB SYNC inschakelen", + "litecoin_enable_mweb_sync": "MWEB -scanning inschakelen", "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Stel mweb altijd op scannen", "litecoin_mweb_display_card": "Toon MWEB -kaart", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 096fdeb95..1f472c0d7 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Upewnij się, że opisz odpowiednią aplikację na swojej księdze", "ledger_please_enable_bluetooth": "Włącz Bluetooth wykrywanie księgi", "light_theme": "Jasny", - "litecoin_enable_mweb_sync": "Włącz synchronizację MWEB", + "litecoin_enable_mweb_sync": "Włącz skanowanie MWEB", "litecoin_mweb": "MWEB", "litecoin_mweb_always_scan": "Ustaw MWEB zawsze skanowanie", "litecoin_mweb_display_card": "Pokaż kartę MWEB", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 290fedb95..2855359cf 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Por favor, certifique -se de optar pelo aplicativo certo no seu livro", "ledger_please_enable_bluetooth": "Ative o Bluetooth para detectar seu livro", "light_theme": "Luz", - "litecoin_enable_mweb_sync": "Habilite MWEB Sync", + "litecoin_enable_mweb_sync": "Ativar digitalização do MWEB", "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Definir mweb sempre digitalizando", "litecoin_mweb_display_card": "Mostre o cartão MWEB", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 0b75ff460..d591cadab 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Пожалуйста, убедитесь, что вы предлагаете правильное приложение в своей бухгалтерской книге", "ledger_please_enable_bluetooth": "Пожалуйста, включите Bluetooth обнаружить вашу бухгалтерскую книгу", "light_theme": "Светлая", - "litecoin_enable_mweb_sync": "Включить MWEB Sync", + "litecoin_enable_mweb_sync": "Включить MWEB сканирование", "litecoin_mweb": "Мвеб", "litecoin_mweb_always_scan": "Установить MWEB всегда сканирование", "litecoin_mweb_display_card": "Показать карту MWEB", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 9eb9795d1..b3a411011 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "โปรดตรวจสอบให้แน่ใจว่าคุณเปิดแอพที่เหมาะสมในบัญชีแยกประเภทของคุณ", "ledger_please_enable_bluetooth": "โปรดเปิดใช้งานบลูทู ธ ในการตรวจจับบัญชีแยกประเภทของคุณ", "light_theme": "สว่าง", - "litecoin_enable_mweb_sync": "เปิดใช้งานการซิงค์ MWEB", + "litecoin_enable_mweb_sync": "เปิดใช้งานการสแกน MWEB", "litecoin_mweb": "mweb", "litecoin_mweb_always_scan": "ตั้งค่าการสแกน MWEB เสมอ", "litecoin_mweb_display_card": "แสดงการ์ด mweb", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index d5903aaa6..581e9527a 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Mangyaring tiyaking pinipili mo ang tamang app sa iyong ledger", "ledger_please_enable_bluetooth": "Mangyaring paganahin ang Bluetooth upang makita ang iyong ledger", "light_theme": "Ilaw", - "litecoin_enable_mweb_sync": "Paganahin ang MWEB Sync", + "litecoin_enable_mweb_sync": "Paganahin ang pag -scan ng MWeb", "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Itakda ang MWeb na laging nag -scan", "litecoin_mweb_display_card": "Ipakita ang MWEB Card", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index da457effc..9d070e9bc 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Lütfen defterinizde doğru uygulamayı açtığınızdan emin olun", "ledger_please_enable_bluetooth": "Defterinizi algılamak için lütfen Bluetooth'u etkinleştirin", "light_theme": "Aydınlık", - "litecoin_enable_mweb_sync": "MWEB senkronizasyonunu etkinleştir", + "litecoin_enable_mweb_sync": "MWEB taramasını etkinleştir", "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "MWEB'i her zaman taramayı ayarlayın", "litecoin_mweb_display_card": "MWEB kartını göster", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 43d896e48..8102252e3 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "Будь ласка, переконайтеся, що ви відкриваєте потрібну програму на своїй книзі", "ledger_please_enable_bluetooth": "Будь ласка, ввімкніть Bluetooth виявити свою книгу", "light_theme": "Світла", - "litecoin_enable_mweb_sync": "Увімкнути MWEB SYNC", + "litecoin_enable_mweb_sync": "Увімкнути сканування MWEB", "litecoin_mweb": "Мвеб", "litecoin_mweb_always_scan": "Встановити mweb завжди сканувати", "litecoin_mweb_display_card": "Показати карту MWeb", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 68ef1a529..33b173144 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "براہ کرم یقینی بنائیں کہ آپ اپنے لیجر پر صحیح ایپ کو کھولتے ہیں", "ledger_please_enable_bluetooth": "براہ کرم بلوٹوتھ کو اپنے لیجر کا پتہ لگانے کے لئے اہل بنائیں", "light_theme": "روشنی", - "litecoin_enable_mweb_sync": "MWEB مطابقت پذیری کو فعال کریں", + "litecoin_enable_mweb_sync": "MWEB اسکیننگ کو فعال کریں", "litecoin_mweb": "MWEB", "litecoin_mweb_always_scan": "MWEB ہمیشہ اسکیننگ سیٹ کریں", "litecoin_mweb_display_card": "MWEB کارڈ دکھائیں", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 9d3ec2549..0090246e7 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -349,7 +349,7 @@ "ledger_error_wrong_app": "Jọwọ rii daju pe iwọ yoo sọ app ti o tọ loju omi rẹ", "ledger_please_enable_bluetooth": "Jọwọ jẹ ki Bluetooth lati rii iṣupọ rẹ", "light_theme": "Funfun bí eérú", - "litecoin_enable_mweb_sync": "Mu ṣiṣẹmu MweB", + "litecoin_enable_mweb_sync": "Mu mweb ọlọjẹ", "litecoin_mweb": "Mweb", "litecoin_mweb_always_scan": "Ṣeto mweb nigbagbogbo n ṣayẹwo", "litecoin_mweb_display_card": "Fihan kaadi Mweb", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index d6b481978..6ad8b92fc 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -348,7 +348,7 @@ "ledger_error_wrong_app": "请确保您在分类帐中操作正确的应用程序", "ledger_please_enable_bluetooth": "请启用蓝牙来检测您的分类帐", "light_theme": "艳丽", - "litecoin_enable_mweb_sync": "启用MWEB同步", + "litecoin_enable_mweb_sync": "启用MWEB扫描", "litecoin_mweb": "MWEB", "litecoin_mweb_always_scan": "设置MWEB总是扫描", "litecoin_mweb_display_card": "显示MWEB卡", From 14cb65b8c95ff5d02b43bee6b99f80363bae08b6 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 16:26:39 -0700 Subject: [PATCH 097/100] [skip ci] addressbook updates --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 84d28dc19..c90d7a1dd 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -324,7 +324,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { ''; Future getAddressAsync( - {required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) async => + {required int index, + required bitcoin.HDWallet hd, + BitcoinAddressType? addressType}) async => getAddress(index: index, hd: hd, addressType: addressType); @override @@ -381,6 +383,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { addressesMap[address] = 'Active - P2WSH'; } + final lastMweb = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb)); + if (lastMweb.address != address) { + addressesMap[lastP2wsh.address] = 'MWEB'; + } else { + addressesMap[address] = 'Active - MWEB'; + } + silentAddresses.forEach((addressRecord) { if (addressRecord.type != SilentPaymentsAddresType.p2sp || addressRecord.isHidden) { return; @@ -546,11 +556,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { void _validateAddresses() { allAddresses.forEach((element) { - if (!element.isHidden && element.address != - getAddress(index: element.index, hd: mainHd, addressType: element.type)) { + if (!element.isHidden && + element.address != + getAddress(index: element.index, hd: mainHd, addressType: element.type)) { element.isHidden = true; - } else if (element.isHidden && element.address != - getAddress(index: element.index, hd: sideHd, addressType: element.type)) { + } else if (element.isHidden && + element.address != + getAddress(index: element.index, hd: sideHd, addressType: element.type)) { element.isHidden = false; } }); From 70764c3dd8179cbbfc39091c1b043fccd7a711ab Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 16:27:52 -0700 Subject: [PATCH 098/100] fix popup --- lib/view_model/dashboard/dashboard_view_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index d34d0bfe3..98bfb8f5b 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -372,7 +372,7 @@ abstract class DashboardViewModelBase with Store { bool mwebScanningActive = false; @computed - bool get hasEnabledMwebBefore => !settingsStore.disableBuy && hasBuyProviders; + bool get hasEnabledMwebBefore => !settingsStore.hasEnabledMwebBefore; @action void setMwebScanningActive(bool active) { From 29a96a7a59d86a0ee21190fc032166426d34cc69 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 16:28:27 -0700 Subject: [PATCH 099/100] fix popup2 --- lib/view_model/dashboard/dashboard_view_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 98bfb8f5b..d3527f997 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -372,7 +372,7 @@ abstract class DashboardViewModelBase with Store { bool mwebScanningActive = false; @computed - bool get hasEnabledMwebBefore => !settingsStore.hasEnabledMwebBefore; + bool get hasEnabledMwebBefore => settingsStore.hasEnabledMwebBefore; @action void setMwebScanningActive(bool active) { From 23199cd8b9317c120ddc74cafddd26e88ba35d84 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 22 Jul 2024 17:34:23 -0700 Subject: [PATCH 100/100] fix litecoin address book --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 139 ++++++++++-------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index c90d7a1dd..f2173adf6 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -329,6 +329,65 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { BitcoinAddressType? addressType}) async => getAddress(index: index, hd: hd, addressType: addressType); + void addBitcoinAddressTypes() { + final lastP2wpkh = _addresses + .where((addressRecord) => + _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh)) + .toList() + .last; + if (lastP2wpkh.address != address) { + addressesMap[lastP2wpkh.address] = 'P2WPKH'; + } else { + addressesMap[address] = 'Active - P2WPKH'; + } + + final lastP2pkh = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, P2pkhAddressType.p2pkh)); + if (lastP2pkh.address != address) { + addressesMap[lastP2pkh.address] = 'P2PKH'; + } else { + addressesMap[address] = 'Active - P2PKH'; + } + + final lastP2sh = _addresses.firstWhere((addressRecord) => + _isUnusedReceiveAddressByType(addressRecord, P2shAddressType.p2wpkhInP2sh)); + if (lastP2sh.address != address) { + addressesMap[lastP2sh.address] = 'P2SH'; + } else { + addressesMap[address] = 'Active - P2SH'; + } + + final lastP2tr = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2tr)); + if (lastP2tr.address != address) { + addressesMap[lastP2tr.address] = 'P2TR'; + } else { + addressesMap[address] = 'Active - P2TR'; + } + + final lastP2wsh = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wsh)); + if (lastP2wsh.address != address) { + addressesMap[lastP2wsh.address] = 'P2WSH'; + } else { + addressesMap[address] = 'Active - P2WSH'; + } + + silentAddresses.forEach((addressRecord) { + if (addressRecord.type != SilentPaymentsAddresType.p2sp || addressRecord.isHidden) { + return; + } + + if (addressRecord.address != address) { + addressesMap[addressRecord.address] = addressRecord.name.isEmpty + ? "Silent Payments" + : "Silent Payments - " + addressRecord.name; + } else { + addressesMap[address] = 'Active - Silent Payments'; + } + }); + } + @override Future updateAddressesInBox() async { try { @@ -340,70 +399,30 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { allAddressesMap[addressRecord.address] = addressRecord.name; }); - final lastP2wpkh = _addresses - .where((addressRecord) => - _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh)) - .toList() - .last; - if (lastP2wpkh.address != address) { - addressesMap[lastP2wpkh.address] = 'P2WPKH'; - } else { - addressesMap[address] = 'Active - P2WPKH'; + if (walletInfo.type == WalletType.bitcoin) { + addBitcoinAddressTypes(); } - final lastP2pkh = _addresses.firstWhere( - (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, P2pkhAddressType.p2pkh)); - if (lastP2pkh.address != address) { - addressesMap[lastP2pkh.address] = 'P2PKH'; - } else { - addressesMap[address] = 'Active - P2PKH'; - } - - final lastP2sh = _addresses.firstWhere((addressRecord) => - _isUnusedReceiveAddressByType(addressRecord, P2shAddressType.p2wpkhInP2sh)); - if (lastP2sh.address != address) { - addressesMap[lastP2sh.address] = 'P2SH'; - } else { - addressesMap[address] = 'Active - P2SH'; - } - - final lastP2tr = _addresses.firstWhere( - (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2tr)); - if (lastP2tr.address != address) { - addressesMap[lastP2tr.address] = 'P2TR'; - } else { - addressesMap[address] = 'Active - P2TR'; - } - - final lastP2wsh = _addresses.firstWhere( - (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wsh)); - if (lastP2wsh.address != address) { - addressesMap[lastP2wsh.address] = 'P2WSH'; - } else { - addressesMap[address] = 'Active - P2WSH'; - } - - final lastMweb = _addresses.firstWhere( - (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb)); - if (lastMweb.address != address) { - addressesMap[lastP2wsh.address] = 'MWEB'; - } else { - addressesMap[address] = 'Active - MWEB'; - } - - silentAddresses.forEach((addressRecord) { - if (addressRecord.type != SilentPaymentsAddresType.p2sp || addressRecord.isHidden) { - return; - } - - if (addressRecord.address != address) { - addressesMap[addressRecord.address] = addressRecord.name.isEmpty - ? "Silent Payments" - : "Silent Payments - " + addressRecord.name; + if (walletInfo.type == WalletType.litecoin) { + final lastP2wpkh = _addresses + .where((addressRecord) => + _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh)) + .toList() + .last; + if (lastP2wpkh.address != address) { + addressesMap[lastP2wpkh.address] = 'P2WPKH'; } else { - addressesMap[address] = 'Active - Silent Payments'; + addressesMap[address] = 'Active - P2WPKH'; } - }); + + final lastMweb = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb)); + if (lastMweb.address != address) { + addressesMap[lastMweb.address] = 'MWEB'; + } else { + addressesMap[address] = 'Active - MWEB'; + } + } await saveAddressesInBox(); } catch (e) {