From 376632e22dcfb8ec05f9c489934f880f9d28bae7 Mon Sep 17 00:00:00 2001 From: Hector Chu Date: Sat, 20 Apr 2024 10:07:50 +0100 Subject: [PATCH 001/203] 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 fb941f6579..1319e4ad49 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 fc4835e812..46b1b4315b 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 37684a16ac..57eec7d00b 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 b80c9eb857..cf9a766a41 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/203] 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 d2379d5a5a..f218ddd5d7 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 ee3b0e6289..7f2066c30d 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 50cd432c06..20851ea866 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 632a3140a6..033d1f84e4 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 0000000000..8959f5d706 --- /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 0000000000..6063039142 --- /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 0000000000..41cc7d8192 --- /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 0000000000..ba75c69f7f --- /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 0000000000..8a839b1ecc --- /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 0000000000..a5744c1cfb --- /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 0000000000..161bdcdaf8 --- /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 0000000000..7e67b98ad7 --- /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 0000000000..88fbd66fb3 --- /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 0000000000..fd3746a8c6 --- /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 0000000000..394b607aba --- /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 0000000000..baa81332f6 --- /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 0000000000..0c885071e3 --- /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 0000000000..e69de29bb2 diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift new file mode 100644 index 0000000000..d742aab618 --- /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 0000000000..cd0600fa15 --- /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 0000000000..e73887aaf5 --- /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 0000000000..112dcfaa71 --- /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 0000000000..ce518402f6 --- /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 0000000000..8d139d7eb7 --- /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 0000000000..6bc48cfdfe --- /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 0000000000..9c0dabd401 --- /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 0000000000..8fadcced9d --- /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 0000000000..5416963d88 --- /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 0000000000..bb0523758b --- /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 0000000000..3677659a92 --- /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 75a78404fd..e309e31425 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/203] 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 f218ddd5d7..628391eede 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 8959f5d706..96486fd930 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 161bdcdaf8..881f3d95ca 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 394b607aba..cf194417b2 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 e73887aaf5..fb3960ab0e 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 112dcfaa71..cc880c6df6 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 ce518402f6..974e072848 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/203] 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 628391eede..b6b996b27e 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 7f2066c30d..ee3b0e6289 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 fb3960ab0e..a54295673c 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 5416963d88..cfe43c70b9 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/203] 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 2d2339a41d..714f1da909 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 4a76ee5dd1..17e9880e9f 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 c43d4988a3..6a7a9fd18e 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 993d17933a..fead1de52f 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 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 - String getAddress( - {required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) => - generateP2WPKHAddress(hd: hd, index: index, network: network); + 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 20851ea866..f93365d897 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 033d1f84e4..9c3c5d7510 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 37827f1bae..e491cea321 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 707f1157bc..77c940ac47 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 3c77cad48c..0e51f759d5 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 1e4726eeea..17e17842ca 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 20980f5f0c..5f3b2b9c26 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/203] 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 b6b996b27e..1608a3dba8 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 fead1de52f..8875efa187 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 17e17842ca..472918f4a8 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/203] 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 8875efa187..e1ee81b311 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/203] 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 0553170ccf..359f9b36d4 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 1608a3dba8..e71a68dee1 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/203] 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 359f9b36d4..d94dccbbac 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 f980bd8842..00315319c6 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 e71a68dee1..8fa5af63b7 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/203] 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 8fa5af63b7..547a2dda52 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/203] 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 17e9880e9f..6794d93c59 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 547a2dda52..1ff5b5ac9b 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/203] 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 6794d93c59..a2839b1e33 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 1ff5b5ac9b..35bb143f31 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 f93365d897..f081d7e87c 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 9c3c5d7510..4615f12193 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/203] 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 35bb143f31..2da4c407ca 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/203] 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 d94dccbbac..0553170ccf 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 a2839b1e33..b250698637 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 2da4c407ca..7506e368a3 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 f081d7e87c..f93365d897 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 4615f12193..9c3c5d7510 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 8d139d7eb7..d0dd486c04 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/203] 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 b250698637..37937f45c0 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 7506e368a3..adce6e6b74 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 e1ee81b311..226bd1fc62 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 967cf9bf0b..bb69d4ecf8 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/203] Fee estimation --- cw_bitcoin/lib/electrum.dart | 27 +++++++--- 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, 101 insertions(+), 31 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 0553170ccf..2b3f490a39 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; - } - - return null; - }); + BehaviorSubject>? tipListener; + int? currentTip; + + 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 37937f45c0..1f5c143197 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 adce6e6b74..b846756691 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 cf194417b2..1dfbe12e5a 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 a54295673c..f16d451d9d 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/203] 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 1dfbe12e5a..d3d4140ef3 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/203] 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 1f5c143197..586c73ed8a 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 b846756691..c9bee05f11 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/203] 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 586c73ed8a..281255e531 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 c9bee05f11..bd69b9e05b 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/203] 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 281255e531..23a15c2804 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/203] 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 bd69b9e05b..fce466b1e1 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/203] 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 fce466b1e1..a07875c1f4 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 a59b4f4293..46f152708a 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/203] 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 bb0523758b..0000000000 --- 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 3677659a92..0000000000 --- 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/203] 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 baa81332f6..0000000000 --- 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/203] 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 a07875c1f4..483d86645b 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 46f152708a..69bdaba700 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/203] 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 483d86645b..ea5176a007 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/203] 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 ea5176a007..6444e217d2 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/203] 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 23a15c2804..ffb6370045 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 6444e217d2..6cabcc34e7 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 69bdaba700..20cb1ebc6e 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/203] 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 6cabcc34e7..4c589b134c 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/203] 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 4c589b134c..703571bd5e 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/203] 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 703571bd5e..fba4bbc4ab 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/203] [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 85b2513e58..0dde34370e 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 1dac9367cb..69b02cd766 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 0318e3d36e..88c1cee167 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 ee88644b69..9f4857f996 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 a2be113e4a..7b54658cb4 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/203] [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 69b02cd766..67e5ceed88 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 7b54658cb4..a7133b1c77 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/203] 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 d742aab618..a5fa63c5ff 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 cd0600fa15..4a1903bae8 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 8ed46a0288..365a13dfb9 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 c87d15a335..5e31d3d342 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/203] small fix --- cw_bitcoin/lib/litecoin_wallet.dart | 260 +++++++++++++++------------- 1 file changed, 144 insertions(+), 116 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 86d62ef055..6853d6a84b 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/203] 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 50740d27ea..3f2307bf1a 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 479f80e1fa..235f98e614 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/203] 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 7578f39a0a..b7005d8cd7 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 3f2307bf1a..8b15b97af8 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 235f98e614..d5c70bd446 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 8f577c53c8..031d9419d7 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 635177778a..367a025cbb 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 d17964b552..4feefbc914 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/203] [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 8b15b97af8..79a98b37d9 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 d5c70bd446..2b670600e5 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/203] 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 ef2ab28666..8e72bc7b00 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 2b670600e5..d4adb10351 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/203] 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 d4adb10351..61f75d2113 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 4da616a79c..6432c484bd 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 0000000000..02f162a372 --- /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 d3d4140ef3..fa4fc909f1 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 a5fa63c5ff..fa1ac39adc 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 0000000000..412bef9735 --- /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/203] 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 412bef9735..8a5e6813f6 --- 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/203] 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 61f75d2113..0e1cf6f068 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 02f162a372..f8dfab3956 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 7e56f35e49..eb7bca5a66 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/203] [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 0e1cf6f068..5a58196e14 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 f1b05c5bfa..d405ad58f8 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 ab05621acd..dfc032c242 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/203] 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 841ea570db..7c8329328f 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 fd19edfc7b..f9079b7499 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 5a58196e14..6a78af0f2d 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 d405ad58f8..a1089f4736 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 dfc032c242..4217d35b49 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 c59ae4ad02..9f32c90678 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 d85680cc8d..1bfc7c0cdc 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 b59dd15920..fd4f1ede26 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 dcc81c0a00..3d8cf39f39 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/203] 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 7c8329328f..331a66279e 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/203] 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 331a66279e..ac1831718f 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/203] 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 ac1831718f..2d1b06775c 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/203] 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 2d1b06775c..3184f64320 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/203] 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 3184f64320..6a43fa6e73 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/203] 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 6a43fa6e73..3b32db9d4b 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/203] 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 3b32db9d4b..1756b7ad94 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/203] 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 1756b7ad94..192b13bb5a 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/203] 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 192b13bb5a..d7dffce46c 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/203] 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 d7dffce46c..d1d7e18178 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/203] 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 d1d7e18178..c84d9faad8 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/203] 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 c84d9faad8..fa06358c8c 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/203] 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 fa06358c8c..6e1cf6d71e 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/203] ??? --- .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 6e1cf6d71e..1c79221a79 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/203] ??? 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 1c79221a79..32809fc37d 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/203] 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 32809fc37d..2d3830e5cf 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/203] 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 6a78af0f2d..8fe0d22958 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 8a5e6813f6..d94f06e035 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/203] 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 8fe0d22958..3e11d27f6e 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/203] 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 3e11d27f6e..5b6513409b 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; - - if (!(tx.outputAddresses?.contains(utxo.address) ?? false)) { - tx.outputAddresses?.add(utxo.address); - isNew = true; - } + await handleIncoming(utxo, stub); - 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 a1089f4736..8b89fb6f94 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/203] 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 5b6513409b..0c5078417a 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/203] 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 0c5078417a..0fb8faf319 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/203] 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 0fb8faf319..e954d0afa3 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 e2d8469f10..5b6e6140f7 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/203] 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 11a6e514f2..2cbccb8dfb 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 e954d0afa3..4e5d1266f9 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/203] 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 2cbccb8dfb..2f49ae7b8f 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 4e5d1266f9..b45e7dec48 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/203] 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 b45e7dec48..81b354e7ae 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/203] save [skip ci] --- .../lib/bitcoin_transaction_priority.dart | 2 +- cw_bitcoin/lib/litecoin_wallet.dart | 49 +++++--- .../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, 182 insertions(+), 104 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_transaction_priority.dart b/cw_bitcoin/lib/bitcoin_transaction_priority.dart index 7c4dcfd5f5..d1f45a5452 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 81b354e7ae..4bafaff261 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"); - - transactionHistory.addOne(transaction); + 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); + // 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 7c0e9cad42..6d76a31841 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 3a95b9f2eb..7dd59bc52d 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 60a23c99b9..236d06f4ee 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 176b4e58d5..56c85737bc 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/203] 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 4bafaff261..75a30d3032 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/203] 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 75a30d3032..f0d810fb3c 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/203] 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 f0d810fb3c..36965f74d0 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/203] 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 2f49ae7b8f..97d466ffe7 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 fa1ac39adc..bd9fff23e2 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 acdfa4346c..26142c4773 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 b19b1bb7e9..c5c241d871 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/203] 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 630318d6e2..74cefc4ec8 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 d94f06e035..0bc770d510 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 0000000000..4aab851424 --- /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/203] 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 36965f74d0..a42a7469bf 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 a616b0bfd8..fd661c2671 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 bd9fff23e2..996d4184a8 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 f16d451d9d..d19c0fc010 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 26142c4773..acdfa4346c 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 7ad8af4c56..58a6e70368 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/203] 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 58a6e70368..6dabfa8dfe 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/203] [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 a42a7469bf..3fd660a288 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/203] 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 74cefc4ec8..1080e4085d 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 0bc770d510..d3a3bf0911 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 4aab851424..ae0834e39b 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/203] 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 1080e4085d..bd015107a7 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/203] 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 3fd660a288..faf1f09e0c 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/203] 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 faf1f09e0c..f40bf7f302 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/203] 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 f40bf7f302..505ff9d8e5 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/203] 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 97d466ffe7..d1b464f889 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 505ff9d8e5..5cfb467093 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/203] 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 5cfb467093..505ff9d8e5 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/203] 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 d1b464f889..38fded49ef 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 505ff9d8e5..22fb8ebc75 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 4217d35b49..df5826a3c6 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 bc01b33ec3..7e02649628 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 e1ee0ada3b..2c669c3bd5 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 7ffcf918d1..530038ee9e 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 8e16adbff5..b6947b1c70 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 28ac989786..f9729e5f52 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 853d064486..f2907678f7 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/203] 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 38fded49ef..276d192b9d 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 22fb8ebc75..684562eafc 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/203] 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 684562eafc..3ed027f24a 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 bb51a4eaa0..a41920dcd9 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 d19c0fc010..765ba7911d 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/203] 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 530038ee9e..7c3db4922e 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 db6a4cae2a..ce00e8b5c1 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 06132c244d..c81124627f 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 589f89fd76..b7530cd03e 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 a4b816f5bb..3219028e22 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 d39b175dde..7b2a6351e5 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 5fba46830b..93af8d398d 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 7289b7511e..7af05b723b 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 c96568a769..362d95982a 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 ef740b4d60..f58f8a8afe 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 295a01165c..b21c068e38 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 8b28e928fe..27ae35fcde 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 b1bd7cd6d2..bd8c148ab7 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 5803b86d53..648a82a0d8 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 874ec2c27c..e7dac598d8 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 890362fcd9..0ca6e4dbd1 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 25b7f6b3ae..3df71b0dd3 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 1567a0841d..7b185472fd 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 272c0862eb..021a571940 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 a8943db971..a1732a3e4c 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 c4d2827615..7a68b3bf29 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 775ab4c3d9..e7cefbba71 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 239a1aa2e3..cf55feacaf 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 3a45752d69..08eae3f6c4 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 2ab1f927ba..4a18365e1f 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 f9751f9f1b..e46e5f92ae 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 f3b8ee176f..7cf83b226a 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/203] 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 a41920dcd9..12013fb63b 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 167a585c4b..210120ba02 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 1462370fc1..8f617fe41d 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 2c669c3bd5..5511ba84a4 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 c09664cef3..4094b4d563 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 78a93bee7c..ec8a8f338f 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 c190a3bde4..3c516e02d5 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 78d8abc95c..2ebbab9780 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 611b2acb75..90729d4c7b 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 0000000000..88dc00f7cc --- /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 5a8ca14a49..5bcd4b40c3 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 272ed57c2b..62c4685b3d 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 0992451be5..404b47181d 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 64e554b1d1..b11213741d 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 0000000000..343947d003 --- /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 b2b8f8d45b..6114ce670e 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 b5429dea03..e55fddbb26 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 7790bce73c..f55ce02b52 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 ee6367effa..e9441bf720 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 a1cb8f80aa..9bb8076450 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 2843be33a6..f3085677c8 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 bd0b1b6b07..48c1c90316 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 9ea2404bc1..1a228e84d3 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 d8ae7530c3..0cd16ff4b1 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 15d9896577..f6770f6d66 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 6dcb892898..fa4e832765 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 157582562f..d79c50bea8 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 fa6d48aa08..2a8f5c7646 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 7d8d44b0d4..ee7f2f9c5f 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 e2e7b816a5..6106b05574 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 2c5f4a0b01..74c6794f79 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 4a76980027..9f37d04a0c 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 2d41ea1d61..c3dd95bad8 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 968140c18e..d3a887b7f4 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 516c5484ea..c615f1a074 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 9d05d4fd8d..c4c6bb2c66 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 f87f2f6b33..60b51424d8 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 dfbc7b83cb..7ae1c0da2b 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 ca52cccc9f..672f3a980d 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 95384b1c31..d4fe70afdc 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 c1d559daa4..c904d6cd3a 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/203] 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 3c516e02d5..ac08de33ed 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 b11213741d..f144bbaece 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/203] [skip ci] update translations + minor fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 9 +++++---- 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, 31 insertions(+), 30 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 4d0beae45d..90e621a64e 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; + stopSync(); + startSync(); } - } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 6114ce670e..27a0ae7897 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 e55fddbb26..8251754293 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 f55ce02b52..edaecbfd8e 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 e9441bf720..fa092c34e2 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 f3085677c8..a7cf90fc7d 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 48c1c90316..055715004f 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 1a228e84d3..19d6921957 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 0cd16ff4b1..f1e24caf54 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 f6770f6d66..a48dc9b5a6 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 fa4e832765..85b110b0d4 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 d79c50bea8..fb75d87745 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 2a8f5c7646..00d5444004 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 ee7f2f9c5f..6268b60ea3 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 6106b05574..e2a8b11274 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 74c6794f79..c1953c129a 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 9f37d04a0c..9b9482e36c 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 c3dd95bad8..3fe816c72a 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 d3a887b7f4..c986b81a0c 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 c615f1a074..f2f41d825b 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 c4c6bb2c66..2dd1bf6fce 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 60b51424d8..146a480d51 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 7ae1c0da2b..025f713896 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 672f3a980d..95a2301d1f 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 d4fe70afdc..e593a866c5 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 c904d6cd3a..943023c5c8 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/203] 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 90e621a64e..c526bb0c0c 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 8b89fb6f94..36bb569c6e 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/203] configure fix --- tool/configure.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/configure.dart b/tool/configure.dart index f2907678f7..021113125a 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/203] 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 5511ba84a4..20e55053dd 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 ac08de33ed..e9c113d8e2 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 90729d4c7b..79f74065a7 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 62c4685b3d..a8a9558d57 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 404b47181d..84b0dac2b3 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 f144bbaece..d34d0bfe39 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 27a0ae7897..1f3c5e123a 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 8251754293..816b768ff7 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 edaecbfd8e..af0fe73fea 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 fa092c34e2..669ace926b 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 9bb8076450..f5aa56ac3a 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 a7cf90fc7d..5dcef82985 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 055715004f..0836b6d19e 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 19d6921957..6c524e850d 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 f1e24caf54..97656a8ec4 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 a48dc9b5a6..49988a6696 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 85b110b0d4..ef49e5c421 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 fb75d87745..5dee2e6bd4 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 00d5444004..c02ffcca7b 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 6268b60ea3..9df0cc463a 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 e2a8b11274..b030407215 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 c1953c129a..2572c53ef4 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 9b9482e36c..096fdeb950 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 3fe816c72a..290fedb951 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 c986b81a0c..0b75ff4602 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 f2f41d825b..9eb9795d15 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 2dd1bf6fce..d5903aaa67 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 146a480d51..da457effcc 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 025f713896..43d896e488 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 95a2301d1f..68ef1a5296 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 e593a866c5..9d3ec2549b 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 943023c5c8..d6b481978f 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/203] 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 1f3c5e123a..bf3b7d4ded 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 816b768ff7..6bca795f88 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 af0fe73fea..e40c6e8c39 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 669ace926b..293c59ad29 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 f5aa56ac3a..10938e7a44 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 5dcef82985..a5eafac065 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 0836b6d19e..348eb905fb 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 6c524e850d..d435dfcd15 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 97656a8ec4..54c774dac9 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 49988a6696..bd8645c708 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 ef49e5c421..6bfe5f4fe8 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 5dee2e6bd4..949a78a3e0 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 c02ffcca7b..8e33511540 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 9df0cc463a..8137827df3 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 b030407215..e69ecea525 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 2572c53ef4..d03f55a1bf 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 096fdeb950..1f472c0d7e 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 290fedb951..2855359cff 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 0b75ff4602..d591cadabc 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 9eb9795d15..b3a4110112 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 d5903aaa67..581e9527aa 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 da457effcc..9d070e9bcd 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 43d896e488..8102252e31 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 68ef1a5296..33b1731444 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 9d3ec2549b..0090246e7f 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 d6b481978f..6ad8b92fcd 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/203] [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 84d28dc19c..c90d7a1ddd 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/203] 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 d34d0bfe39..98bfb8f5b0 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/203] 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 98bfb8f5b0..d3527f9971 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/203] fix litecoin address book --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 137 ++++++++++-------- 1 file changed, 78 insertions(+), 59 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index c90d7a1ddd..f2173adf6e 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'; - } - - 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'; + if (walletInfo.type == WalletType.bitcoin) { + addBitcoinAddressTypes(); } - silentAddresses.forEach((addressRecord) { - if (addressRecord.type != SilentPaymentsAddresType.p2sp || addressRecord.isHidden) { - return; + 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 - P2WPKH'; } - - if (addressRecord.address != address) { - addressesMap[addressRecord.address] = addressRecord.name.isEmpty - ? "Silent Payments" - : "Silent Payments - " + addressRecord.name; + + final lastMweb = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb)); + if (lastMweb.address != address) { + addressesMap[lastMweb.address] = 'MWEB'; } else { - addressesMap[address] = 'Active - Silent Payments'; + addressesMap[address] = 'Active - MWEB'; } - }); + } await saveAddressesInBox(); } catch (e) { From 9b33e682f429c9aa98e176ccc42de820f5e1bc95 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 24 Jul 2024 11:52:26 -0500 Subject: [PATCH 101/203] fix ios mwebd build script --- scripts/ios/build_mwebd.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/ios/build_mwebd.sh b/scripts/ios/build_mwebd.sh index ae0834e39b..f0bdfb44c2 100755 --- a/scripts/ios/build_mwebd.sh +++ b/scripts/ios/build_mwebd.sh @@ -8,8 +8,7 @@ gomobile init # build mwebd: git clone https://github.com/ltcmweb/mwebd cd mwebd -gomobile bind -target=ios ./mwebd -mkdir -p ../../../cw_mweb/android/libs/ +gomobile bind -target=ios . mv ./Mwebd.xcframework ../../../ios/ # cleanup: cd .. From 93f6dd4ceeb120407a97ad727997bec327de1856 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 24 Jul 2024 12:02:04 -0500 Subject: [PATCH 102/203] fix for building monero.com --- lib/bitcoin/cw_bitcoin.dart | 3 +++ lib/view_model/dashboard/receive_option_view_model.dart | 6 +++--- tool/configure.dart | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 210120ba02..0d7d3a1f20 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -248,6 +248,9 @@ class CWBitcoin extends Bitcoin { @override List getBitcoinReceivePageOptions() => BitcoinReceivePageOption.all; + @override + List getLitecoinReceivePageOptions() => BitcoinReceivePageOption.allLitecoin; + @override BitcoinAddressType getBitcoinAddressType(ReceivePageOption option) { switch (option) { diff --git a/lib/view_model/dashboard/receive_option_view_model.dart b/lib/view_model/dashboard/receive_option_view_model.dart index 472918f4a8..b9c8c5fc67 100644 --- a/lib/view_model/dashboard/receive_option_view_model.dart +++ b/lib/view_model/dashboard/receive_option_view_model.dart @@ -1,5 +1,4 @@ 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'; @@ -17,7 +16,8 @@ abstract class ReceiveOptionViewModelBase with Store { ? bitcoin!.getSelectedAddressType(_wallet) : ReceivePageOption.mainnet), _options = [] { - switch (_wallet.type) { + final walletType = _wallet.type; + switch (walletType) { case WalletType.bitcoin: _options = [ ...bitcoin!.getBitcoinReceivePageOptions(), @@ -26,7 +26,7 @@ abstract class ReceiveOptionViewModelBase with Store { break; case WalletType.litecoin: _options = [ - ...BitcoinReceivePageOption.allLitecoin, + ...bitcoin!.getLitecoinReceivePageOptions() ...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet) ]; break; diff --git a/tool/configure.dart b/tool/configure.dart index 021113125a..58b0971c46 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -196,6 +196,7 @@ abstract class Bitcoin { Future setAddressType(Object wallet, dynamic option); ReceivePageOption getSelectedAddressType(Object wallet); List getBitcoinReceivePageOptions(); + List getLitecoinReceivePageOptions(); BitcoinAddressType getBitcoinAddressType(ReceivePageOption option); bool hasSelectedSilentPayments(Object wallet); bool isBitcoinReceivePageOption(ReceivePageOption option); From f3c976cf42bd2bf9c10c7f4806cb076d2311c58d Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 24 Jul 2024 12:33:09 -0500 Subject: [PATCH 103/203] minor fix --- lib/view_model/dashboard/receive_option_view_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/view_model/dashboard/receive_option_view_model.dart b/lib/view_model/dashboard/receive_option_view_model.dart index b9c8c5fc67..744e4c58d9 100644 --- a/lib/view_model/dashboard/receive_option_view_model.dart +++ b/lib/view_model/dashboard/receive_option_view_model.dart @@ -26,7 +26,7 @@ abstract class ReceiveOptionViewModelBase with Store { break; case WalletType.litecoin: _options = [ - ...bitcoin!.getLitecoinReceivePageOptions() + ...bitcoin!.getLitecoinReceivePageOptions(), ...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet) ]; break; From c78243f802de039889ce3772112fd7fd245db760 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 24 Jul 2024 16:17:50 -0500 Subject: [PATCH 104/203] uncomment fix for state issues --- 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 6dabfa8dfe..58a6e70368 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 2a7a185f1e6418ce8356eed11a5978b0acf3faf5 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 25 Jul 2024 09:03:40 -0500 Subject: [PATCH 105/203] potential mweb sync fix (ios) --- cw_mweb/lib/cw_mweb.dart | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index 765ba7911d..c464930f5b 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -4,20 +4,31 @@ import 'cw_mweb_platform_interface.dart'; import 'mwebd.pbgrpc.dart'; class CwMweb { + static RpcClient? _rpcClient; + static ClientChannel? _clientChannel; + static Future stub() async { final appDir = await getApplicationSupportDirectory(); 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), - )), - ); + _clientChannel = ClientChannel('127.0.0.1', + port: port, + options: const ChannelOptions( + credentials: ChannelCredentials.insecure(), + keepAlive: ClientKeepAliveOptions(permitWithoutCalls: true), + )); + _rpcClient = RpcClient(_clientChannel!); + return _rpcClient!; } static Future stop() async { await CwMwebPlatform.instance.start("stop"); + await cleanup(); + } + + static Future cleanup() async { + await _clientChannel?.terminate(); + _rpcClient = null; + _clientChannel = null; + print("rpc has been shut down"); } } From ce9e605b9e7b0e974e3e7200ff1d5fc85320626c Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 25 Jul 2024 09:05:10 -0500 Subject: [PATCH 106/203] remove print [skip ci] --- cw_mweb/lib/cw_mweb.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index c464930f5b..1db0c02f57 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -29,6 +29,5 @@ class CwMweb { await _clientChannel?.terminate(); _rpcClient = null; _clientChannel = null; - print("rpc has been shut down"); } } From 3dbbffaf5ced37f3874f3c2cb121cc6c8167234f Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 25 Jul 2024 10:22:55 -0500 Subject: [PATCH 107/203] electrum stream potential fix --- cw_bitcoin/lib/electrum_wallet.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 4e35c8e1e9..84be047bc0 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -261,6 +261,7 @@ abstract class ElectrumWalletBase void Function(FlutterErrorDetails)? _onError; Timer? _autoSaveTimer; static const int _autoSaveInterval = 30; + StreamSubscription? _receiveStream; Future init() async { await walletAddresses.init(); @@ -312,7 +313,8 @@ abstract class ElectrumWalletBase isSingleScan: doSingleScan ?? false, )); - await for (var message in receivePort) { + _receiveStream?.cancel(); + _receiveStream = receivePort.listen((var message) async { if (message is Map) { for (final map in message.entries) { final txid = map.key; @@ -378,7 +380,7 @@ abstract class ElectrumWalletBase syncStatus = message.syncStatus; await walletInfo.updateRestoreHeight(message.height); } - } + }); } void _updateSilentAddressRecord(BitcoinSilentPaymentsUnspent unspent) { @@ -455,6 +457,7 @@ abstract class ElectrumWalletBase try { syncStatus = ConnectingSyncStatus(); + await _receiveStream?.cancel(); await electrumClient.close(); electrumClient.onConnectionStatusChange = _onConnectionStatusChange; @@ -1141,6 +1144,7 @@ abstract class ElectrumWalletBase @override Future close() async { try { + await _receiveStream?.cancel(); await electrumClient.close(); } catch (_) {} _autoSaveTimer?.cancel(); From 13aeaf2c015339acf03d651d6302ee07447eb87e Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Fri, 26 Jul 2024 10:24:24 +0200 Subject: [PATCH 108/203] fix ios build issues [skip ci] --- ios/.gitignore | 2 ++ ios/Podfile.lock | 12 ++++++++++++ ios/Runner.xcodeproj/project.pbxproj | 16 ++++++++++++---- scripts/ios/build_mwebd.sh | 7 +++---- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/ios/.gitignore b/ios/.gitignore index e96ef602b8..8ded86f140 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -30,3 +30,5 @@ Runner/GeneratedPluginRegistrant.* !default.mode2v3 !default.pbxuser !default.perspectivev3 + +Mwebd.xcframework \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index fddf6e24fc..b99afe1d20 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -8,6 +8,8 @@ PODS: - Flutter - ReachabilitySwift - CryptoSwift (1.8.2) + - cw_mweb (0.0.1): + - Flutter - device_display_brightness (0.0.1): - Flutter - device_info_plus (0.0.1): @@ -94,6 +96,8 @@ PODS: - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS + - sp_scanner (0.0.1): + - Flutter - SwiftProtobuf (1.26.0) - SwiftyGif (5.4.5) - Toast (4.1.1) @@ -113,6 +117,7 @@ DEPENDENCIES: - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - CryptoSwift + - cw_mweb (from `.symlinks/plugins/cw_mweb/ios`) - device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`) @@ -132,6 +137,7 @@ DEPENDENCIES: - sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - sp_scanner (from `.symlinks/plugins/sp_scanner/ios`) - uni_links (from `.symlinks/plugins/uni_links/ios`) - UnstoppableDomainsResolution (~> 4.0.0) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) @@ -159,6 +165,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/barcode_scan2/ios" connectivity_plus: :path: ".symlinks/plugins/connectivity_plus/ios" + cw_mweb: + :path: ".symlinks/plugins/cw_mweb/ios" device_display_brightness: :path: ".symlinks/plugins/device_display_brightness/ios" device_info_plus: @@ -197,6 +205,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + sp_scanner: + :path: ".symlinks/plugins/sp_scanner/ios" uni_links: :path: ".symlinks/plugins/uni_links/ios" url_launcher_ios: @@ -211,6 +221,7 @@ SPEC CHECKSUMS: BigInt: f668a80089607f521586bbe29513d708491ef2f7 connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d CryptoSwift: c63a805d8bb5e5538e88af4e44bb537776af11ea + cw_mweb: 87af74f9659fed0c1a2cbfb44413f1070e79e3ae device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7 device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 devicelocale: b22617f40038496deffba44747101255cee005b0 @@ -237,6 +248,7 @@ SPEC CHECKSUMS: sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986 share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12 SwiftProtobuf: 5e8349171e7c2f88f5b9e683cb3cb79d1dc780b3 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 7f5f09d79e..850b634b7b 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -28,6 +28,9 @@ A3D5E17CC53DF13FA740DEFA /* RedeemSwap.swift in Resources */ = {isa = PBXBuildFile; fileRef = 9D2F2C9F2555316C95EE7EA3 /* RedeemSwap.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; B6C6E59403ACDE44724C12F4 /* ServiceConfig.swift in Resources */ = {isa = PBXBuildFile; fileRef = B3D5E78267F5F18D882FDC3B /* ServiceConfig.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; CE291CFE2C15DB9A00B9F709 /* WowneroWallet.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE291CFD2C15DB9A00B9F709 /* WowneroWallet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CEAFE49E2C539250009FF3AD /* Mwebd.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEAFE49D2C539250009FF3AD /* Mwebd.xcframework */; }; + CEAFE49F2C539250009FF3AD /* Mwebd.xcframework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CEAFE49D2C539250009FF3AD /* Mwebd.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CEAFE4A02C53926F009FF3AD /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C58D93382C00FAC6004BCF69 /* libresolv.tbd */; }; CFEFC24F82F78FE747DF1D22 /* LnurlPayInfo.swift in Resources */ = {isa = PBXBuildFile; fileRef = 58C22CBD8C22B9D6023D59F8 /* LnurlPayInfo.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; D0D7A0D4E13F31C4E02E235B /* ReceivePayment.swift in Resources */ = {isa = PBXBuildFile; fileRef = 91C524F800843E0A3F17E004 /* ReceivePayment.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; D3AD73A327249AFE8F016A51 /* BreezSDK.swift in Resources */ = {isa = PBXBuildFile; fileRef = ABD6FCBB0F4244B090459128 /* BreezSDK.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; @@ -43,6 +46,7 @@ files = ( CE291CFE2C15DB9A00B9F709 /* WowneroWallet.framework in CopyFiles */, 0C50DFB92BF3CB56002B0EB3 /* MoneroWallet.framework in CopyFiles */, + CEAFE49F2C539250009FF3AD /* Mwebd.xcframework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -83,9 +87,10 @@ 9F46EE5D2BC11178009318F5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; ABD6FCBB0F4244B090459128 /* BreezSDK.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BreezSDK.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/BreezSDK.swift"; 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; }; B3D5E78267F5F18D882FDC3B /* ServiceConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServiceConfig.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/ServiceConfig.swift"; sourceTree = ""; }; + C58D93382C00FAC6004BCF69 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; }; CE291CFD2C15DB9A00B9F709 /* WowneroWallet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = WowneroWallet.framework; sourceTree = ""; }; + CEAFE49D2C539250009FF3AD /* Mwebd.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Mwebd.xcframework; sourceTree = ""; }; DCEA540E3586164FB47AD13E /* LnurlPayInvoice.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LnurlPayInvoice.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Task/LnurlPayInvoice.swift"; sourceTree = ""; }; F42258C3697CFE3C8C8D1933 /* ServiceLogger.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServiceLogger.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/ServiceLogger.swift"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -96,6 +101,8 @@ buildActionMask = 2147483647; files = ( 4DFD1BB54A3A50573E19A583 /* Pods_Runner.framework in Frameworks */, + CEAFE49E2C539250009FF3AD /* Mwebd.xcframework in Frameworks */, + CEAFE4A02C53926F009FF3AD /* libresolv.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -105,6 +112,7 @@ 06957875428D0F5AAE053765 /* Frameworks */ = { isa = PBXGroup; children = ( + CEAFE49D2C539250009FF3AD /* Mwebd.xcframework */, C58D93382C00FAC6004BCF69 /* libresolv.tbd */, 0C9986A3251A932F00D566FD /* CryptoSwift.framework */, 3C663361C56EBB242598F609 /* Pods_Runner.framework */, @@ -485,7 +493,7 @@ "$(PROJECT_DIR)/Flutter", ); MARKETING_VERSION = 1.0.1; - PRODUCT_BUNDLE_IDENTIFIER = "com.fotolockr.cakewallet"; + PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -631,7 +639,7 @@ "$(PROJECT_DIR)/Flutter", ); MARKETING_VERSION = 1.0.1; - PRODUCT_BUNDLE_IDENTIFIER = "com.fotolockr.cakewallet"; + PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -669,7 +677,7 @@ "$(PROJECT_DIR)/Flutter", ); MARKETING_VERSION = 1.0.1; - PRODUCT_BUNDLE_IDENTIFIER = "com.fotolockr.cakewallet"; + PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/scripts/ios/build_mwebd.sh b/scripts/ios/build_mwebd.sh index f0bdfb44c2..08dbd7cd0f 100755 --- a/scripts/ios/build_mwebd.sh +++ b/scripts/ios/build_mwebd.sh @@ -1,7 +1,6 @@ +#!/bin/bash # 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 +brew install go export PATH=$PATH:~/go/bin go install golang.org/x/mobile/cmd/gomobile@latest gomobile init @@ -9,7 +8,7 @@ gomobile init git clone https://github.com/ltcmweb/mwebd cd mwebd gomobile bind -target=ios . -mv ./Mwebd.xcframework ../../../ios/ +mv -fn ./Mwebd.xcframework ../../../ios/ # cleanup: cd .. rm -rf mwebd \ No newline at end of file From 56ea67666dd928f78a125f00a5308bfba9a808dd Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 29 Jul 2024 13:09:53 -0700 Subject: [PATCH 109/203] connection reliability updates, update kotlin code to match swift code, minor electrum error handling --- cw_bitcoin/lib/electrum.dart | 19 ++-- .../com/cakewallet/mweb/CwMwebPlugin.kt | 6 ++ cw_mweb/ios/Classes/CwMwebPlugin.swift | 86 +++++++++---------- cw_mweb/lib/cw_mweb.dart | 37 ++++++-- cw_mweb/lib/cw_mweb_method_channel.dart | 5 ++ cw_mweb/lib/cw_mweb_platform_interface.dart | 4 + 6 files changed, 102 insertions(+), 55 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 983da52d2d..b02e1d29ab 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -356,14 +356,19 @@ class ElectrumClient { BehaviorSubject>? tipListener; int? currentTip; 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); - callWithTimeout(method: method).then(cb); + try { + 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); + callWithTimeout(method: method).then(cb); + } + return currentTip; + } catch (_) { + // our websocket connection was likely terminated ungracefully :/ + return null; } - return currentTip; } BehaviorSubject? chainTipSubscribe() { 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 fa4fc909f1..2b366ad769 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 @@ -32,6 +32,11 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { server = server ?: Mwebd.newServer("", dataDir, "") port = port ?: server?.start(0) result.success(port) + } else if (call.method == "stop") { + server?.stop() + server = null + port = null + result.success(null) } else { result.notImplemented() } @@ -41,5 +46,6 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { channel.setMethodCallHandler(null) server?.stop() server = null + port = null } } diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index 996d4184a8..5c45a8434e 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -11,6 +11,7 @@ public class CwMwebPlugin: NSObject, FlutterPlugin { private static var server: MwebdServer? private static var port: Int = 0 + private static var dataDir: String? public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { @@ -18,54 +19,53 @@ public class CwMwebPlugin: NSObject, FlutterPlugin { result("iOS " + UIDevice.current.systemVersion) case "start": let args = call.arguments as? [String: String] - // print("args: \(args)") let dataDir = args?["dataDir"] - var error: NSError? + CwMwebPlugin.dataDir = dataDir + startServer(result: result) + case "stop": + stopServer() + result(nil) + default: + result(FlutterMethodNotImplemented) + } + } - if dataDir == "stop" && CwMwebPlugin.server != nil { - print("Stopping server") - CwMwebPlugin.server?.stop() - CwMwebPlugin.server = nil - result(0) - return - } + private func startServer(result: @escaping FlutterResult) { + if CwMwebPlugin.server == nil { + var error: NSError? + CwMwebPlugin.server = MwebdNewServer("", CwMwebPlugin.dataDir, "", &error) - 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 { - 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)) + if let server = CwMwebPlugin.server { + do { + print("Starting server...") + try server.start(0, ret0_: &CwMwebPlugin.port) + print("Server started successfully on port: \(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("Server already running on port: \(CwMwebPlugin.port)") -// result(FlutterError(code: "Server Already Running", message: "The server is already running", details: nil)) - result(CwMwebPlugin.port) + print("Unknown Error: Failed to create server") + result(FlutterError(code: "Unknown Error", message: "Failed to create server", details: nil)) } - - - // result(0) - default: - result(FlutterMethodNotImplemented) - } + } else { + print("Server already running on port: \(CwMwebPlugin.port)") + result(CwMwebPlugin.port) + } } - deinit { - print("Stopping and cleaning up server") - // Perform cleanup tasks - CwMwebPlugin.server?.stop() - CwMwebPlugin.server = nil - } -} + private func stopServer() { + print("Stopping server") + CwMwebPlugin.server?.stop() + CwMwebPlugin.server = nil + CwMwebPlugin.port = 0 + } + + deinit { + stopServer() + } +} \ No newline at end of file diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index 1db0c02f57..98afc73ddc 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -6,22 +6,48 @@ import 'mwebd.pbgrpc.dart'; class CwMweb { static RpcClient? _rpcClient; static ClientChannel? _clientChannel; + static int? _port; - static Future stub() async { + static Future _initializeClient() async { final appDir = await getApplicationSupportDirectory(); - int port = await CwMwebPlatform.instance.start(appDir.path) ?? 0; + _port = await CwMwebPlatform.instance.start(appDir.path); + if (_port == null || _port == 0) { + throw Exception("Failed to start server"); + } + print("Attempting to connect to server on port: $_port"); + _clientChannel = ClientChannel('127.0.0.1', - port: port, + port: _port!, options: const ChannelOptions( credentials: ChannelCredentials.insecure(), keepAlive: ClientKeepAliveOptions(permitWithoutCalls: true), )); _rpcClient = RpcClient(_clientChannel!); - return _rpcClient!; + } + + static Future stub({int maxRetries = 3}) async { + for (int i = 0; i < maxRetries; i++) { + try { + if (_rpcClient == null) { + await _initializeClient(); + } + final status = await _rpcClient! + .status(StatusRequest(), options: CallOptions(timeout: const Duration(seconds: 3))); + if (status.blockTime == 0) { + throw Exception("blockTime shouldn't be 0! (this connection is likely broken)"); + } + return _rpcClient!; + } catch (e) { + print("Attempt $i failed: $e"); + await stop(); // call stop so we create a new instance before retrying + await Future.delayed(const Duration(seconds: 2)); // wait before retrying + } + } + throw Exception("Failed to connect after $maxRetries attempts"); } static Future stop() async { - await CwMwebPlatform.instance.start("stop"); + await CwMwebPlatform.instance.stop(); await cleanup(); } @@ -29,5 +55,6 @@ class CwMweb { await _clientChannel?.terminate(); _rpcClient = null; _clientChannel = null; + _port = null; } } diff --git a/cw_mweb/lib/cw_mweb_method_channel.dart b/cw_mweb/lib/cw_mweb_method_channel.dart index cc880c6df6..9451db3108 100644 --- a/cw_mweb/lib/cw_mweb_method_channel.dart +++ b/cw_mweb/lib/cw_mweb_method_channel.dart @@ -14,4 +14,9 @@ class MethodChannelCwMweb extends CwMwebPlatform { final result = await methodChannel.invokeMethod('start', {'dataDir': dataDir}); return result; } + + @override + Future stop() async { + await methodChannel.invokeMethod('stop'); + } } diff --git a/cw_mweb/lib/cw_mweb_platform_interface.dart b/cw_mweb/lib/cw_mweb_platform_interface.dart index 974e072848..a5a46adbc1 100644 --- a/cw_mweb/lib/cw_mweb_platform_interface.dart +++ b/cw_mweb/lib/cw_mweb_platform_interface.dart @@ -26,4 +26,8 @@ abstract class CwMwebPlatform extends PlatformInterface { Future start(String dataDir) { throw UnimplementedError('start() has not been implemented.'); } + + Future stop() { + throw UnimplementedError('stop() has not been implemented.'); + } } From c1215f403284d0df224d7e2e7d98f70ebf834075 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 12 Aug 2024 10:54:43 -0700 Subject: [PATCH 110/203] dep fixes --- cw_bitcoin_cash/pubspec.yaml | 4 ++-- pubspec_base.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index 9c2561def5..149777b27a 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -28,7 +28,7 @@ dependencies: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base - ref: cake-mweb + ref: cake-update-v5 blockchain_utils: git: url: https://github.com/cake-tech/blockchain_utils @@ -46,7 +46,7 @@ dependency_overrides: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base - ref: cake-mweb + ref: cake-update-v5 # 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 4c4df60fc4..a2e804350f 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -100,7 +100,7 @@ dependencies: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base - ref: cake-mweb + ref: cake-update-v5 ledger_flutter: ^1.0.1 hashlib: 1.12.0 @@ -139,7 +139,7 @@ dependency_overrides: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base - ref: cake-mweb + ref: cake-update-v5 flutter_icons: image_path: "assets/images/app_logo.png" From b686c82a44dfd5032e1de5f3b7220ac2eb7cdde2 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 12 Aug 2024 12:15:32 -0700 Subject: [PATCH 111/203] minor fix --- cw_bitcoin/lib/litecoin_wallet.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 49401f6fde..dea91b3452 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -35,7 +35,6 @@ import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_mweb/cw_mweb.dart'; -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; part 'litecoin_wallet.g.dart'; @@ -87,7 +86,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } late final Bip32Slip10Secp256k1 mwebHd; - late final bitcoin.HDWallet oldMwebHd; late final Box mwebUtxosBox; Timer? _syncTimer; StreamSubscription? _utxoStream; @@ -371,7 +369,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } Future processMwebUtxos() async { - // final scanSecret = oldMwebHd.derive(0x80000000).privKey!; int restoreHeight = walletInfo.restoreHeight; print("SCANNING FROM HEIGHT: $restoreHeight"); final req = UtxosRequest(scanSecret: scanSecret, fromHeight: restoreHeight); From 3408de80d6cbc57306786dea8eec7cbd6d4687d9 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 12 Aug 2024 12:46:36 -0700 Subject: [PATCH 112/203] more merge fixes --- cw_bitcoin/lib/electrum_wallet.dart | 2 +- cw_bitcoin/lib/electrum_wallet_addresses.dart | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 45e7b24abb..9b2b6fd7ac 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:isolate'; -import 'dart:math'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; @@ -21,6 +20,7 @@ import 'package:cw_bitcoin/electrum_transaction_history.dart'; 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_wallet.dart'; import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; import 'package:cw_bitcoin/script_hash.dart'; import 'package:cw_bitcoin/utils.dart'; diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 166ec9bb35..388b3d4685 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -319,16 +319,18 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return address; } - String getAddress( - {required int index, - required Bip32Slip10Secp256k1 hd, - BitcoinAddressType? addressType}) => + String getAddress({ + required int index, + required Bip32Slip10Secp256k1 hd, + BitcoinAddressType? addressType, + }) => ''; - Future getAddressAsync( - {required int index, - required bitcoin.HDWallet hd, - BitcoinAddressType? addressType}) async => + Future getAddressAsync({ + required int index, + required Bip32Slip10Secp256k1 hd, + BitcoinAddressType? addressType, + }) async => getAddress(index: index, hd: hd, addressType: addressType); void addBitcoinAddressTypes() { @@ -416,7 +418,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } else { addressesMap[address] = 'Active - P2WPKH'; } - + final lastMweb = _addresses.firstWhere( (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb)); if (lastMweb.address != address) { From 8949db3b93c9c0b4a0c68d39b155064b24af1f05 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 12 Aug 2024 14:55:55 -0700 Subject: [PATCH 113/203] bitcoin_flutter removal fixes --- cw_bitcoin/lib/electrum_wallet.dart | 14 +++++++++----- cw_bitcoin/lib/litecoin_wallet.dart | 4 +--- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 5 +++-- cw_bitcoin/pubspec.yaml | 5 ++++- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 9b2b6fd7ac..c06435d8eb 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -105,11 +105,17 @@ abstract class ElectrumWalletBase } if (seedBytes != null) { - return currency == CryptoCurrency.bch - ? bitcoinCashHDWallet(seedBytes) - : Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath( + switch (currency) { + case CryptoCurrency.btc: + case CryptoCurrency.ltc: + return Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath( _hardenedDerivationPath(derivationInfo?.derivationPath ?? electrum_path)) as Bip32Slip10Secp256k1; + case CryptoCurrency.bch: + return bitcoinCashHDWallet(seedBytes); + default: + throw Exception("Unsupported currency"); + } } return Bip32Slip10Secp256k1.fromExtendedKey(xpub!); @@ -461,7 +467,6 @@ abstract class ElectrumWalletBase } } - node!.isElectrs = false; node!.save(); return node!.isElectrs!; @@ -2281,4 +2286,3 @@ class UtxoDetails { required this.spendsUnconfirmedTX, }); } - diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index dea91b3452..6d7554bab8 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:math'; -import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; import 'package:collection/collection.dart'; import 'package:convert/convert.dart' as convert; import 'package:crypto/crypto.dart'; @@ -65,7 +64,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { seedBytes: seedBytes, currency: CryptoCurrency.ltc, ) { - mwebHd = Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/1000") as Bip32Slip10Secp256k1; + mwebHd = Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/1000'") as Bip32Slip10Secp256k1; mwebEnabled = alwaysScan ?? false; walletAddresses = LitecoinWalletAddresses( walletInfo, @@ -84,7 +83,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { _stub = value; }); } - late final Bip32Slip10Secp256k1 mwebHd; late final Box mwebUtxosBox; Timer? _syncTimer; diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 7fc013211e..46ab1d12a6 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -27,9 +27,10 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with } final Bip32Slip10Secp256k1 mwebHd; + List get scanSecret => mwebHd.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw; - // TODO: I'm not 100% sure if it's supposed to be the compressed or uncompressed public key! - List get spendPubkey => mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed; + List get spendPubkey => + mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed; List mwebAddrs = []; diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index fc20cf2f98..23ce23e180 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -45,7 +45,6 @@ dependencies: url: https://github.com/rafael-xmr/sp_scanner ref: sp_v4.0.0 - dev_dependencies: flutter_test: sdk: flutter @@ -66,6 +65,10 @@ dependency_overrides: url: https://github.com/cake-tech/bitcoin_base ref: cake-update-v5 + + + pointycastle: 3.7.4 + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From d8accfb38b00b4fcd63a37a615c787557b656084 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 14 Aug 2024 11:31:28 -0700 Subject: [PATCH 114/203] [skip ci] fix always scan setting, swift updates --- cw_bitcoin/lib/litecoin_wallet.dart | 12 ++++++++++++ cw_mweb/ios/Classes/CwMwebPlugin.swift | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 0b4ec54594..a5702547fc 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -189,6 +189,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialRegularAddressIndex: snp?.regularAddressIndex, initialChangeAddressIndex: snp?.changeAddressIndex, addressPageType: snp?.addressPageType, + alwaysScan: alwaysScan, ); } @@ -818,4 +819,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { stopSync(); startSync(); } + + Future getStub() async { + _stub = await CwMweb.stub(); + return _stub; + } + + Future getStatusRequest() async { + await getStub(); + final resp = await _stub.status(StatusRequest()); + return resp; + } } diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index 5c45a8434e..fff1283d86 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -15,18 +15,18 @@ public class CwMwebPlugin: NSObject, FlutterPlugin { public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { - case "getPlatformVersion": - result("iOS " + UIDevice.current.systemVersion) - case "start": - let args = call.arguments as? [String: String] - let dataDir = args?["dataDir"] - CwMwebPlugin.dataDir = dataDir - startServer(result: result) - case "stop": - stopServer() - result(nil) - default: - result(FlutterMethodNotImplemented) + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + case "start": + let args = call.arguments as? [String: String] + let dataDir = args?["dataDir"] + CwMwebPlugin.dataDir = dataDir + startServer(result: result) + case "stop": + stopServer() + result(nil) + default: + result(FlutterMethodNotImplemented) } } From c4499869e591a9bc4d4541b7a763d8a7b7b09a97 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 14 Aug 2024 12:31:27 -0700 Subject: [PATCH 115/203] updates --- cw_bitcoin/lib/litecoin_wallet.dart | 66 +++++++++++++++-------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index a5702547fc..e48db6475d 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -90,8 +90,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { late final Bip32Slip10Secp256k1 mwebHd; late final Box mwebUtxosBox; Timer? _syncTimer; + Timer? _feeRatesTimer; StreamSubscription? _utxoStream; - int mwebUtxosHeight = 0; late RpcClient _stub; late bool mwebEnabled; @@ -196,6 +196,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @action @override Future startSync() async { + print("STARTING SYNC"); if (!mwebEnabled) { syncStatus = SyncronizingSyncStatus(); await subscribeForUpdates(); @@ -208,21 +209,24 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await updateTransactions(); await updateFeeRates(); - Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); + _feeRatesTimer?.cancel(); + _feeRatesTimer = + 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 nodeHeight = await electrumClient.getCurrentBlockChainTip() ?? 0; final resp = await _stub.status(StatusRequest()); - if (resp.blockHeaderHeight < height) { + + if (resp.blockHeaderHeight < nodeHeight) { int h = resp.blockHeaderHeight; syncStatus = SyncingSyncStatus(height - h, h / height); - } else if (resp.mwebHeaderHeight < height) { + } else if (resp.mwebHeaderHeight < nodeHeight) { int h = resp.mwebHeaderHeight; syncStatus = SyncingSyncStatus(height - h, h / height); - } else if (resp.mwebUtxosHeight < height) { + } else if (resp.mwebUtxosHeight < nodeHeight) { syncStatus = SyncingSyncStatus(1, 0.999); } else { // prevent unnecessary reaction triggers: @@ -230,14 +234,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { syncStatus = SyncedSyncStatus(); } - if (resp.mwebUtxosHeight > mwebUtxosHeight) { - mwebUtxosHeight = resp.mwebUtxosHeight; + if (resp.mwebUtxosHeight > walletInfo.restoreHeight) { + await walletInfo.updateRestoreHeight(resp.mwebUtxosHeight); await checkMwebUtxosSpent(); // update the confirmations for each transaction: for (final transaction in transactionHistory.transactions.values) { if (transaction.isPending) continue; - int txHeight = transaction.height ?? mwebUtxosHeight; - final confirmations = (mwebUtxosHeight - txHeight) + 1; + int txHeight = transaction.height ?? resp.mwebUtxosHeight; + final confirmations = (resp.mwebUtxosHeight - txHeight) + 1; if (transaction.confirmations == confirmations) continue; transaction.confirmations = confirmations; transactionHistory.addOne(transaction); @@ -292,7 +296,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { }) async { await mwebUtxosBox.clear(); transactionHistory.clear(); - mwebUtxosHeight = height; await walletInfo.updateRestoreHeight(height); // reset coin balances and txCount to 0: @@ -307,7 +310,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { addressRecord.txCount = 0; } - print("STARTING SYNC"); await startSync(); } @@ -362,6 +364,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final addressRecord = walletAddresses.allAddresses .firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address); if (addressRecord == null) { + print("we don't have this address in the wallet! ${utxo.address}"); return; } @@ -391,21 +394,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final req = UtxosRequest(scanSecret: scanSecret, fromHeight: restoreHeight); // process old utxos: - for (final utxo in mwebUtxosBox.values) { - if (utxo.address.isEmpty) { - continue; - } - - // if (walletInfo.restoreHeight > utxo.height) { - // continue; - // } + // for (final utxo in mwebUtxosBox.values) { + // if (utxo.address.isEmpty) { + // continue; + // } - await handleIncoming(utxo, _stub); + // // if (walletInfo.restoreHeight > utxo.height) { + // // continue; + // // } + // // await handleIncoming(utxo, _stub); - if (utxo.height > walletInfo.restoreHeight) { - await walletInfo.updateRestoreHeight(utxo.height); - } - } + // if (utxo.height > walletInfo.restoreHeight) { + // await walletInfo.updateRestoreHeight(utxo.height); + // } + // } // process new utxos as they come in: _utxoStream?.cancel(); @@ -418,10 +420,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: - return; - } + // if (mwebUtxosBox.containsKey(utxo.outputId)) { + // // we've already stored this utxo, skip it: + // return; + // } // if (utxo.address.isEmpty) { // await updateUnspent(); @@ -631,10 +633,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // 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; + for (final tx in transactionHistory.transactions.values) { + // if (tx.isPending) continue; final txAddresses = tx.inputAddresses! + tx.outputAddresses!; - for (var address in txAddresses) { + for (final address in txAddresses) { final addressRecord = walletAddresses.allAddresses .firstWhereOrNull((addressRecord) => addressRecord.address == address); if (addressRecord == null) { From 192045310cd0c252e03f61344e05253200e91c20 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 14 Aug 2024 13:16:52 -0700 Subject: [PATCH 116/203] fixes --- cw_bitcoin/lib/electrum.dart | 5 ++++- cw_bitcoin/lib/litecoin_wallet.dart | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index d58e31e45b..879f4a5f9a 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -115,7 +115,10 @@ class ElectrumClient { _setConnectionStatus(ConnectionStatus.failed); }, onDone: () { unterminatedString = ''; - if (host == socket?.address.host) _setConnectionStatus(ConnectionStatus.disconnected); + if (host == socket?.address.host) { + _setConnectionStatus(ConnectionStatus.disconnected); + } + socket = null; }); keepAlive(); diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index e48db6475d..9cec5a2c27 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -222,10 +222,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (resp.blockHeaderHeight < nodeHeight) { int h = resp.blockHeaderHeight; - syncStatus = SyncingSyncStatus(height - h, h / height); + syncStatus = SyncingSyncStatus(nodeHeight - h, h / nodeHeight); } else if (resp.mwebHeaderHeight < nodeHeight) { int h = resp.mwebHeaderHeight; - syncStatus = SyncingSyncStatus(height - h, h / height); + syncStatus = SyncingSyncStatus(nodeHeight - h, h / nodeHeight); } else if (resp.mwebUtxosHeight < nodeHeight) { syncStatus = SyncingSyncStatus(1, 0.999); } else { From 7d36da91fa79d4a649977767c6462c6c0b528387 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 19 Aug 2024 12:30:25 -0400 Subject: [PATCH 117/203] small fix --- cw_bitcoin/lib/litecoin_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 288b7809b8..fb7dad986a 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:math'; import 'package:collection/collection.dart'; import 'package:convert/convert.dart' as convert; @@ -23,7 +24,6 @@ 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/encryption_file_utils.dart'; -import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; From d315617efa1d805273b8b5c2082878fbc594e5e0 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 19 Aug 2024 12:31:50 -0400 Subject: [PATCH 118/203] small 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 fb7dad986a..9449e65996 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; import 'package:collection/collection.dart'; -import 'package:convert/convert.dart' as convert; import 'package:crypto/crypto.dart'; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/mweb_utxo.dart'; @@ -468,7 +467,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (status.mwebUtxosHeight != height) return; int amount = 0; Set inputAddresses = {}; - var output = convert.AccumulatorSink(); + var output = AccumulatorSink(); var input = sha256.startChunkedConversion(output); for (final outputId in spent) { final utxo = mwebUtxosBox.get(outputId); From 501e4998c78d010063d8aa366c9702fecf9ccb60 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 19 Aug 2024 13:14:00 -0400 Subject: [PATCH 119/203] fix --- 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 9449e65996..14662e7a8b 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:convert' as convert; import 'dart:math'; import 'package:collection/collection.dart'; import 'package:crypto/crypto.dart'; @@ -467,7 +468,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (status.mwebUtxosHeight != height) return; int amount = 0; Set inputAddresses = {}; - var output = AccumulatorSink(); + var output = convert.AccumulatorSink(); var input = sha256.startChunkedConversion(output); for (final outputId in spent) { final utxo = mwebUtxosBox.get(outputId); From c620d6d5a86c32790af6decc8098e4c3592427d8 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 19 Aug 2024 14:33:05 -0400 Subject: [PATCH 120/203] dart:convert != package:convert --- cw_bitcoin/lib/litecoin_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 14662e7a8b..7d8e70c41f 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:convert' as convert; +import 'package:convert/convert.dart' as convert; import 'dart:math'; import 'package:collection/collection.dart'; import 'package:crypto/crypto.dart'; From ef077c864763488cc25f6b0cc985971c8c0f6c10 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 19 Aug 2024 15:11:08 -0400 Subject: [PATCH 121/203] change address fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 2 ++ cw_bitcoin/lib/litecoin_wallet_addresses.dart | 15 ++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 7d8e70c41f..bb10231648 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -83,6 +83,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { sideHd: accountHD.childKey(Bip32KeyIndex(1)), network: network, mwebHd: mwebHd, + mwebEnabled: mwebEnabled, ); autorun((_) { this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; @@ -822,6 +823,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } mwebEnabled = enabled; + (walletAddresses as LitecoinWalletAddresses).mwebEnabled = enabled; stopSync(); startSync(); } diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 46ab1d12a6..24cdd04d80 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -17,8 +17,9 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with WalletInfo walletInfo, { required super.mainHd, required super.sideHd, - required this.mwebHd, required super.network, + required this.mwebHd, + required this.mwebEnabled, super.initialAddresses, super.initialRegularAddressIndex, super.initialChangeAddressIndex, @@ -27,6 +28,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with } final Bip32Slip10Secp256k1 mwebHd; + bool mwebEnabled; List get scanSecret => mwebHd.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw; List get spendPubkey => @@ -78,11 +80,10 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with @action @override Future getChangeAddress() async { - // super.getChangeAddress(); - // updateChangeAddresses(); - // print("getChangeAddress @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); - // this means all change addresses used will be mweb addresses!: - await topUpMweb(0); - return mwebAddrs[0]; + if (mwebEnabled) { + await topUpMweb(0); + return mwebAddrs[0]; + } + return super.getChangeAddress(); } } From e0a0b7ab5dc0a77375c35205e914a23235eb012b Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 19 Aug 2024 16:20:54 -0400 Subject: [PATCH 122/203] update bitcoin_base to fix mweb address program checking --- cw_bitcoin/pubspec.yaml | 2 +- cw_bitcoin_cash/pubspec.yaml | 2 +- pubspec_base.yaml | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 2901bf0f5d..cd7440c530 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -59,7 +59,7 @@ dependency_overrides: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base - ref: cake-update-v5 + ref: cake-update-v6 pointycastle: 3.7.4 # For information on the generic Dart part of this file, see the diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index 64bd38b1d6..dcf6584b8f 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -42,7 +42,7 @@ dependency_overrides: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base - ref: cake-update-v5 + ref: cake-update-v6 # 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 3ea8c24e4f..6e07b989b0 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -97,10 +97,6 @@ dependencies: polyseed: ^0.0.6 nostr_tools: ^1.0.9 solana: ^0.30.1 - bitcoin_base: - git: - url: https://github.com/cake-tech/bitcoin_base - ref: cake-update-v5 ledger_flutter: ^1.0.1 hashlib: ^1.19.2 @@ -139,7 +135,7 @@ dependency_overrides: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base - ref: cake-update-v5 + ref: cake-update-v6 flutter_icons: image_path: "assets/images/app_logo.png" From 9cab9b9bc2a471a27efd4f06f8b84e116966a2a6 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 22 Aug 2024 11:49:05 -0400 Subject: [PATCH 123/203] fix ios xcode project [skip ci] --- ios/Runner.xcodeproj/project.pbxproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 850b634b7b..10cc6434dd 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -28,8 +28,6 @@ A3D5E17CC53DF13FA740DEFA /* RedeemSwap.swift in Resources */ = {isa = PBXBuildFile; fileRef = 9D2F2C9F2555316C95EE7EA3 /* RedeemSwap.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; B6C6E59403ACDE44724C12F4 /* ServiceConfig.swift in Resources */ = {isa = PBXBuildFile; fileRef = B3D5E78267F5F18D882FDC3B /* ServiceConfig.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; CE291CFE2C15DB9A00B9F709 /* WowneroWallet.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE291CFD2C15DB9A00B9F709 /* WowneroWallet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEAFE49E2C539250009FF3AD /* Mwebd.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEAFE49D2C539250009FF3AD /* Mwebd.xcframework */; }; - CEAFE49F2C539250009FF3AD /* Mwebd.xcframework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CEAFE49D2C539250009FF3AD /* Mwebd.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEAFE4A02C53926F009FF3AD /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C58D93382C00FAC6004BCF69 /* libresolv.tbd */; }; CFEFC24F82F78FE747DF1D22 /* LnurlPayInfo.swift in Resources */ = {isa = PBXBuildFile; fileRef = 58C22CBD8C22B9D6023D59F8 /* LnurlPayInfo.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; D0D7A0D4E13F31C4E02E235B /* ReceivePayment.swift in Resources */ = {isa = PBXBuildFile; fileRef = 91C524F800843E0A3F17E004 /* ReceivePayment.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; @@ -46,7 +44,6 @@ files = ( CE291CFE2C15DB9A00B9F709 /* WowneroWallet.framework in CopyFiles */, 0C50DFB92BF3CB56002B0EB3 /* MoneroWallet.framework in CopyFiles */, - CEAFE49F2C539250009FF3AD /* Mwebd.xcframework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -101,7 +98,6 @@ buildActionMask = 2147483647; files = ( 4DFD1BB54A3A50573E19A583 /* Pods_Runner.framework in Frameworks */, - CEAFE49E2C539250009FF3AD /* Mwebd.xcframework in Frameworks */, CEAFE4A02C53926F009FF3AD /* libresolv.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; From 527983af4f9c328a844de77fe16efc2d18e7723d Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 23 Aug 2024 15:17:28 -0400 Subject: [PATCH 124/203] updates --- cw_bitcoin/lib/electrum_wallet.dart | 88 ++++++--------- cw_bitcoin/lib/litecoin_wallet.dart | 104 +++++++++--------- .../unspent_coins_list_view_model.dart | 19 +++- 3 files changed, 103 insertions(+), 108 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 4e447814a2..3da039afe7 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -424,9 +424,7 @@ abstract class ElectrumWalletBase @override Future startSync() async { try { - if (this is! LitecoinWallet) { - syncStatus = SyncronizingSyncStatus(); - } + syncStatus = SyncronizingSyncStatus(); if (hasSilentPaymentsScanning) { await _setInitialHeight(); @@ -1262,69 +1260,51 @@ abstract class ElectrumWalletBase updatedUnspentCoins.addAll(await fetchUnspent(address)); })); - unspentCoins = updatedUnspentCoins; - } + if (unspentCoinsInfo.length != updatedUnspentCoins.length) { + updatedUnspentCoins.forEach((coin) => addCoinInfo(coin)); + } - Future updateUnspent() async { - await updateAllUnspents(); + await updateCoins(updatedUnspentCoins, set: true); + await _refreshUnspentCoinsInfo(); + } - if (unspentCoinsInfo.length != unspentCoins.length) { - unspentCoins.forEach((coin) => addCoinInfo(coin)); + Future updateCoins(List newUnspentCoins, {bool set = false}) async { + if (newUnspentCoins.isEmpty) { return; } - if (unspentCoins.isNotEmpty) { - unspentCoins.forEach((coin) { - final coinInfoList = unspentCoinsInfo.values.where((element) => + if (set) { + unspentCoins = newUnspentCoins; + } else { + unspentCoins.addAll(newUnspentCoins); + } + + newUnspentCoins.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; + element.vout == coin.vout, + ); - coin.isFrozen = coinInfo.isFrozen; - coin.isSending = coinInfo.isSending; - coin.note = coinInfo.note; - if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) - coin.bitcoinAddressRecord.balance += coinInfo.value; - } else { - addCoinInfo(coin); - } - }); - } + if (coinInfoList.isNotEmpty) { + final coinInfo = coinInfoList.first; - await _refreshUnspentCoinsInfo(); + coin.isFrozen = coinInfo.isFrozen; + coin.isSending = coinInfo.isSending; + coin.note = coinInfo.note; + if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) + coin.bitcoinAddressRecord.balance += coinInfo.value; + } else { + addCoinInfo(coin); + } + }); } @action - Future updateUnspents(BitcoinAddressRecord address) async { + Future updateUnspentsForAddress(BitcoinAddressRecord address) async { final newUnspentCoins = await fetchUnspent(address); - - if (newUnspentCoins.isNotEmpty) { - unspentCoins.addAll(newUnspentCoins); - - newUnspentCoins.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 { - addCoinInfo(coin); - } - }); - } + await updateCoins(newUnspentCoins); } @action @@ -1819,7 +1799,7 @@ abstract class ElectrumWalletBase _scripthashesUpdateSubject[sh] = await electrumClient.scripthashUpdate(sh); _scripthashesUpdateSubject[sh]?.listen((event) async { try { - await updateUnspents(address); + await updateUnspentsForAddress(address); await updateBalance(); diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index bb10231648..c5e6a6df33 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -202,14 +202,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future startSync() async { print("STARTING SYNC"); - if (!mwebEnabled) { - syncStatus = SyncronizingSyncStatus(); - await subscribeForUpdates(); - await updateTransactions(); - syncStatus = SyncedSyncStatus(); - return; - } - + syncStatus = SyncronizingSyncStatus(); await subscribeForUpdates(); await updateTransactions(); await updateFeeRates(); @@ -218,6 +211,16 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { _feeRatesTimer = Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); + if (!mwebEnabled) { + await super.updateAllUnspents(); + await updateBalance(); + syncStatus = SyncedSyncStatus(); + return; + } + + await updateUnspent(); + await updateBalance(); + _stub = await CwMweb.stub(); _syncTimer?.cancel(); _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { @@ -255,8 +258,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } }); - updateUnspent(); - fetchBalances(); // this runs in the background and processes new utxos as they come in: processMwebUtxos(); } @@ -538,62 +539,60 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return true; } - @override Future updateUnspent() async { - await super.updateUnspent(); await checkMwebUtxosSpent(); + await updateAllUnspents(); } @override @action Future updateAllUnspents() async { - List updatedUnspentCoins = []; - - await Future.wait(walletAddresses.allAddresses.map((address) async { - updatedUnspentCoins.addAll(await fetchUnspent(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); + // get ltc unspents: + await super.updateAllUnspents(); - 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); - }); + if (!mwebEnabled) { + return; } + // add the mweb unspents to the list: + List mwebUnspentCoins = []; + // 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); - unspentCoins = updatedUnspentCoins; + 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; + } + mwebUnspentCoins.add(unspent); + }); + unspentCoins.addAll(mwebUnspentCoins); } @override Future fetchBalances() async { final balance = await super.fetchBalances(); - var confirmed = balance.confirmed; - var unconfirmed = balance.unconfirmed; + int confirmed = balance.confirmed; + int unconfirmed = balance.unconfirmed; mwebUtxosBox.values.forEach((utxo) { if (utxo.height > 0) { confirmed += utxo.value.toInt(); @@ -640,6 +639,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // to have an accurate count, we should just keep it in sync with what we know from the tx history: for (final tx in transactionHistory.transactions.values) { // if (tx.isPending) continue; + if (tx.inputAddresses == null || tx.outputAddresses == null) { + continue; + } final txAddresses = tx.inputAddresses! + tx.outputAddresses!; for (final address in txAddresses) { final addressRecord = walletAddresses.allAddresses 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 5b6e6140f7..72dcdb27b7 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 @@ -38,6 +38,10 @@ abstract class UnspentCoinsListViewModelBase with Store { final info = getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout, item.keyImage); + if (info == null) { + return; + } + info.isFrozen = item.isFrozen; info.isSending = item.isSending; info.note = item.note; @@ -50,15 +54,21 @@ abstract class UnspentCoinsListViewModelBase with Store { } } - UnspentCoinsInfo getUnspentCoinInfo( - String hash, String address, int value, int vout, String? keyImage) => - _unspentCoinsInfo.values.firstWhere((element) => + UnspentCoinsInfo? getUnspentCoinInfo( + String hash, String address, int value, int vout, String? keyImage) { + try { + return _unspentCoinsInfo.values.firstWhere((element) => element.walletId == wallet.id && element.hash == hash && element.address == address && element.value == value && element.vout == vout && element.keyImage == keyImage); + } catch (e) { + print("UnspentCoinsInfo not found for coin: $e"); + return null; + } + } String formatAmountToString(int fullBalance) { if (wallet.type == WalletType.monero) @@ -108,6 +118,9 @@ abstract class UnspentCoinsListViewModelBase with Store { try { final info = getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage); + if (info == null) { + return; + } unspents.add(UnspentCoinsItem( address: elem.address, From 10c235328810047a4cd64335c185caa42a5fb669 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 23 Aug 2024 17:25:33 -0400 Subject: [PATCH 125/203] more fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 35 ++++++++++++++++++----------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index c5e6a6df33..5a5e3bc282 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -212,9 +212,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); if (!mwebEnabled) { - await super.updateAllUnspents(); - await updateBalance(); - syncStatus = SyncedSyncStatus(); + try { + await updateAllUnspents(); + await updateBalance(); + syncStatus = SyncedSyncStatus(); + } catch (e, s) { + print(e); + print(s); + syncStatus = FailedSyncStatus(); + } return; } @@ -480,7 +486,6 @@ 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} !!!!!!"); } addressRecord.balance -= utxo.value.toInt(); amount += utxo.value.toInt(); @@ -593,13 +598,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final balance = await super.fetchBalances(); int confirmed = balance.confirmed; int unconfirmed = balance.unconfirmed; - mwebUtxosBox.values.forEach((utxo) { - if (utxo.height > 0) { - confirmed += utxo.value.toInt(); - } else { - unconfirmed += utxo.value.toInt(); - } - }); + try { + mwebUtxosBox.values.forEach((utxo) { + if (utxo.height > 0) { + confirmed += utxo.value.toInt(); + } else { + unconfirmed += utxo.value.toInt(); + } + }); + } catch (_) {} // update unspent balances: @@ -609,6 +616,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // coin.bitcoinAddressRecord.balance = 0; // coin.bitcoinAddressRecord.txCount = 0; // }); + + await updateUnspent(); + for (var addressRecord in walletAddresses.allAddresses) { addressRecord.balance = 0; addressRecord.txCount = 0; @@ -653,7 +663,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } - await updateUnspent(); + return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } @@ -814,7 +824,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future close() async { await super.close(); - await mwebUtxosBox.close(); _syncTimer?.cancel(); _utxoStream?.cancel(); } From a0c315c4cf42a62c510c4cb14bc4e47233a1e160 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 23 Aug 2024 17:52:40 -0400 Subject: [PATCH 126/203] more fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 20 +++++++++++-------- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 12 ++++++++--- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 5a5e3bc282..cd20a5b249 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -88,9 +88,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { autorun((_) { this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; }); - CwMweb.stub().then((value) { - _stub = value; - }); } late final Bip32Slip10Secp256k1 mwebHd; late final Box mwebUtxosBox; @@ -201,7 +198,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @action @override Future startSync() async { - print("STARTING SYNC"); + print("STARTING SYNC - MWEB ENABLED: $mwebEnabled"); syncStatus = SyncronizingSyncStatus(); await subscribeForUpdates(); await updateTransactions(); @@ -211,6 +208,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { _feeRatesTimer = Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); + if (!mwebEnabled) { try { await updateAllUnspents(); @@ -224,10 +222,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return; } + await getStub(); await updateUnspent(); await updateBalance(); - _stub = await CwMweb.stub(); _syncTimer?.cancel(); _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { if (syncStatus is FailedSyncStatus) return; @@ -596,6 +594,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future fetchBalances() async { final balance = await super.fetchBalances(); + if (!mwebEnabled) { + return balance; + } + int confirmed = balance.confirmed; int unconfirmed = balance.unconfirmed; try { @@ -618,7 +620,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // }); await updateUnspent(); - + for (var addressRecord in walletAddresses.allAddresses) { addressRecord.balance = 0; addressRecord.txCount = 0; @@ -663,8 +665,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } - - return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } @@ -835,6 +835,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { mwebEnabled = enabled; (walletAddresses as LitecoinWalletAddresses).mwebEnabled = enabled; + if (enabled) { + // generate inital mweb addresses: + (walletAddresses as LitecoinWalletAddresses).topUpMweb(0); + } stopSync(); startSync(); } diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 24cdd04d80..12140472cb 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -24,7 +24,9 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialRegularAddressIndex, super.initialChangeAddressIndex, }) : super(walletInfo) { - topUpMweb(0); + if (mwebEnabled) { + topUpMweb(0); + } } final Bip32Slip10Secp256k1 mwebHd; @@ -58,7 +60,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType, }) { - if (addressType == SegwitAddresType.mweb) { + if (addressType == SegwitAddresType.mweb && mwebEnabled) { topUpMweb(index); return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1]; } @@ -71,7 +73,11 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType, }) async { - if (addressType == SegwitAddresType.mweb) { + // if mweb isn't enabled we'll just return the regular address type which does effectively nothing + // sort of a hack but easier than trying to pull the mweb setting into the electrum_wallet_addresses initialization code + // (we want to avoid initializing the mweb.stub() if it's not enabled or we'd be starting the whole server for no reason and it's slow) + // TODO: find a way to do address generation without starting the whole mweb server + if (addressType == SegwitAddresType.mweb && mwebEnabled) { await topUpMweb(index); } return getAddress(index: index, hd: hd, addressType: addressType); From 2552fb9552ff48379baefb220e759f44b2f334c4 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 23 Aug 2024 18:04:26 -0400 Subject: [PATCH 127/203] ensure we don't initialize mweb until we really have to --- cw_bitcoin/lib/litecoin_wallet.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index cd20a5b249..ea3c395cc0 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -208,7 +208,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { _feeRatesTimer = Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); - if (!mwebEnabled) { try { await updateAllUnspents(); @@ -556,6 +555,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (!mwebEnabled) { return; } + await getStub(); + // add the mweb unspents to the list: List mwebUnspentCoins = []; // update mweb unspents: @@ -597,7 +598,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (!mwebEnabled) { return balance; } - + await getStub(); + int confirmed = balance.confirmed; int unconfirmed = balance.unconfirmed; try { @@ -758,6 +760,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (!mwebEnabled) { return tx; } + await getStub(); final resp = await _stub.create(CreateRequest( rawTx: hex.decode(tx.hex), From dadae35c955fa77074ebdbae2a0a7ed00391af82 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 27 Aug 2024 11:44:18 -0400 Subject: [PATCH 128/203] fix regression --- cw_bitcoin/lib/electrum_wallet.dart | 16 +++---- cw_bitcoin/lib/litecoin_wallet.dart | 66 +++++++++++++++-------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 1952c78da3..f1aea380c1 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1262,25 +1262,22 @@ abstract class ElectrumWalletBase updatedUnspentCoins.addAll(await fetchUnspent(address)); })); + unspentCoins = updatedUnspentCoins; + if (unspentCoinsInfo.length != updatedUnspentCoins.length) { - updatedUnspentCoins.forEach((coin) => addCoinInfo(coin)); + unspentCoins.forEach((coin) => addCoinInfo(coin)); + return; } - await updateCoins(updatedUnspentCoins, set: true); + await updateCoins(unspentCoins); await _refreshUnspentCoinsInfo(); } - Future updateCoins(List newUnspentCoins, {bool set = false}) async { + Future updateCoins(List newUnspentCoins) async { if (newUnspentCoins.isEmpty) { return; } - if (set) { - unspentCoins = newUnspentCoins; - } else { - unspentCoins.addAll(newUnspentCoins); - } - newUnspentCoins.forEach((coin) { final coinInfoList = unspentCoinsInfo.values.where( (element) => @@ -1467,7 +1464,6 @@ abstract class ElectrumWalletBase // Create a list of available outputs final outputs = []; for (final out in bundle.originalTransaction.outputs) { - // Check if the script contains OP_RETURN final script = out.scriptPubKey.script; if (script.contains('OP_RETURN') && memo == null) { diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index ea3c395cc0..824d8d6daa 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -226,40 +226,43 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await updateBalance(); _syncTimer?.cancel(); - _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { - if (syncStatus is FailedSyncStatus) return; - final nodeHeight = await electrumClient.getCurrentBlockChainTip() ?? 0; - final resp = await _stub.status(StatusRequest()); - - if (resp.blockHeaderHeight < nodeHeight) { - int h = resp.blockHeaderHeight; - syncStatus = SyncingSyncStatus(nodeHeight - h, h / nodeHeight); - } else if (resp.mwebHeaderHeight < nodeHeight) { - int h = resp.mwebHeaderHeight; - syncStatus = SyncingSyncStatus(nodeHeight - h, h / nodeHeight); - } else if (resp.mwebUtxosHeight < nodeHeight) { - syncStatus = SyncingSyncStatus(1, 0.999); - } else { - // prevent unnecessary reaction triggers: - if (syncStatus is! SyncedSyncStatus) { - syncStatus = SyncedSyncStatus(); - } + // delay the timer by a second so we don't overrride the restoreheight if one is set + Timer(const Duration(seconds: 1), () async { + _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { + if (syncStatus is FailedSyncStatus) return; + final nodeHeight = await electrumClient.getCurrentBlockChainTip() ?? 0; + final resp = await _stub.status(StatusRequest()); + + if (resp.blockHeaderHeight < nodeHeight) { + int h = resp.blockHeaderHeight; + syncStatus = SyncingSyncStatus(nodeHeight - h, h / nodeHeight); + } else if (resp.mwebHeaderHeight < nodeHeight) { + int h = resp.mwebHeaderHeight; + syncStatus = SyncingSyncStatus(nodeHeight - h, h / nodeHeight); + } else if (resp.mwebUtxosHeight < nodeHeight) { + syncStatus = SyncingSyncStatus(1, 0.999); + } else { + // prevent unnecessary reaction triggers: + if (syncStatus is! SyncedSyncStatus) { + syncStatus = SyncedSyncStatus(); + } - if (resp.mwebUtxosHeight > walletInfo.restoreHeight) { - await walletInfo.updateRestoreHeight(resp.mwebUtxosHeight); - await checkMwebUtxosSpent(); - // update the confirmations for each transaction: - for (final transaction in transactionHistory.transactions.values) { - if (transaction.isPending) continue; - int txHeight = transaction.height ?? resp.mwebUtxosHeight; - final confirmations = (resp.mwebUtxosHeight - txHeight) + 1; - if (transaction.confirmations == confirmations) continue; - transaction.confirmations = confirmations; - transactionHistory.addOne(transaction); + if (resp.mwebUtxosHeight > walletInfo.restoreHeight) { + await walletInfo.updateRestoreHeight(resp.mwebUtxosHeight); + await checkMwebUtxosSpent(); + // update the confirmations for each transaction: + for (final transaction in transactionHistory.transactions.values) { + if (transaction.isPending) continue; + int txHeight = transaction.height ?? resp.mwebUtxosHeight; + final confirmations = (resp.mwebUtxosHeight - txHeight) + 1; + if (transaction.confirmations == confirmations) continue; + transaction.confirmations = confirmations; + transactionHistory.addOne(transaction); + } + await transactionHistory.save(); } - await transactionHistory.save(); } - } + }); }); // this runs in the background and processes new utxos as they come in: processMwebUtxos(); @@ -305,6 +308,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { }) async { await mwebUtxosBox.clear(); transactionHistory.clear(); + _syncTimer?.cancel(); await walletInfo.updateRestoreHeight(height); // reset coin balances and txCount to 0: From d1c85651f29811192c61133e094332bc4499296b Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 3 Sep 2024 11:05:55 -0700 Subject: [PATCH 129/203] improve mweb reliability --- cw_bitcoin/lib/litecoin_wallet.dart | 14 ++++++++++++++ cw_bitcoin/lib/litecoin_wallet_addresses.dart | 7 +++++-- cw_bitcoin/lib/litecoin_wallet_service.dart | 6 +++--- .../kotlin/com/cakewallet/mweb/CwMwebPlugin.kt | 5 ++++- cw_mweb/ios/Classes/CwMwebPlugin.swift | 1 + cw_mweb/lib/cw_mweb.dart | 2 ++ 6 files changed, 29 insertions(+), 6 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 3204e5cbdb..b69ba59ddb 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -443,6 +443,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } Future processMwebUtxos() async { + if (!mwebEnabled) { + return; + } + int restoreHeight = walletInfo.restoreHeight; print("SCANNING FROM HEIGHT: $restoreHeight"); final req = UtxosRequest(scanSecret: scanSecret, fromHeight: restoreHeight); @@ -502,6 +506,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } Future checkMwebUtxosSpent() async { + if (!mwebEnabled) { + return; + } + while ((await Future.wait(transactionHistory.transactions.values .where((tx) => tx.direction == TransactionDirection.outgoing && tx.isPending) .map(checkPendingTransaction))) @@ -557,6 +565,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } Future checkPendingTransaction(ElectrumTransactionInfo tx) async { + if (!mwebEnabled) return false; if (!tx.isPending) return false; final outputId = [], target = {}; final isHash = RegExp(r'^[a-f0-9]{64}$').hasMatch; @@ -755,6 +764,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { vinOutpoints: vinOutpoints, ); } + + if (!mwebEnabled) { + throw Exception("MWEB is not enabled! can't calculate fee without starting the mweb server!"); + } + if (outputs.length == 1 && outputs[0].toOutput.amount == BigInt.zero) { outputs = [ BitcoinScriptOutput( diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 12140472cb..7db9d7baed 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -25,7 +25,10 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialChangeAddressIndex, }) : super(walletInfo) { if (mwebEnabled) { - topUpMweb(0); + // give the server a few seconds to start up before trying to get the addresses: + Future.delayed(const Duration(seconds: 5), () async { + await topUpMweb(0); + }); } } @@ -39,9 +42,9 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with List mwebAddrs = []; Future topUpMweb(int index) async { + final stub = await CwMweb.stub(); while (mwebAddrs.length - index < 1000) { final length = mwebAddrs.length; - final stub = await CwMweb.stub(); final resp = await stub.addresses(AddressRequest( fromIndex: length, toIndex: index + 1000, diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index 72efa41c75..da36020246 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -150,9 +150,9 @@ class LitecoinWalletService extends WalletService< @override Future restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { - if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { - throw LitecoinMnemonicIsIncorrectException(); - } + // if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { + // throw LitecoinMnemonicIsIncorrectException(); + // } final wallet = await LitecoinWalletBase.create( password: credentials.password!, 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 2b366ad769..b1180dd4a1 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 @@ -28,9 +28,12 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { if (call.method == "start") { + server?.stop() val dataDir = call.argument("dataDir") ?: "" + // server = server ?: Mwebd.newServer("", dataDir, "") + // port = port ?: server?.start(0) server = server ?: Mwebd.newServer("", dataDir, "") - port = port ?: server?.start(0) + port = server?.start(0) result.success(port) } else if (call.method == "stop") { server?.stop() diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index fff1283d86..ed08d6748e 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -18,6 +18,7 @@ public class CwMwebPlugin: NSObject, FlutterPlugin { case "getPlatformVersion": result("iOS " + UIDevice.current.systemVersion) case "start": + stopServer() let args = call.arguments as? [String: String] let dataDir = args?["dataDir"] CwMwebPlugin.dataDir = dataDir diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index 98afc73ddc..df33003797 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:grpc/grpc.dart'; import 'package:path_provider/path_provider.dart'; import 'cw_mweb_platform_interface.dart'; From 5ec0e264ced153fee9e195533a8c9842915870f8 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 4 Sep 2024 11:06:52 -0700 Subject: [PATCH 130/203] [skip ci] wip adress generation --- .github/workflows/pr_test_build_android.yml | 1 + cw_bitcoin/lib/litecoin_wallet_addresses.dart | 102 ++++++++++++++++-- cw_bitcoin/pubspec.yaml | 5 + 3 files changed, 102 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index fb44b2fdee..904802f26e 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -112,6 +112,7 @@ jobs: # build mwebd: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd + git reset --hard 49c42597ce5036fe1065200c3c056d0aba5f1a58 cd /opt/android/cake_wallet/mwebd gomobile bind -target=android -androidapi 21 . mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 7db9d7baed..49ca81476a 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -1,4 +1,3 @@ -import 'package:convert/convert.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_bitcoin/utils.dart'; @@ -8,10 +7,88 @@ import 'package:cw_mweb/cw_mweb.dart'; import 'package:cw_mweb/mwebd.pb.dart'; import 'package:mobx/mobx.dart'; +import 'dart:typed_data'; +import 'package:bech32/bech32.dart'; +import 'package:r_crypto/r_crypto.dart'; + part 'litecoin_wallet_addresses.g.dart'; -class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalletAddresses; + + +class Keychain { + final ECPrivate scan; + ECPrivate? spend; + ECPublic? spendPubKey; + + + Keychain({required this.scan, this.spend, this.spendPubKey}) { + if (this.spend != null) { + spendPubKey = this.spend!.getPublic(); + } + } + + + static const HashTagAddress = 'A'; + + ECPrivate mi(int index) { + final input = BytesBuilder(); + + // Write HashTagAddress to the input + input.addByte(HashTagAddress.codeUnitAt(0)); + + // Write index to the input in little endian + final indexBytes = Uint8List(4); + final byteData = ByteData.view(indexBytes.buffer); + byteData.setUint32(0, index, Endian.little); + input.add(indexBytes); + + // Write scan to the input + input.add(scan.prive.raw); + + // Hash the input using Blake3 with a length of 32 bytes + final hash = rHash.hashString(HashType.blake3(length: 32), input.toString()); + + // Return the hash digest + var res = Uint8List.fromList(hash); + return ECPrivate.fromBytes(res); + } + + Keychain address(int index) { + + final miPub = this.mi(index).getPublic(); + final Bi = spendPubKey!.pubkeyAdd(miPub); + final Ai = Bi.pubkeyMult(ECPublic.fromBytes(scan.toBytes())); + // final miPubKey = ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16); + // final Bi = spendPubKey + miPubKey; + // return Uint8List.fromList(Ai.getEncoded(compressed: true) + Bi.getEncoded(compressed: true)); + final AiPriv = ECPrivate.fromBytes(Ai.toBytes()); + final BiPriv = ECPrivate.fromBytes(Bi.toBytes()); + + return Keychain(scan: AiPriv, spend: BiPriv); + } + + String addressString(int index) { + final address = this.address(index); + List bytes = []; + bytes.addAll(address.scan.toBytes()); + bytes.addAll(address.spend!.toBytes()); + return encodeMwebAddress(bytes); + } + + // Uint8List spendKey(int index) { + // final mi = this.mi(index); + // final spendKey = spend + ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16); + // return spendKey.getEncoded(compressed: true); + // } + + String encodeMwebAddress(List scriptPubKey) { + return bech32.encode(Bech32("ltcmweb", scriptPubKey)); + } +} + + +class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalletAddresses; abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store { LitecoinWalletAddressesBase( WalletInfo walletInfo, { @@ -40,21 +117,34 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed; List mwebAddrs = []; + List oldMwebAddrs = []; + Future topUpMweb(int index) async { final stub = await CwMweb.stub(); - while (mwebAddrs.length - index < 1000) { - final length = mwebAddrs.length; + while (oldMwebAddrs.length - index < 1000) { + final length = oldMwebAddrs.length; final resp = await stub.addresses(AddressRequest( fromIndex: length, toIndex: index + 1000, scanSecret: scanSecret, spendPubkey: spendPubkey, )); - if (mwebAddrs.length == length) { - mwebAddrs.addAll(resp.address); + if (oldMwebAddrs.length == length) { + oldMwebAddrs.addAll(resp.address); } } + + + Keychain k = Keychain(scan: ECPrivate.fromBytes(scanSecret), spendPubKey: ECPublic.fromBytes(spendPubkey),); + + + for (int i = 0; i < 10; i++) { + final address = k.addressString(i + 1000); + mwebAddrs.add(address); + } + print("old function: ${oldMwebAddrs.first} new function!: ${mwebAddrs.first}"); + } @override diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 29d1783192..b121a95385 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -41,6 +41,10 @@ dependencies: git: url: https://github.com/rafael-xmr/sp_scanner ref: sp_v4.0.0 + bech32: + git: + url: https://github.com/cake-tech/bech32.git + r_crypto: ^0.5.0 dev_dependencies: flutter_test: @@ -62,6 +66,7 @@ dependency_overrides: url: https://github.com/cake-tech/bitcoin_base ref: cake-update-v6 pointycastle: 3.7.4 + ffi: 2.1.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 7b5c36e5a714e5aa4788656a158289a258ba41dd Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 4 Sep 2024 15:50:56 -0700 Subject: [PATCH 131/203] wip --- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 171 +++++++++--------- .../com/cakewallet/mweb/CwMwebPlugin.kt | 6 + cw_mweb/ios/Classes/CwMwebPlugin.swift | 144 ++++++++------- cw_mweb/lib/cw_mweb.dart | 9 + cw_mweb/lib/cw_mweb_method_channel.dart | 10 + cw_mweb/lib/cw_mweb_platform_interface.dart | 4 + pubspec_base.yaml | 1 + 7 files changed, 201 insertions(+), 144 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 49ca81476a..c0940ca5d0 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -1,4 +1,6 @@ +import 'package:bech32/bech32.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:blockchain_utils/bech32/bech32_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_bitcoin/utils.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; @@ -7,88 +9,91 @@ import 'package:cw_mweb/cw_mweb.dart'; import 'package:cw_mweb/mwebd.pb.dart'; import 'package:mobx/mobx.dart'; -import 'dart:typed_data'; -import 'package:bech32/bech32.dart'; -import 'package:r_crypto/r_crypto.dart'; +// import 'dart:typed_data'; +// import 'package:bech32/bech32.dart'; +// import 'package:r_crypto/r_crypto.dart'; part 'litecoin_wallet_addresses.g.dart'; - - - -class Keychain { - final ECPrivate scan; - ECPrivate? spend; - ECPublic? spendPubKey; - - - Keychain({required this.scan, this.spend, this.spendPubKey}) { - if (this.spend != null) { - spendPubKey = this.spend!.getPublic(); - } - } - - - static const HashTagAddress = 'A'; - - ECPrivate mi(int index) { - final input = BytesBuilder(); - - // Write HashTagAddress to the input - input.addByte(HashTagAddress.codeUnitAt(0)); - - // Write index to the input in little endian - final indexBytes = Uint8List(4); - final byteData = ByteData.view(indexBytes.buffer); - byteData.setUint32(0, index, Endian.little); - input.add(indexBytes); - - // Write scan to the input - input.add(scan.prive.raw); - - // Hash the input using Blake3 with a length of 32 bytes - final hash = rHash.hashString(HashType.blake3(length: 32), input.toString()); - - // Return the hash digest - var res = Uint8List.fromList(hash); - return ECPrivate.fromBytes(res); - } - - Keychain address(int index) { - - final miPub = this.mi(index).getPublic(); - final Bi = spendPubKey!.pubkeyAdd(miPub); - final Ai = Bi.pubkeyMult(ECPublic.fromBytes(scan.toBytes())); - // final miPubKey = ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16); - // final Bi = spendPubKey + miPubKey; - // return Uint8List.fromList(Ai.getEncoded(compressed: true) + Bi.getEncoded(compressed: true)); - final AiPriv = ECPrivate.fromBytes(Ai.toBytes()); - final BiPriv = ECPrivate.fromBytes(Bi.toBytes()); - - return Keychain(scan: AiPriv, spend: BiPriv); - } - - String addressString(int index) { - final address = this.address(index); - List bytes = []; - bytes.addAll(address.scan.toBytes()); - bytes.addAll(address.spend!.toBytes()); - return encodeMwebAddress(bytes); - } - - // Uint8List spendKey(int index) { - // final mi = this.mi(index); - // final spendKey = spend + ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16); - // return spendKey.getEncoded(compressed: true); - // } - - String encodeMwebAddress(List scriptPubKey) { - return bech32.encode(Bech32("ltcmweb", scriptPubKey)); - } +// class Keychain { +// // ECPrivate scan; +// // ECPrivate? spend; +// ECPrivate scan; +// ECPrivate? spend; +// ECPublic? spendPubKey; + +// Keychain({required this.scan, this.spend, this.spendPubKey}) { +// if (this.spend != null) { +// spendPubKey = this.spend!.getPublic(); +// } +// } + +// static const HashTagAddress = 'A'; + +// ECPrivate mi(int index) { +// final input = BytesBuilder(); + +// // Write HashTagAddress to the input +// input.addByte(HashTagAddress.codeUnitAt(0)); + +// // Write index to the input in little endian +// final indexBytes = Uint8List(4); +// final byteData = ByteData.view(indexBytes.buffer); +// byteData.setUint32(0, index, Endian.little); +// input.add(indexBytes); + +// // Write scan to the input +// input.add(scan.prive.raw); + +// // Hash the input using Blake3 with a length of 32 bytes +// final hash = rHash.hashString(HashType.blake3(length: 32), input.toString()); + +// // Return the hash digest +// var res = Uint8List.fromList(hash); +// return ECPrivate.fromBytes(res); +// } + +// Keychain address(int index) { + +// final miPub = this.mi(index).getPublic(); +// final Bi = spendPubKey!.pubkeyAdd(miPub); +// // final Ai = Bi.pubkeyMult(ECPublic.fromBytes(scan.toBytes())); +// final Ai = Bi.tweakMul(scan.toBigInt()); + +// // final miPubKey = ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16); +// // final Bi = spendPubKey + miPubKey; +// // return Uint8List.fromList(Ai.getEncoded(compressed: true) + Bi.getEncoded(compressed: true)); +// final AiPriv = ECPrivate.fromBytes(Ai.toBytes()); +// final BiPriv = ECPrivate.fromBytes(Bi.toBytes()); + +// return Keychain(scan: AiPriv, spend: BiPriv); +// } + +// String addressString(int index) { +// final address = this.address(index); +// List bytes = []; +// bytes.addAll(address.scan.toBytes()); +// bytes.addAll(address.spend!.toBytes()); +// return encodeMwebAddress(bytes); +// } + +// // Uint8List spendKey(int index) { +// // final mi = this.mi(index); +// // final spendKey = spend + ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16); +// // return spendKey.getEncoded(compressed: true); +// // } + +// String encodeMwebAddress(List scriptPubKey) { +// return bech32.encode(Bech32("ltcmweb", scriptPubKey)); +// } +// } + +String encodeMwebAddress(List scriptPubKey) { + return bech32.encode(Bech32("ltcmweb1", scriptPubKey), 250); } - class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalletAddresses; + abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store { LitecoinWalletAddressesBase( WalletInfo walletInfo, { @@ -119,7 +124,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with List mwebAddrs = []; List oldMwebAddrs = []; - Future topUpMweb(int index) async { final stub = await CwMweb.stub(); while (oldMwebAddrs.length - index < 1000) { @@ -135,16 +139,19 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with } } - - Keychain k = Keychain(scan: ECPrivate.fromBytes(scanSecret), spendPubKey: ECPublic.fromBytes(spendPubkey),); - + // Keychain k = Keychain(scan: ECPrivate.fromBytes(scanSecret), spendPubKey: ECPublic.fromBytes(spendPubkey),); for (int i = 0; i < 10; i++) { - final address = k.addressString(i + 1000); + // final address = k.addressString(i + 1000); + final addressHex = + await CwMweb.address(hex.encode(scanSecret), hex.encode(spendPubkey), index); + // print(addressHex); + // print(hex.decode(addressHex!).length); + // return; + final address = encodeMwebAddress(hex.decode(addressHex!)); mwebAddrs.add(address); } print("old function: ${oldMwebAddrs.first} new function!: ${mwebAddrs.first}"); - } @override 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 b1180dd4a1..228725ef1d 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,6 +40,12 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { server = null port = null result.success(null) + } else if (call.method == "address") { + val scanSecret: String = call.argument("scanSecret") ?: "" + val spendPub: String = call.argument("spendPub") ?: "" + val index: Int = call.argument("index") ?: 0 + val res = Mwebd.addressIndex(scanSecret, spendPub) + result.success(res) } else { result.notImplemented() } diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index ed08d6748e..e68344a4b9 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -3,70 +3,90 @@ import UIKit import Mwebd 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 static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "cw_mweb", binaryMessenger: registrar.messenger()) + let instance = CwMwebPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } - private static var server: MwebdServer? - private static var port: Int = 0 - private static var dataDir: String? - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "getPlatformVersion": - result("iOS " + UIDevice.current.systemVersion) - case "start": - stopServer() - let args = call.arguments as? [String: String] - let dataDir = args?["dataDir"] - CwMwebPlugin.dataDir = dataDir - startServer(result: result) - case "stop": - stopServer() - result(nil) - default: - result(FlutterMethodNotImplemented) - } - } + private static var server: MwebdServer? + private static var port: Int = 0 + private static var dataDir: String? - private func startServer(result: @escaping FlutterResult) { - if CwMwebPlugin.server == nil { - var error: NSError? - CwMwebPlugin.server = MwebdNewServer("", CwMwebPlugin.dataDir, "", &error) - - if let server = CwMwebPlugin.server { - do { - print("Starting server...") - try server.start(0, ret0_: &CwMwebPlugin.port) - print("Server started successfully on port: \(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(CwMwebPlugin.port) - } - } + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + break + case "start": + stopServer() + let args = call.arguments as? [String: String] + let dataDir = args?["dataDir"] + CwMwebPlugin.dataDir = dataDir + startServer(result: result) + break + case "stop": + stopServer() + result(nil) + break + case "address": + let args = call.arguments as? [String: String] + let scanSecret = args?["scanSecret"] + let spendPub = args?["spendPub"] + let index = args?["index"] + result(address(scanSecret, spendPub, index)) + break + default: + result(FlutterMethodNotImplemented) + break + } + } - private func stopServer() { - print("Stopping server") - CwMwebPlugin.server?.stop() - CwMwebPlugin.server = nil - CwMwebPlugin.port = 0 - } + private func startServer(result: @escaping FlutterResult) { + if CwMwebPlugin.server == nil { + var error: NSError? + CwMwebPlugin.server = MwebdNewServer("", CwMwebPlugin.dataDir, "", &error) - deinit { - stopServer() - } + if let server = CwMwebPlugin.server { + do { + print("Starting server...") + try server.start(0, ret0_: &CwMwebPlugin.port) + print("Server started successfully on port: \(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(CwMwebPlugin.port) + } + } + + private func stopServer() { + print("Stopping server") + CwMwebPlugin.server?.stop() + CwMwebPlugin.server = nil + CwMwebPlugin.port = 0 + } + + private func address(_ scanSecret: String?, _ spendPub: String?, _ index: Int?) -> String? { + guard let scanSecret = scanSecret, let spendPub = spendPub, let index = index else { + print("Invalid arguments for address function") + return nil + } + + return MwebdAddressIndex(scanSecret, spendPub, UInt32(index)) + } + + deinit { + stopServer() + } } \ No newline at end of file diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index df33003797..46ae512a0e 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -53,6 +53,15 @@ class CwMweb { await cleanup(); } + static Future address(String scanSecret, String spendPub, int index) async { + // try { + // return (await CwMwebPlatform.instance.address(scan, spendPub, index))!; + // } catch (e) { + // print("error generating address!: $e"); + // } + return CwMwebPlatform.instance.address(scanSecret, spendPub, index); + } + static Future cleanup() async { await _clientChannel?.terminate(); _rpcClient = null; diff --git a/cw_mweb/lib/cw_mweb_method_channel.dart b/cw_mweb/lib/cw_mweb_method_channel.dart index 9451db3108..6f7ad22793 100644 --- a/cw_mweb/lib/cw_mweb_method_channel.dart +++ b/cw_mweb/lib/cw_mweb_method_channel.dart @@ -19,4 +19,14 @@ class MethodChannelCwMweb extends CwMwebPlatform { Future stop() async { await methodChannel.invokeMethod('stop'); } + + @override + Future address(String scanSecret, String spendPub, int index) async { + final result = await methodChannel.invokeMethod('address', { + 'scanSecret': scanSecret, + 'spendPub': spendPub, + 'index': index, + }); + return result; + } } diff --git a/cw_mweb/lib/cw_mweb_platform_interface.dart b/cw_mweb/lib/cw_mweb_platform_interface.dart index a5a46adbc1..a633193411 100644 --- a/cw_mweb/lib/cw_mweb_platform_interface.dart +++ b/cw_mweb/lib/cw_mweb_platform_interface.dart @@ -30,4 +30,8 @@ abstract class CwMwebPlatform extends PlatformInterface { Future stop() { throw UnimplementedError('stop() has not been implemented.'); } + + Future address(String scanSecret, String spendPub, int index) { + throw UnimplementedError('address(int) has not been implemented.'); + } } diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 6e07b989b0..f056540096 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -136,6 +136,7 @@ dependency_overrides: git: url: https://github.com/cake-tech/bitcoin_base ref: cake-update-v6 + ffi: 2.1.0 flutter_icons: image_path: "assets/images/app_logo.png" From aa6cac897e9bbd8479bd79f02708438be8946ecc Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 4 Sep 2024 17:48:56 -0700 Subject: [PATCH 132/203] wip --- cw_bitcoin/lib/electrum_wallet.dart | 11 +- cw_bitcoin/lib/electrum_wallet_addresses.dart | 3 +- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 133 ++++++------------ cw_bitcoin/pubspec.yaml | 1 - .../com/cakewallet/mweb/CwMwebPlugin.kt | 4 +- 5 files changed, 54 insertions(+), 98 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 521384e53d..ea99c6cd87 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'dart:isolate'; import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:cw_bitcoin/litecoin_wallet_addresses.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; @@ -795,7 +796,10 @@ abstract class ElectrumWalletBase throw BitcoinTransactionWrongBalanceException(); } - final changeAddress = await walletAddresses.getChangeAddress(); + final changeAddress = await walletAddresses.getChangeAddress( + outputs: outputs, + utxoDetails: utxoDetails, + ); final address = addressTypeFromStr(changeAddress, network); outputs.add(BitcoinOutput( address: address, @@ -2061,7 +2065,8 @@ abstract class ElectrumWalletBase _isTryingToConnect = true; Timer(Duration(seconds: 10), () { - if (this.syncStatus is NotConnectedSyncStatus || this.syncStatus is LostConnectionSyncStatus) { + if (this.syncStatus is NotConnectedSyncStatus || + this.syncStatus is LostConnectionSyncStatus) { this.electrumClient.connectToUri( node!.uri, useSSL: node!.useSSL ?? false, @@ -2387,6 +2392,8 @@ class PublicKeyWithDerivationPath { } BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) { + // print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + // print(network); if (network is BitcoinCashNetwork) { if (!address.startsWith("bitcoincash:") && (address.startsWith("q") || address.startsWith("p"))) { diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 388b3d4685..e442a03e8a 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -1,6 +1,7 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; +import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; @@ -239,7 +240,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } @action - Future getChangeAddress() async { + Future getChangeAddress({List? outputs, UtxoDetails? utxoDetails}) async { updateChangeAddresses(); if (changeAddresses.isEmpty) { diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index c0940ca5d0..ed321841e6 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -2,6 +2,7 @@ import 'package:bech32/bech32.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/bech32/bech32_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/utils.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; @@ -15,79 +16,6 @@ import 'package:mobx/mobx.dart'; part 'litecoin_wallet_addresses.g.dart'; -// class Keychain { -// // ECPrivate scan; -// // ECPrivate? spend; -// ECPrivate scan; -// ECPrivate? spend; -// ECPublic? spendPubKey; - -// Keychain({required this.scan, this.spend, this.spendPubKey}) { -// if (this.spend != null) { -// spendPubKey = this.spend!.getPublic(); -// } -// } - -// static const HashTagAddress = 'A'; - -// ECPrivate mi(int index) { -// final input = BytesBuilder(); - -// // Write HashTagAddress to the input -// input.addByte(HashTagAddress.codeUnitAt(0)); - -// // Write index to the input in little endian -// final indexBytes = Uint8List(4); -// final byteData = ByteData.view(indexBytes.buffer); -// byteData.setUint32(0, index, Endian.little); -// input.add(indexBytes); - -// // Write scan to the input -// input.add(scan.prive.raw); - -// // Hash the input using Blake3 with a length of 32 bytes -// final hash = rHash.hashString(HashType.blake3(length: 32), input.toString()); - -// // Return the hash digest -// var res = Uint8List.fromList(hash); -// return ECPrivate.fromBytes(res); -// } - -// Keychain address(int index) { - -// final miPub = this.mi(index).getPublic(); -// final Bi = spendPubKey!.pubkeyAdd(miPub); -// // final Ai = Bi.pubkeyMult(ECPublic.fromBytes(scan.toBytes())); -// final Ai = Bi.tweakMul(scan.toBigInt()); - -// // final miPubKey = ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16); -// // final Bi = spendPubKey + miPubKey; -// // return Uint8List.fromList(Ai.getEncoded(compressed: true) + Bi.getEncoded(compressed: true)); -// final AiPriv = ECPrivate.fromBytes(Ai.toBytes()); -// final BiPriv = ECPrivate.fromBytes(Bi.toBytes()); - -// return Keychain(scan: AiPriv, spend: BiPriv); -// } - -// String addressString(int index) { -// final address = this.address(index); -// List bytes = []; -// bytes.addAll(address.scan.toBytes()); -// bytes.addAll(address.spend!.toBytes()); -// return encodeMwebAddress(bytes); -// } - -// // Uint8List spendKey(int index) { -// // final mi = this.mi(index); -// // final spendKey = spend + ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16); -// // return spendKey.getEncoded(compressed: true); -// // } - -// String encodeMwebAddress(List scriptPubKey) { -// return bech32.encode(Bech32("ltcmweb", scriptPubKey)); -// } -// } - String encodeMwebAddress(List scriptPubKey) { return bech32.encode(Bech32("ltcmweb1", scriptPubKey), 250); } @@ -126,32 +54,28 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Future topUpMweb(int index) async { final stub = await CwMweb.stub(); - while (oldMwebAddrs.length - index < 1000) { - final length = oldMwebAddrs.length; + while (mwebAddrs.length - index < 1000) { + final length = mwebAddrs.length; final resp = await stub.addresses(AddressRequest( fromIndex: length, toIndex: index + 1000, scanSecret: scanSecret, spendPubkey: spendPubkey, )); - if (oldMwebAddrs.length == length) { - oldMwebAddrs.addAll(resp.address); + if (mwebAddrs.length == length) { + mwebAddrs.addAll(resp.address); } } - // Keychain k = Keychain(scan: ECPrivate.fromBytes(scanSecret), spendPubKey: ECPublic.fromBytes(spendPubkey),); - - for (int i = 0; i < 10; i++) { - // final address = k.addressString(i + 1000); - final addressHex = - await CwMweb.address(hex.encode(scanSecret), hex.encode(spendPubkey), index); - // print(addressHex); - // print(hex.decode(addressHex!).length); - // return; - final address = encodeMwebAddress(hex.decode(addressHex!)); - mwebAddrs.add(address); - } - print("old function: ${oldMwebAddrs.first} new function!: ${mwebAddrs.first}"); + // for (int i = 0; i < 10; i++) { + // final address = await CwMweb.address( + // hex.encode(scanSecret), + // hex.encode(spendPubkey), + // index + 1000, + // ); + // mwebAddrs.add(address!); + // } + // print("old function: ${oldMwebAddrs.first} new function!: ${mwebAddrs.first}"); } @override @@ -185,11 +109,38 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with @action @override - Future getChangeAddress() async { + Future getChangeAddress({List? outputs, UtxoDetails? utxoDetails}) async { + // use regular change address on peg in, otherwise use mweb for change address: + + if (outputs != null && utxoDetails != null) { + // check if this is a PEGIN: + bool outputsToMweb = false; + bool comesFromMweb = false; + + for (var i = 0; i < outputs.length; i++) { + // TODO: probably not the best way to tell if this is an mweb address + // (but it doesn't contain the "mweb" text at this stage) + if (outputs[i].address.toAddress(network).length > 110) { + outputsToMweb = true; + } + } + utxoDetails.availableInputs.forEach((element) { + if (element.address.contains("mweb")) { + comesFromMweb = true; + } + }); + + bool isPegIn = !comesFromMweb && outputsToMweb; + if (isPegIn && mwebEnabled) { + return super.getChangeAddress(); + } + } + if (mwebEnabled) { await topUpMweb(0); return mwebAddrs[0]; } + return super.getChangeAddress(); } } diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index b121a95385..8588969c47 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -44,7 +44,6 @@ dependencies: bech32: git: url: https://github.com/cake-tech/bech32.git - r_crypto: ^0.5.0 dev_dependencies: flutter_test: 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 228725ef1d..fea382487b 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 @@ -30,8 +30,6 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { if (call.method == "start") { server?.stop() val dataDir = call.argument("dataDir") ?: "" - // server = server ?: Mwebd.newServer("", dataDir, "") - // port = port ?: server?.start(0) server = server ?: Mwebd.newServer("", dataDir, "") port = server?.start(0) result.success(port) @@ -44,7 +42,7 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { val scanSecret: String = call.argument("scanSecret") ?: "" val spendPub: String = call.argument("spendPub") ?: "" val index: Int = call.argument("index") ?: 0 - val res = Mwebd.addressIndex(scanSecret, spendPub) + val res = Mwebd.addressIndex(scanSecret, spendPub, index.toString()) result.success(res) } else { result.notImplemented() From c8aa159f69dd1c29b01a2c13a92ffe351ca0cc92 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 5 Sep 2024 11:02:16 -0700 Subject: [PATCH 133/203] [skip ci] wip --- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 38 ++++++------------- .../com/cakewallet/mweb/CwMwebPlugin.kt | 6 +-- cw_mweb/ios/Classes/CwMwebPlugin.swift | 13 +------ cw_mweb/lib/cw_mweb.dart | 3 +- cw_mweb/lib/cw_mweb_method_channel.dart | 2 +- cw_mweb/lib/cw_mweb_platform_interface.dart | 4 +- 6 files changed, 22 insertions(+), 44 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index ed321841e6..dcdf4ca1b7 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:bech32/bech32.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/bech32/bech32_base.dart'; @@ -50,32 +52,18 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed; List mwebAddrs = []; - List oldMwebAddrs = []; Future topUpMweb(int index) async { - final stub = await CwMweb.stub(); + // generate up to index + 1000 addresses: while (mwebAddrs.length - index < 1000) { final length = mwebAddrs.length; - final resp = await stub.addresses(AddressRequest( - fromIndex: length, - toIndex: index + 1000, - scanSecret: scanSecret, - spendPubkey: spendPubkey, - )); - if (mwebAddrs.length == length) { - mwebAddrs.addAll(resp.address); - } + final address = await CwMweb.address( + Uint8List.fromList(scanSecret), + Uint8List.fromList(spendPubkey), + length, + ); + mwebAddrs.add(address!); } - - // for (int i = 0; i < 10; i++) { - // final address = await CwMweb.address( - // hex.encode(scanSecret), - // hex.encode(spendPubkey), - // index + 1000, - // ); - // mwebAddrs.add(address!); - // } - // print("old function: ${oldMwebAddrs.first} new function!: ${mwebAddrs.first}"); } @override @@ -84,7 +72,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType, }) { - if (addressType == SegwitAddresType.mweb && mwebEnabled) { + if (addressType == SegwitAddresType.mweb) { topUpMweb(index); return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1]; } @@ -97,11 +85,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType, }) async { - // if mweb isn't enabled we'll just return the regular address type which does effectively nothing - // sort of a hack but easier than trying to pull the mweb setting into the electrum_wallet_addresses initialization code - // (we want to avoid initializing the mweb.stub() if it's not enabled or we'd be starting the whole server for no reason and it's slow) - // TODO: find a way to do address generation without starting the whole mweb server - if (addressType == SegwitAddresType.mweb && mwebEnabled) { + if (addressType == SegwitAddresType.mweb) { await topUpMweb(index); } return getAddress(index: index, hd: hd, addressType: addressType); 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 fea382487b..47f6362e66 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 @@ -39,10 +39,10 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { port = null result.success(null) } else if (call.method == "address") { - val scanSecret: String = call.argument("scanSecret") ?: "" - val spendPub: String = call.argument("spendPub") ?: "" + val scanSecret: ByteArray = call.argument("scanSecret") ?: ByteArray(0) + val spendPub: ByteArray = call.argument("spendPub") ?: ByteArray(0) val index: Int = call.argument("index") ?: 0 - val res = Mwebd.addressIndex(scanSecret, spendPub, index.toString()) + val res = Mwebd.addressIndex(scanSecret, spendPub, index.toLong()) result.success(res) } else { result.notImplemented() diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index e68344a4b9..eb79d3e32f 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -30,11 +30,11 @@ public static func register(with registrar: FlutterPluginRegistrar) { result(nil) break case "address": - let args = call.arguments as? [String: String] + let args = call.arguments as? [String: Any] let scanSecret = args?["scanSecret"] let spendPub = args?["spendPub"] let index = args?["index"] - result(address(scanSecret, spendPub, index)) + result(MwebdAddressIndex(scanSecret, spendPub, index)) break default: result(FlutterMethodNotImplemented) @@ -77,15 +77,6 @@ public static func register(with registrar: FlutterPluginRegistrar) { CwMwebPlugin.port = 0 } - private func address(_ scanSecret: String?, _ spendPub: String?, _ index: Int?) -> String? { - guard let scanSecret = scanSecret, let spendPub = spendPub, let index = index else { - print("Invalid arguments for address function") - return nil - } - - return MwebdAddressIndex(scanSecret, spendPub, UInt32(index)) - } - deinit { stopServer() } diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index 46ae512a0e..e71ddde973 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'dart:typed_data'; import 'package:grpc/grpc.dart'; import 'package:path_provider/path_provider.dart'; @@ -53,7 +54,7 @@ class CwMweb { await cleanup(); } - static Future address(String scanSecret, String spendPub, int index) async { + static Future address(Uint8List scanSecret, Uint8List spendPub, int index) async { // try { // return (await CwMwebPlatform.instance.address(scan, spendPub, index))!; // } catch (e) { diff --git a/cw_mweb/lib/cw_mweb_method_channel.dart b/cw_mweb/lib/cw_mweb_method_channel.dart index 6f7ad22793..70e4a17895 100644 --- a/cw_mweb/lib/cw_mweb_method_channel.dart +++ b/cw_mweb/lib/cw_mweb_method_channel.dart @@ -21,7 +21,7 @@ class MethodChannelCwMweb extends CwMwebPlatform { } @override - Future address(String scanSecret, String spendPub, int index) async { + Future address(Uint8List scanSecret, Uint8List spendPub, int index) async { final result = await methodChannel.invokeMethod('address', { 'scanSecret': scanSecret, 'spendPub': spendPub, diff --git a/cw_mweb/lib/cw_mweb_platform_interface.dart b/cw_mweb/lib/cw_mweb_platform_interface.dart index a633193411..8cc80f3e9d 100644 --- a/cw_mweb/lib/cw_mweb_platform_interface.dart +++ b/cw_mweb/lib/cw_mweb_platform_interface.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'cw_mweb_method_channel.dart'; @@ -31,7 +33,7 @@ abstract class CwMwebPlatform extends PlatformInterface { throw UnimplementedError('stop() has not been implemented.'); } - Future address(String scanSecret, String spendPub, int index) { + Future address(Uint8List scanSecret, Uint8List spendPub, int index) { throw UnimplementedError('address(int) has not been implemented.'); } } From 20fa6a0bbdda928adf2177fed470fed0032b6764 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 5 Sep 2024 13:32:06 -0700 Subject: [PATCH 134/203] updates [skip ci] --- .github/workflows/pr_test_build_android.yml | 4 +-- .github/workflows/pr_test_build_linux.yml | 26 +++++++++++++++++++ .../com/cakewallet/mweb/CwMwebPlugin.kt | 2 +- scripts/android/build_mwebd.sh | 1 + scripts/ios/build_mwebd.sh | 1 + 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index 904802f26e..0ff5ef367f 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -112,7 +112,7 @@ jobs: # build mwebd: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd - git reset --hard 49c42597ce5036fe1065200c3c056d0aba5f1a58 + git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 cd /opt/android/cake_wallet/mwebd gomobile bind -target=android -androidapi 21 . mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ @@ -120,8 +120,6 @@ jobs: cd .. rm -rf mwebd - - - name: Generate KeyStore run: | cd /opt/android/cake_wallet/android/app diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index 7935dd1776..e425cffbc7 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -89,6 +89,32 @@ jobs: cd /opt/android/cake_wallet flutter pub get + - 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: | + # 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 + git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 + 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 $_ + cd .. + rm -rf mwebd + - name: Generate localization run: | cd /opt/android/cake_wallet 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 47f6362e66..396bce5891 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 @@ -42,7 +42,7 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { val scanSecret: ByteArray = call.argument("scanSecret") ?: ByteArray(0) val spendPub: ByteArray = call.argument("spendPub") ?: ByteArray(0) val index: Int = call.argument("index") ?: 0 - val res = Mwebd.addressIndex(scanSecret, spendPub, index.toLong()) + val res = Mwebd.addressIndex(scanSecret, spendPub, index) result.success(res) } else { result.notImplemented() diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh index d3a3bf0911..269e58d8c0 100755 --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -8,6 +8,7 @@ gomobile init # build mwebd: git clone https://github.com/ltcmweb/mwebd cd mwebd +git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 gomobile bind -target=android -androidapi 21 . mkdir -p ../../../cw_mweb/android/libs/ mv ./mwebd.aar $_ diff --git a/scripts/ios/build_mwebd.sh b/scripts/ios/build_mwebd.sh index 08dbd7cd0f..ae1726f233 100755 --- a/scripts/ios/build_mwebd.sh +++ b/scripts/ios/build_mwebd.sh @@ -7,6 +7,7 @@ gomobile init # build mwebd: git clone https://github.com/ltcmweb/mwebd cd mwebd +git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 gomobile bind -target=ios . mv -fn ./Mwebd.xcframework ../../../ios/ # cleanup: From 14891ec4f5c57838608e4c23937433f1b9fe36f3 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 5 Sep 2024 14:40:13 -0700 Subject: [PATCH 135/203] ios fixes --- .../main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt | 2 +- cw_mweb/ios/Classes/CwMwebPlugin.swift | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) 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 396bce5891..57ae3d4c3f 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 @@ -42,7 +42,7 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { val scanSecret: ByteArray = call.argument("scanSecret") ?: ByteArray(0) val spendPub: ByteArray = call.argument("spendPub") ?: ByteArray(0) val index: Int = call.argument("index") ?: 0 - val res = Mwebd.addressIndex(scanSecret, spendPub, index) + val res = Mwebd.address(scanSecret, spendPub, index) result.success(res) } else { result.notImplemented() diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index eb79d3e32f..81ebc4a87a 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -31,10 +31,10 @@ public static func register(with registrar: FlutterPluginRegistrar) { break case "address": let args = call.arguments as? [String: Any] - let scanSecret = args?["scanSecret"] - let spendPub = args?["spendPub"] - let index = args?["index"] - result(MwebdAddressIndex(scanSecret, spendPub, index)) + let scanSecret = args?["scanSecret"] as! Data + let spendPub = args?["spendPub"] as! Data + let index = args?["index"] as! Int32 + result(MwebdAddress(scanSecret, spendPub, index)) break default: result(FlutterMethodNotImplemented) @@ -80,4 +80,4 @@ public static func register(with registrar: FlutterPluginRegistrar) { deinit { stopServer() } -} \ No newline at end of file +} From 5dbc414e33ed3a1796eb980a7dbf94b7d13becca Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 5 Sep 2024 14:48:11 -0700 Subject: [PATCH 136/203] fix workflows + ios fix --- .github/workflows/pr_test_build_android.yml | 2 +- .github/workflows/pr_test_build_linux.yml | 2 +- cw_mweb/ios/Classes/CwMwebPlugin.swift | 13 ++++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index 0ff5ef367f..306eb82828 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -112,8 +112,8 @@ jobs: # build mwebd: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd - git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 cd /opt/android/cake_wallet/mwebd + git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 gomobile bind -target=android -androidapi 21 . mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index e425cffbc7..9faac2533e 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -107,8 +107,8 @@ jobs: # build mwebd: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd - git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 cd /opt/android/cake_wallet/mwebd + git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 gomobile bind -target=android -androidapi 21 . mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index 81ebc4a87a..f1fd78cd89 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -30,11 +30,14 @@ public static func register(with registrar: FlutterPluginRegistrar) { result(nil) break case "address": - let args = call.arguments as? [String: Any] - let scanSecret = args?["scanSecret"] as! Data - let spendPub = args?["spendPub"] as! Data - let index = args?["index"] as! Int32 - result(MwebdAddress(scanSecret, spendPub, index)) + let args = call.arguments as! [String: Any] + let scanSecret = args["scanSecret"] as! FlutterStandardTypedData + let spendPub = args["spendPub"] as! FlutterStandardTypedData + let index = args["index"] as! Int32 + + let scanSecretData = scanSecret.data + let spendPubData = spendPub.data + result(MwebdAddress(scanSecretData, spendPubData, index)) break default: result(FlutterMethodNotImplemented) From bae741acaf1b56823391533ad8176629524bbf45 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 6 Sep 2024 08:40:30 -0700 Subject: [PATCH 137/203] test old mweb version --- .github/workflows/pr_test_build_android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index 074181f101..80198155c9 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -113,7 +113,7 @@ jobs: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd - git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 + git reset --hard 8dc343cf555e30ea909c5905bc8d6ac67b7fefcf gomobile bind -target=android -androidapi 21 . mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ From ec60fa9dc2bb1cdf78f008d780a488529c07978e Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 6 Sep 2024 08:45:13 -0700 Subject: [PATCH 138/203] update go version and mwebd hash --- .github/workflows/pr_test_build_android.yml | 8 ++++---- .github/workflows/pr_test_build_linux.yml | 8 ++++---- scripts/android/build_mwebd.sh | 8 ++++---- scripts/ios/build_mwebd.sh | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index 80198155c9..2253cc5515 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -96,9 +96,9 @@ jobs: - 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 + # install go > 1.23: + wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz + sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.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 @@ -113,7 +113,7 @@ jobs: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd - git reset --hard 8dc343cf555e30ea909c5905bc8d6ac67b7fefcf + git reset --hard f6ea8a9e3d348b01bb44f03a1cc4ad65b0abe935 gomobile bind -target=android -androidapi 21 . mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ mv ./mwebd.aar $_ diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index 5f4898f6b3..50acdce943 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -91,9 +91,9 @@ jobs: - 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 + # install go > 1.23: + wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz + sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.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 @@ -108,7 +108,7 @@ jobs: cd /opt/android/cake_wallet git clone https://github.com/ltcmweb/mwebd cd /opt/android/cake_wallet/mwebd - git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 + git reset --hard f6ea8a9e3d348b01bb44f03a1cc4ad65b0abe935 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 269e58d8c0..bbe436ff68 100755 --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -1,6 +1,6 @@ -# 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 +# install go > 1.23: +wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz +sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.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 @@ -8,7 +8,7 @@ gomobile init # build mwebd: git clone https://github.com/ltcmweb/mwebd cd mwebd -git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 +git reset --hard f6ea8a9e3d348b01bb44f03a1cc4ad65b0abe935 gomobile bind -target=android -androidapi 21 . mkdir -p ../../../cw_mweb/android/libs/ mv ./mwebd.aar $_ diff --git a/scripts/ios/build_mwebd.sh b/scripts/ios/build_mwebd.sh index ae1726f233..ee1658800a 100755 --- a/scripts/ios/build_mwebd.sh +++ b/scripts/ios/build_mwebd.sh @@ -1,5 +1,5 @@ #!/bin/bash -# install go > 1.21: +# install go > 1.23: brew install go export PATH=$PATH:~/go/bin go install golang.org/x/mobile/cmd/gomobile@latest @@ -7,7 +7,7 @@ gomobile init # build mwebd: git clone https://github.com/ltcmweb/mwebd cd mwebd -git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3 +git reset --hard f6ea8a9e3d348b01bb44f03a1cc4ad65b0abe935 gomobile bind -target=ios . mv -fn ./Mwebd.xcframework ../../../ios/ # cleanup: From 56c505da5927794d60b8c4813fbd99f669cbc11b Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 9 Sep 2024 12:46:53 -0700 Subject: [PATCH 139/203] review updates pt.1 --- .github/workflows/pr_test_build_android.yml | 11 +-- .github/workflows/pr_test_build_linux.yml | 11 +-- cw_bitcoin/lib/bitcoin_mnemonic.dart | 3 +- cw_bitcoin/lib/electrum_wallet.dart | 7 +- cw_bitcoin/lib/electrum_wallet_addresses.dart | 87 ++++++++++++------- cw_bitcoin/lib/litecoin_wallet_service.dart | 8 +- cw_core/lib/get_height_by_date.dart | 20 +++++ lib/bitcoin/cw_bitcoin.dart | 3 + lib/src/widgets/blockchain_height_widget.dart | 3 +- scripts/android/build_all.sh | 3 +- scripts/android/build_mwebd.sh | 17 ++-- scripts/ios/build_all.sh | 2 +- scripts/ios/build_mwebd.sh | 13 +-- tool/configure.dart | 1 + 14 files changed, 115 insertions(+), 74 deletions(-) diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index 2253cc5515..6daeb84eee 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -110,15 +110,8 @@ jobs: 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 - git reset --hard f6ea8a9e3d348b01bb44f03a1cc4ad65b0abe935 - gomobile bind -target=android -androidapi 21 . - mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ - mv ./mwebd.aar $_ - cd .. - rm -rf mwebd + cd /opt/android/cake_wallet/scripts/android/ + ./build_mwebd.sh - name: Generate KeyStore run: | diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index 50acdce943..116dc3c952 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -105,15 +105,8 @@ jobs: 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 - git reset --hard f6ea8a9e3d348b01bb44f03a1cc4ad65b0abe935 - gomobile bind -target=android -androidapi 21 . - mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ - mv ./mwebd.aar $_ - cd .. - rm -rf mwebd + cd /opt/android/cake_wallet/scripts/android/ + ./build_mwebd.sh - name: Generate localization run: | diff --git a/cw_bitcoin/lib/bitcoin_mnemonic.dart b/cw_bitcoin/lib/bitcoin_mnemonic.dart index 0749627e92..21ff3891ec 100644 --- a/cw_bitcoin/lib/bitcoin_mnemonic.dart +++ b/cw_bitcoin/lib/bitcoin_mnemonic.dart @@ -8,6 +8,7 @@ import 'package:cw_core/sec_random_native.dart'; import 'package:cw_core/utils/text_normalizer.dart'; const segwit = '100'; +const mweb = 'eb'; final wordlist = englishWordlist; double logBase(num x, num base) => log(x) / log(base); @@ -125,7 +126,7 @@ Future mnemonicToSeedBytes(String mnemonic, return Uint8List.fromList(bytes); } -bool matchesAnyPrefix(String mnemonic) => prefixMatches(mnemonic, [segwit]).any((el) => el); +bool matchesAnyPrefix(String mnemonic) => prefixMatches(mnemonic, [segwit, mweb]).any((el) => el); bool validateMnemonic(String mnemonic, {String prefix = segwit}) { try { diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index ea99c6cd87..222a16e91c 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1689,12 +1689,15 @@ abstract class ElectrumWalletBase final Map historiesWithDetails = {}; if (type == WalletType.bitcoin) { - await Future.wait(ADDRESS_TYPES + await Future.wait(BITCOIN_ADDRESS_TYPES .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); } else if (type == WalletType.bitcoinCash) { + await Future.wait(BITCOIN_CASH_ADDRESS_TYPES + .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); await fetchTransactionsForAddressType(historiesWithDetails, P2pkhAddressType.p2pkh); } else if (type == WalletType.litecoin) { - await fetchTransactionsForAddressType(historiesWithDetails, SegwitAddresType.p2wpkh); + await Future.wait(LITECOIN_ADDRESS_TYPES + .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); } transactionHistory.transactions.values.forEach((tx) async { diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index e442a03e8a..f320ef1106 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -11,15 +11,23 @@ part 'electrum_wallet_addresses.g.dart'; class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses; -const List ADDRESS_TYPES = [ +const List BITCOIN_ADDRESS_TYPES = [ SegwitAddresType.p2wpkh, P2pkhAddressType.p2pkh, SegwitAddresType.p2tr, SegwitAddresType.p2wsh, - SegwitAddresType.mweb, P2shAddressType.p2wpkhInP2sh, ]; +const List LITECOIN_ADDRESS_TYPES = [ + SegwitAddresType.p2wpkh, + SegwitAddresType.mweb, +]; + +const List BITCOIN_CASH_ADDRESS_TYPES = [ + P2pkhAddressType.p2pkh, +]; + abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { ElectrumWalletAddressesBase( WalletInfo walletInfo, { @@ -327,13 +335,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { }) => ''; - Future getAddressAsync({ - required int index, - required Bip32Slip10Secp256k1 hd, - BitcoinAddressType? addressType, - }) async => - getAddress(index: index, hd: hd, addressType: addressType); - void addBitcoinAddressTypes() { final lastP2wpkh = _addresses .where((addressRecord) => @@ -393,6 +394,37 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { }); } + void addLitecoinAddressTypes() { + 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 lastMweb = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb)); + if (lastMweb.address != address) { + addressesMap[lastMweb.address] = 'MWEB'; + } else { + addressesMap[address] = 'Active - MWEB'; + } + } + + void addBitcoinCashAddressTypes() { + final lastP2pkh = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, P2pkhAddressType.p2pkh)); + if (lastP2pkh.address != address) { + addressesMap[lastP2pkh.address] = 'P2PKH'; + } else { + addressesMap[address] = 'Active - P2PKH'; + } + } + @override Future updateAddressesInBox() async { try { @@ -404,29 +436,18 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { allAddressesMap[addressRecord.address] = addressRecord.name; }); - if (walletInfo.type == WalletType.bitcoin) { - addBitcoinAddressTypes(); - } - - 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 - P2WPKH'; - } - - final lastMweb = _addresses.firstWhere( - (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb)); - if (lastMweb.address != address) { - addressesMap[lastMweb.address] = 'MWEB'; - } else { - addressesMap[address] = 'Active - MWEB'; - } + switch (walletInfo.type) { + case WalletType.bitcoin: + addBitcoinAddressTypes(); + break; + case WalletType.litecoin: + addLitecoinAddressTypes(); + break; + case WalletType.bitcoinCash: + addBitcoinCashAddressTypes(); + break; + default: + break; } await saveAddressesInBox(); @@ -548,7 +569,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { for (var i = startIndex; i < count + startIndex; i++) { final address = BitcoinAddressRecord( - await getAddressAsync(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType), + getAddress(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType), index: i, isHidden: isHidden, type: type ?? addressPageType, diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index da36020246..25438007a7 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -1,10 +1,10 @@ import 'dart:io'; import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart'; +import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:hive/hive.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; -import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart'; import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart'; import 'package:cw_bitcoin/litecoin_wallet.dart'; import 'package:cw_core/wallet_service.dart'; @@ -150,9 +150,9 @@ class LitecoinWalletService extends WalletService< @override Future restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { - // if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { - // throw LitecoinMnemonicIsIncorrectException(); - // } + if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { + throw LitecoinMnemonicIsIncorrectException(); + } final wallet = await LitecoinWalletBase.create( password: credentials.password!, diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index 204f03d62e..52136fdcd2 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -267,6 +267,11 @@ const bitcoinDates = { "2023-01": 769810, }; + +const Map litecoinDates = { + // TODO: add litecoin dates +}; + int getBitcoinHeightByDate({required DateTime date}) { String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; final closestKey = bitcoinDates.keys @@ -300,6 +305,21 @@ DateTime getDateByBitcoinHeight(int height) { return estimatedDate; } +int getLitecoinHeightByDate({required DateTime date}) { + String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; + final closestKey = litecoinDates.keys + .firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => litecoinDates.keys.last); + final beginningBlock = litecoinDates[dateKey] ?? litecoinDates[closestKey]!; + + final startOfMonth = DateTime(date.year, date.month); + final daysDifference = date.difference(startOfMonth).inDays; + + // approximately 6 blocks per hour, 24 hours per day + int estimatedBlocksSinceStartOfMonth = (daysDifference * 24 * 6); + + return beginningBlock + estimatedBlocksSinceStartOfMonth; +} + // TODO: enhance all of this global const lists const wowDates = { "2023-12": 583048, diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index e6d1893562..0c3e27930f 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -529,6 +529,9 @@ class CWBitcoin extends Bitcoin { @override int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date); + @override + int getLitecoinHeightByDate({required DateTime date}) => getLitecoinHeightByDate(date: date); + @override Future rescan(Object wallet, {required int height, bool? doSingleScan}) async { final bitcoinWallet = wallet as ElectrumWallet; diff --git a/lib/src/widgets/blockchain_height_widget.dart b/lib/src/widgets/blockchain_height_widget.dart index b9ab45e555..f3bcbc25c6 100644 --- a/lib/src/widgets/blockchain_height_widget.dart +++ b/lib/src/widgets/blockchain_height_widget.dart @@ -168,8 +168,7 @@ class BlockchainHeightState extends State { if (date != null) { int height; if (widget.isMwebScan) { - throw UnimplementedError(); - // height = bitcoin!.getMwebHeightByDate(date: date); + height = bitcoin!.getLitecoinHeightByDate(date: date); } else if (widget.isSilentPaymentsScan) { height = bitcoin!.getHeightByDate(date: date); } else { diff --git a/scripts/android/build_all.sh b/scripts/android/build_all.sh index ec70f02a60..ad4ec984be 100755 --- a/scripts/android/build_all.sh +++ b/scripts/android/build_all.sh @@ -10,6 +10,7 @@ DIR=$(dirname "$0") case $APP_ANDROID_TYPE in "monero.com") $DIR/build_monero_all.sh ;; "cakewallet") $DIR/build_monero_all.sh - $DIR/build_haven_all.sh ;; + $DIR/build_haven_all.sh + $DIR/build_mwebd.sh ;; "haven") $DIR/build_haven_all.sh ;; esac diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh index bbe436ff68..1e4f51be99 100755 --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -1,10 +1,13 @@ -# install go > 1.23: -wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz -sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.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 +if [[ "$1" == "--install" ]]; then + # install go > 1.23: + wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz + sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.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 +fi + # build mwebd: git clone https://github.com/ltcmweb/mwebd cd mwebd diff --git a/scripts/ios/build_all.sh b/scripts/ios/build_all.sh index 565679e2dc..ba5c55a1f5 100755 --- a/scripts/ios/build_all.sh +++ b/scripts/ios/build_all.sh @@ -9,6 +9,6 @@ DIR=$(dirname "$0") case $APP_IOS_TYPE in "monero.com") $DIR/build_monero_all.sh ;; - "cakewallet") $DIR/build_monero_all.sh && $DIR/build_haven.sh ;; + "cakewallet") $DIR/build_monero_all.sh && $DIR/build_haven.sh && $DIR/build_mwebd.sh ;; "haven") $DIR/build_haven_all.sh ;; esac diff --git a/scripts/ios/build_mwebd.sh b/scripts/ios/build_mwebd.sh index ee1658800a..b0d67115ef 100755 --- a/scripts/ios/build_mwebd.sh +++ b/scripts/ios/build_mwebd.sh @@ -1,9 +1,12 @@ #!/bin/bash -# install go > 1.23: -brew install go -export PATH=$PATH:~/go/bin -go install golang.org/x/mobile/cmd/gomobile@latest -gomobile init +if [[ "$1" == "--install" ]]; then + # install go > 1.23: + brew install go + export PATH=$PATH:~/go/bin + go install golang.org/x/mobile/cmd/gomobile@latest + gomobile init +fi + # build mwebd: git clone https://github.com/ltcmweb/mwebd cd mwebd diff --git a/tool/configure.dart b/tool/configure.dart index 326a794ba5..8ac6c76940 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -214,6 +214,7 @@ abstract class Bitcoin { {int? outputsCount, int? size}); int feeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size}); int getHeightByDate({required DateTime date}); + int getLitecoinHeightByDate({required DateTime date}); Future rescan(Object wallet, {required int height, bool? doSingleScan}); Future getNodeIsElectrsSPEnabled(Object wallet); void deleteSilentPaymentAddress(Object wallet, String address); From 0f5ee476f18381e407c642589f52eb95b26bc1cc Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 9 Sep 2024 12:49:22 -0700 Subject: [PATCH 140/203] Update cw_bitcoin/lib/litecoin_wallet.dart Co-authored-by: Omar Hatem --- 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 b69ba59ddb..e3f68888e8 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -317,11 +317,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final oldBoxName = "${walletInfo.name.replaceAll(" ", "_")}_${MwebUtxo.boxName}"; final newBoxName = "${newWalletName.replaceAll(" ", "_")}_${MwebUtxo.boxName}"; - final oldBox = await Hive.openBox(oldBoxName); + final oldBox = await CakeHive.openBox(oldBoxName); mwebUtxosBox = await CakeHive.openBox(newBoxName); for (final key in oldBox.keys) { await mwebUtxosBox.put(key, oldBox.get(key)!); } + oldBox.deleteFromDisk(); await super.renameWalletFiles(newWalletName); } From 577aec0f042b8a981b7dcc59d0759ef7065e492e Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Mon, 9 Sep 2024 23:24:09 +0300 Subject: [PATCH 141/203] remove non-litecoin address types regex [skip ci] --- lib/core/address_validator.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 03d1668eea..5453ef7e65 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -27,9 +27,9 @@ class AddressValidator extends TextValidator { return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$' '|^[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}\$|^${SilentPaymentAddress.regex.pattern}\$'; + return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|^${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${SilentPaymentAddress.regex.pattern}\$'; case CryptoCurrency.ltc: - return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${MwebAddress.regex.pattern}\$'; + return '^${P2wpkhAddress.regex.pattern}\$|^${MwebAddress.regex.pattern}\$'; case CryptoCurrency.nano: return '[0-9a-zA-Z_]'; case CryptoCurrency.banano: From 728a0bf4b2880be745b544b0fc839b8bc2a2f82e Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 9 Sep 2024 13:24:50 -0700 Subject: [PATCH 142/203] more minor fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 11 +++----- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 25 +------------------ lib/bitcoin/cw_bitcoin.dart | 2 +- tool/configure.dart | 2 +- 4 files changed, 7 insertions(+), 33 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index e3f68888e8..e2010c5207 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -776,6 +776,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { script: outputs[0].toOutput.scriptPubKey, value: utxos.sumOfUtxosValue()) ]; } + // https://github.com/ltcmweb/mwebd?tab=readme-ov-file#fee-estimation final preOutputSum = outputs.fold(BigInt.zero, (acc, output) => acc + output.toOutput.amount); final fee = utxos.sumOfUtxosValue() - preOutputSum; @@ -891,19 +892,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { _utxoStream?.cancel(); } - void setMwebEnabled(bool enabled) { + Future setMwebEnabled(bool enabled) async { if (mwebEnabled == enabled) { return; } mwebEnabled = enabled; (walletAddresses as LitecoinWalletAddresses).mwebEnabled = enabled; - if (enabled) { - // generate inital mweb addresses: - (walletAddresses as LitecoinWalletAddresses).topUpMweb(0); - } - stopSync(); - startSync(); + await stopSync(); + await startSync(); } Future getStub() async { diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index dcdf4ca1b7..9f63f66624 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -2,20 +2,14 @@ import 'dart:typed_data'; import 'package:bech32/bech32.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; -import 'package:blockchain_utils/bech32/bech32_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_bitcoin/electrum_wallet.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'; -// import 'dart:typed_data'; -// import 'package:bech32/bech32.dart'; -// import 'package:r_crypto/r_crypto.dart'; - part 'litecoin_wallet_addresses.g.dart'; String encodeMwebAddress(List scriptPubKey) { @@ -36,12 +30,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialRegularAddressIndex, super.initialChangeAddressIndex, }) : super(walletInfo) { - if (mwebEnabled) { - // give the server a few seconds to start up before trying to get the addresses: - Future.delayed(const Duration(seconds: 5), () async { - await topUpMweb(0); - }); - } + topUpMweb(0); } final Bip32Slip10Secp256k1 mwebHd; @@ -79,18 +68,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with return generateP2WPKHAddress(hd: hd, index: index, network: network); } - @override - Future getAddressAsync({ - required int index, - required Bip32Slip10Secp256k1 hd, - BitcoinAddressType? addressType, - }) async { - if (addressType == SegwitAddresType.mweb) { - await topUpMweb(index); - } - return getAddress(index: index, hd: hd, addressType: addressType); - } - @action @override Future getChangeAddress({List? outputs, UtxoDetails? utxoDetails}) async { diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 0c3e27930f..0743982636 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -557,7 +557,7 @@ class CWBitcoin extends Bitcoin { } @override - void setMwebEnabled(Object wallet, bool enabled) { + Future setMwebEnabled(Object wallet, bool enabled) async { final litecoinWallet = wallet as LitecoinWallet; litecoinWallet.setMwebEnabled(enabled); } diff --git a/tool/configure.dart b/tool/configure.dart index 8ac6c76940..4605c99fc9 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -224,7 +224,7 @@ 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); + Future setMwebEnabled(Object wallet, bool enabled); bool getMwebEnabled(Object wallet); } """; From 18a9bdea87da2a773cec8511ce66521e6af70b15 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Mon, 9 Sep 2024 23:55:16 +0300 Subject: [PATCH 143/203] remove duplicate [skip ci] --- cw_bitcoin/lib/electrum_wallet.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 2db6eec1cb..b04ee382f3 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1694,7 +1694,6 @@ abstract class ElectrumWalletBase } else if (type == WalletType.bitcoinCash) { await Future.wait(BITCOIN_CASH_ADDRESS_TYPES .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); - await fetchTransactionsForAddressType(historiesWithDetails, P2pkhAddressType.p2pkh); } else if (type == WalletType.litecoin) { await Future.wait(LITECOIN_ADDRESS_TYPES .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); From dc77dee8493d5a9a51f4ec3a9e06cf49b0dba929 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 9 Sep 2024 14:03:09 -0700 Subject: [PATCH 144/203] Update lib/store/settings_store.dart Co-authored-by: Omar Hatem --- lib/store/settings_store.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 1b1536fddb..474fd65249 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -950,7 +950,7 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false; final mwebAlwaysScan = sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false; final mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; - final mwebEnabled = sharedPreferences.getBool(PreferencesKey.hasEnabledMwebBefore) ?? false; + final mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; final hasEnabledMwebBefore = sharedPreferences.getBool(PreferencesKey.hasEnabledMwebBefore) ?? false; From fd578dd530d6162f267e878621b13f8f6cdf087c Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 9 Sep 2024 14:04:49 -0700 Subject: [PATCH 145/203] script updates, swap params on createLitecoinWalletService --- .github/workflows/pr_test_build_android.yml | 3 +-- .github/workflows/pr_test_build_linux.yml | 2 +- lib/di.dart | 2 +- scripts/android/build_mwebd.sh | 4 +++- scripts/ios/build_mwebd.sh | 4 +++- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index 6daeb84eee..3cc67e53a4 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -109,9 +109,8 @@ jobs: # 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/scripts/android/ - ./build_mwebd.sh + ./build_mwebd.sh --dont-install - name: Generate KeyStore run: | diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index 116dc3c952..f005843456 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -106,7 +106,7 @@ jobs: export PATH=$PATH:~/go/bin # build mwebd: cd /opt/android/cake_wallet/scripts/android/ - ./build_mwebd.sh + ./build_mwebd.sh --dont-install - name: Generate localization run: | diff --git a/lib/di.dart b/lib/di.dart index 9c3c5bcb28..30727f33c8 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1000,8 +1000,8 @@ Future setup({ return bitcoin!.createLitecoinWalletService( _walletInfoSource, _unspentCoinsInfoSource, - getIt.get().mwebAlwaysScan, SettingsStoreBase.walletPasswordDirectInput, + getIt.get().mwebAlwaysScan, ); case WalletType.ethereum: return ethereum!.createEthereumWalletService( diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh index 1e4f51be99..90dbc4c203 100755 --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -1,4 +1,6 @@ -if [[ "$1" == "--install" ]]; then +if [[ "$1" == "--dont-install" ]]; then + echo "Skipping Go installation as per --dont-install flag" +else # install go > 1.23: wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.linux-amd64.tar.gz diff --git a/scripts/ios/build_mwebd.sh b/scripts/ios/build_mwebd.sh index b0d67115ef..f0fa64605b 100755 --- a/scripts/ios/build_mwebd.sh +++ b/scripts/ios/build_mwebd.sh @@ -1,5 +1,7 @@ #!/bin/bash -if [[ "$1" == "--install" ]]; then +if [[ "$1" == "--dont-install" ]]; then + echo "Skipping Go installation as per --dont-install flag" +else # install go > 1.23: brew install go export PATH=$PATH:~/go/bin From 3963233c0e84f896a8af33a298b9e18067eea4fd Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 9 Sep 2024 15:39:19 -0700 Subject: [PATCH 146/203] topup fix --- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 9f63f66624..26a18df00d 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -62,8 +62,9 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with BitcoinAddressType? addressType, }) { if (addressType == SegwitAddresType.mweb) { - topUpMweb(index); - return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1]; + topUpMweb(index).then((value) { + return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1]; + }); } return generateP2WPKHAddress(hd: hd, index: index, network: network); } From 1061b7de0103e23cb0478ea5699f89c785d9289d Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 9 Sep 2024 20:59:52 -0700 Subject: [PATCH 147/203] [skip ci] wip --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 9 ++- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 73 ++++++++++++++++--- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index f320ef1106..e40f6fd893 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -335,6 +335,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { }) => ''; + Future getAddressAsync({ + required int index, + required Bip32Slip10Secp256k1 hd, + BitcoinAddressType? addressType, + }) async => + getAddress(index: index, hd: hd, addressType: addressType); + void addBitcoinAddressTypes() { final lastP2wpkh = _addresses .where((addressRecord) => @@ -569,7 +576,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 26a18df00d..1e6b1a3896 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:typed_data'; import 'package:bech32/bech32.dart'; @@ -30,29 +31,63 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialRegularAddressIndex, super.initialChangeAddressIndex, }) : super(walletInfo) { - topUpMweb(0); + initMwebAddresses(); + // topUpMweb(0); + print("initialized LitecoinWalletAddressesBase"); } final Bip32Slip10Secp256k1 mwebHd; bool mwebEnabled; + int mwebTopUpIndex = 1000; + List mwebAddrs = []; + static Timer? mwebTopUpTimer; List get scanSecret => mwebHd.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw; List get spendPubkey => mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed; - List mwebAddrs = []; - - Future topUpMweb(int index) async { - // generate up to index + 1000 addresses: - while (mwebAddrs.length - index < 1000) { - final length = mwebAddrs.length; + // Future topUpMweb(int index) async { + // // generate up to index + 1000 addresses: + // while (mwebAddrs.length - index < 1000) { + // final length = mwebAddrs.length; + // final address = await CwMweb.address( + // Uint8List.fromList(scanSecret), + // Uint8List.fromList(spendPubkey), + // length, + // ); + // mwebAddrs.add(address!); + // } + // } + + Future initMwebAddresses() async { + print("mweb addresses: ${mwebAddrs.length}"); + const int INITIAL_MWEB_ADDRESSES = 25; + // make sure we have at least 20 addresses: + while (mwebAddrs.length < INITIAL_MWEB_ADDRESSES) { final address = await CwMweb.address( Uint8List.fromList(scanSecret), Uint8List.fromList(spendPubkey), - length, + mwebAddrs.length, ); mwebAddrs.add(address!); } + + // set up a periodic task to fill up the mweb addresses to 1000: + mwebTopUpTimer?.cancel(); + mwebTopUpTimer = Timer.periodic(Duration(seconds: 5), (timer) async { + if (mwebAddrs.length >= mwebTopUpIndex) { + return; + } + for (int i = 0; i < 10; i++) { + final address = await CwMweb.address( + Uint8List.fromList(scanSecret), + Uint8List.fromList(spendPubkey), + mwebAddrs.length, + ); + mwebAddrs.add(address!); + } + print("mweb addresses: ${mwebAddrs.length}"); + }); } @override @@ -62,13 +97,28 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with BitcoinAddressType? addressType, }) { if (addressType == SegwitAddresType.mweb) { - topUpMweb(index).then((value) { - 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 Bip32Slip10Secp256k1 hd, + BitcoinAddressType? addressType, + }) async { + if (addressType == SegwitAddresType.mweb) { + if (index + 1000 > mwebTopUpIndex) { + mwebTopUpIndex = index + 1000; + } + while (mwebAddrs.length <= index) { + await Future.delayed(const Duration(seconds: 1)); + } + } + return getAddress(index: index, hd: hd, addressType: addressType); + } + @action @override Future getChangeAddress({List? outputs, UtxoDetails? utxoDetails}) async { @@ -99,7 +149,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with } if (mwebEnabled) { - await topUpMweb(0); return mwebAddrs[0]; } From 150d065dbb31a34062d6339e7a768cea02f57f4c Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 10 Sep 2024 11:41:47 -0700 Subject: [PATCH 148/203] [skip ci] testing --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 11 +-- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 69 +++++++------------ cw_bitcoin/lib/litecoin_wallet_service.dart | 4 +- cw_core/lib/get_height_by_date.dart | 22 +----- cw_mweb/lib/cw_mweb.dart | 5 -- lib/bitcoin/cw_bitcoin.dart | 4 +- lib/core/wallet_loading_service.dart | 3 +- lib/di.dart | 2 +- lib/src/screens/rescan/rescan_page.dart | 2 +- .../widgets/unspent_coins_list_item.dart | 16 ++--- .../dashboard/dashboard_view_model.dart | 6 +- 11 files changed, 51 insertions(+), 93 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index e40f6fd893..0653200ee6 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -223,8 +223,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { if (walletInfo.type == WalletType.bitcoinCash) { await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); } else if (walletInfo.type == WalletType.litecoin) { - await _generateInitialAddresses(); - await _generateInitialAddresses(type: SegwitAddresType.mweb); + // await _generateInitialAddresses(); + // await _generateInitialAddresses(type: SegwitAddresType.mweb); } else if (walletInfo.type == WalletType.bitcoin) { await _generateInitialAddresses(); await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); @@ -232,6 +232,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { await _generateInitialAddresses(type: SegwitAddresType.p2tr); await _generateInitialAddresses(type: SegwitAddresType.p2wsh); } + updateAddressesByMatch(); updateReceiveAddresses(); updateChangeAddresses(); @@ -607,14 +608,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } void _validateAddresses() { - _addresses.forEach((element) { + _addresses.forEach((element) async { if (!element.isHidden && element.address != - getAddress(index: element.index, hd: mainHd, addressType: element.type)) { + await getAddressAsync(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)) { + await getAddressAsync(index: element.index, hd: sideHd, addressType: element.type)) { element.isHidden = false; } }); diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 1e6b1a3896..0e74340293 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -9,6 +9,7 @@ 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:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; part 'litecoin_wallet_addresses.g.dart'; @@ -31,9 +32,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialRegularAddressIndex, super.initialChangeAddressIndex, }) : super(walletInfo) { + // start generating mweb addresses in the background: initMwebAddresses(); - // topUpMweb(0); - print("initialized LitecoinWalletAddressesBase"); } final Bip32Slip10Secp256k1 mwebHd; @@ -46,48 +46,30 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with List get spendPubkey => mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed; - // Future topUpMweb(int index) async { - // // generate up to index + 1000 addresses: - // while (mwebAddrs.length - index < 1000) { - // final length = mwebAddrs.length; - // final address = await CwMweb.address( - // Uint8List.fromList(scanSecret), - // Uint8List.fromList(spendPubkey), - // length, - // ); - // mwebAddrs.add(address!); - // } - // } + Future ensureMwebAddressUpToIndexExists(int index) async { + Uint8List scan = Uint8List.fromList(scanSecret); + Uint8List spend = Uint8List.fromList(spendPubkey); + while (mwebAddrs.length <= (index + 1)) { + final address = await CwMweb.address(scan, spend, mwebAddrs.length); + mwebAddrs.add(address!); + } + } - Future initMwebAddresses() async { - print("mweb addresses: ${mwebAddrs.length}"); - const int INITIAL_MWEB_ADDRESSES = 25; - // make sure we have at least 20 addresses: - while (mwebAddrs.length < INITIAL_MWEB_ADDRESSES) { - final address = await CwMweb.address( - Uint8List.fromList(scanSecret), - Uint8List.fromList(spendPubkey), - mwebAddrs.length, - ); + Future generateNumAddresses(int num) async { + Uint8List scan = Uint8List.fromList(scanSecret); + Uint8List spend = Uint8List.fromList(spendPubkey); + for (int i = 0; i < num; i++) { + final address = await CwMweb.address(scan, spend, mwebAddrs.length); mwebAddrs.add(address!); + await Future.delayed(Duration.zero); } + } - // set up a periodic task to fill up the mweb addresses to 1000: - mwebTopUpTimer?.cancel(); - mwebTopUpTimer = Timer.periodic(Duration(seconds: 5), (timer) async { - if (mwebAddrs.length >= mwebTopUpIndex) { - return; - } - for (int i = 0; i < 10; i++) { - final address = await CwMweb.address( - Uint8List.fromList(scanSecret), - Uint8List.fromList(spendPubkey), - mwebAddrs.length, - ); - mwebAddrs.add(address!); - } - print("mweb addresses: ${mwebAddrs.length}"); - }); + Future initMwebAddresses() async { + for (int i = 0; i < 4; i++) { + await generateNumAddresses(250); + await Future.delayed(const Duration(milliseconds: 1500)); + } } @override @@ -109,12 +91,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with BitcoinAddressType? addressType, }) async { if (addressType == SegwitAddresType.mweb) { - if (index + 1000 > mwebTopUpIndex) { - mwebTopUpIndex = index + 1000; - } - while (mwebAddrs.length <= index) { - await Future.delayed(const Duration(seconds: 1)); - } + await ensureMwebAddressUpToIndexExists(index); } return getAddress(index: index, hd: hd, addressType: addressType); } diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index 25438007a7..7d976fa031 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -21,12 +21,12 @@ class LitecoinWalletService extends WalletService< BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials, BitcoinNewWalletCredentials> { - LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect, this.alwaysScan); + LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan, this.isDirect); final Box walletInfoSource; final Box unspentCoinsInfoSource; - final bool isDirect; final bool alwaysScan; + final bool isDirect; @override WalletType getType() => WalletType.litecoin; diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index 52136fdcd2..2f0bbc51de 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -267,11 +267,6 @@ const bitcoinDates = { "2023-01": 769810, }; - -const Map litecoinDates = { - // TODO: add litecoin dates -}; - int getBitcoinHeightByDate({required DateTime date}) { String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; final closestKey = bitcoinDates.keys @@ -305,19 +300,9 @@ DateTime getDateByBitcoinHeight(int height) { return estimatedDate; } -int getLitecoinHeightByDate({required DateTime date}) { - String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; - final closestKey = litecoinDates.keys - .firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => litecoinDates.keys.last); - final beginningBlock = litecoinDates[dateKey] ?? litecoinDates[closestKey]!; - - final startOfMonth = DateTime(date.year, date.month); - final daysDifference = date.difference(startOfMonth).inDays; - - // approximately 6 blocks per hour, 24 hours per day - int estimatedBlocksSinceStartOfMonth = (daysDifference * 24 * 6); - - return beginningBlock + estimatedBlocksSinceStartOfMonth; +int getLtcHeightByDate({required DateTime date}) { + // TODO: use the proxy layer to get the height with a binary search of blocked header heights + return 0; } // TODO: enhance all of this global const lists @@ -397,4 +382,3 @@ int getWowneroHeightByDate({required DateTime date}) { return wowDates[closestKey] ?? 0; } - diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index e71ddde973..ab7dfda10b 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -55,11 +55,6 @@ class CwMweb { } static Future address(Uint8List scanSecret, Uint8List spendPub, int index) async { - // try { - // return (await CwMwebPlatform.instance.address(scan, spendPub, index))!; - // } catch (e) { - // print("error generating address!: $e"); - // } return CwMwebPlatform.instance.address(scanSecret, spendPub, index); } diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 0743982636..33d79b450a 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -210,7 +210,7 @@ class CWBitcoin extends Bitcoin { WalletService createLitecoinWalletService(Box walletInfoSource, Box unspentCoinSource, bool alwaysScan, bool isDirect) { - return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect, alwaysScan); + return LitecoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan, isDirect); } @override @@ -530,7 +530,7 @@ class CWBitcoin extends Bitcoin { int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date); @override - int getLitecoinHeightByDate({required DateTime date}) => getLitecoinHeightByDate(date: date); + int getLitecoinHeightByDate({required DateTime date}) => getLtcHeightByDate(date: date); @override Future rescan(Object wallet, {required int height, bool? doSingleScan}) async { diff --git a/lib/core/wallet_loading_service.dart b/lib/core/wallet_loading_service.dart index 0087b1332f..e58e146524 100644 --- a/lib/core/wallet_loading_service.dart +++ b/lib/core/wallet_loading_service.dart @@ -85,7 +85,8 @@ class WalletLoadingService { authenticatedErrorStreamController.add(corruptedWalletsSeeds); return wallet; - } catch (_) { + } catch (e) { + print(e); // save seeds and show corrupted wallets' seeds to the user try { final seeds = await _getCorruptedWalletSeeds(walletInfo.name, walletInfo.type); diff --git a/lib/di.dart b/lib/di.dart index 30727f33c8..9c3c5bcb28 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1000,8 +1000,8 @@ Future setup({ return bitcoin!.createLitecoinWalletService( _walletInfoSource, _unspentCoinsInfoSource, - SettingsStoreBase.walletPasswordDirectInput, getIt.get().mwebAlwaysScan, + SettingsStoreBase.walletPasswordDirectInput, ); case WalletType.ethereum: return ethereum!.createEthereumWalletService( diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index 2d4e86e4f5..7dcd17f766 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -35,7 +35,7 @@ class RescanPage extends BasePage { isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan, isMwebScan: _rescanViewModel.isMwebScan, doSingleScan: _rescanViewModel.doSingleScan, - hasDatePicker: !_rescanViewModel.isMwebScan, + hasDatePicker: !_rescanViewModel.isMwebScan,// disable date picker for mweb for now toggleSingleScan: () => _rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan, walletType: _rescanViewModel.wallet.type, 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 236d06f4ee..42e701bb4f 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,10 +103,10 @@ class UnspentCoinsListItem extends StatelessWidget { ), maxLines: 1, ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - if (isChange) + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // children: [ + if (isChange || true) Container( height: 17, padding: EdgeInsets.only(left: 6, right: 6), @@ -123,7 +123,7 @@ class UnspentCoinsListItem extends StatelessWidget { ), ), ), - if (address.toLowerCase().contains("mweb")) + if (address.toLowerCase().contains("mweb") || true) Container( height: 17, padding: EdgeInsets.only(left: 6, right: 6), @@ -141,7 +141,7 @@ class UnspentCoinsListItem extends StatelessWidget { ), ), ), - if (isSilentPayment) + if (isSilentPayment || true) Container( height: 17, padding: EdgeInsets.only(left: 6, right: 6), @@ -158,8 +158,8 @@ class UnspentCoinsListItem extends StatelessWidget { ), ), ), - ], - ), + // ], + // ), ], ), ), diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 6e8c2070e3..7c2c4f5493 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -414,8 +414,8 @@ abstract class DashboardViewModelBase with Store { @computed bool get showMwebCard => hasMweb && settingsStore.mwebCardDisplay; - @observable - bool mwebScanningActive = false; + @computed + bool get mwebScanningActive => settingsStore.mwebEnabled; @computed bool get hasEnabledMwebBefore => settingsStore.hasEnabledMwebBefore; @@ -430,7 +430,7 @@ abstract class DashboardViewModelBase with Store { settingsStore.hasEnabledMwebBefore = true; } - mwebScanningActive = active; + settingsStore.mwebEnabled = active; bitcoin!.setMwebEnabled(wallet, active); } From 69332b30cd5aed02c7b2a8a4092c4e5934a42f7a Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 10 Sep 2024 11:43:00 -0700 Subject: [PATCH 149/203] [skip ci] file didn't get saved --- .../dashboard/dashboard_view_model.dart | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 7c2c4f5493..06e78209d5 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -248,6 +248,11 @@ abstract class DashboardViewModelBase with Store { if (hasMweb) { mwebScanningActive = bitcoin!.getMwebEnabled(wallet); + reaction((_) => settingsStore.mwebAlwaysScan, (bool alwaysScan) { + if (alwaysScan) { + mwebScanningActive = true; + } + }); } } @@ -376,12 +381,15 @@ abstract class DashboardViewModelBase with Store { // to not cause work duplication, this will do the job as well, it will be slightly less precise // about what happened - but still enough. // if (keys['privateSpendKey'] == List.generate(64, (index) => "0").join("")) "Private spend key is 0", - if (keys['privateViewKey'] == List.generate(64, (index) => "0").join("")) "private view key is 0", + if (keys['privateViewKey'] == List.generate(64, (index) => "0").join("")) + "private view key is 0", // if (keys['publicSpendKey'] == List.generate(64, (index) => "0").join("")) "public spend key is 0", - if (keys['publicViewKey'] == List.generate(64, (index) => "0").join("")) "public view key is 0", + if (keys['publicViewKey'] == List.generate(64, (index) => "0").join("")) + "public view key is 0", // if (wallet.seed == null) "wallet seed is null", // if (wallet.seed == "") "wallet seed is empty", - if (monero!.getSubaddressList(wallet).getAll(wallet)[0].address == "41d7FXjswpK1111111111111111111111111111111111111111111111111111111111111111111111111111112KhNi4") + if (monero!.getSubaddressList(wallet).getAll(wallet)[0].address == + "41d7FXjswpK1111111111111111111111111111111111111111111111111111111111111111111111111111112KhNi4") "primary address is invalid, you won't be able to receive / spend funds", ]; return errors; @@ -414,8 +422,8 @@ abstract class DashboardViewModelBase with Store { @computed bool get showMwebCard => hasMweb && settingsStore.mwebCardDisplay; - @computed - bool get mwebScanningActive => settingsStore.mwebEnabled; + @observable + bool mwebScanningActive = false; @computed bool get hasEnabledMwebBefore => settingsStore.hasEnabledMwebBefore; @@ -430,7 +438,7 @@ abstract class DashboardViewModelBase with Store { settingsStore.hasEnabledMwebBefore = true; } - settingsStore.mwebEnabled = active; + mwebScanningActive = active; bitcoin!.setMwebEnabled(wallet, active); } From 2c38641d5c6220c85bbb70899b81982e71b93a19 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 10 Sep 2024 14:58:03 -0700 Subject: [PATCH 150/203] more address generation reliability fixes --- cw_bitcoin/lib/electrum_wallet.dart | 2 -- cw_bitcoin/lib/electrum_wallet_addresses.dart | 4 +-- cw_bitcoin/lib/litecoin_wallet.dart | 6 ++++ cw_bitcoin/lib/litecoin_wallet_addresses.dart | 34 +++++++++++++++++-- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 7733562fbe..b812907ad5 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -2395,8 +2395,6 @@ class PublicKeyWithDerivationPath { } BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) { - // print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); - // print(network); if (network is BitcoinCashNetwork) { if (!address.startsWith("bitcoincash:") && (address.startsWith("q") || address.startsWith("p"))) { diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 0653200ee6..314b8768ad 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -223,8 +223,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { if (walletInfo.type == WalletType.bitcoinCash) { await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); } else if (walletInfo.type == WalletType.litecoin) { - // await _generateInitialAddresses(); - // await _generateInitialAddresses(type: SegwitAddresType.mweb); + await _generateInitialAddresses(type: SegwitAddresType.p2wpkh); + await _generateInitialAddresses(type: SegwitAddresType.mweb); } else if (walletInfo.type == WalletType.bitcoin) { await _generateInitialAddresses(); await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index e2010c5207..c5ddad44a4 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -250,6 +250,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return; } + final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; + while (mwebAddrs.length < 1000) { + print("waiting for mweb addresses to finish generating..."); + await Future.delayed(const Duration(milliseconds: 1000)); + } + await getStub(); await updateUnspent(); await updateBalance(); diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 0e74340293..b657ce60dd 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:bech32/bech32.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/utils.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; @@ -33,7 +34,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialChangeAddressIndex, }) : super(walletInfo) { // start generating mweb addresses in the background: - initMwebAddresses(); + // initMwebAddresses(); } final Bip32Slip10Secp256k1 mwebHd; @@ -46,6 +47,12 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with List get spendPubkey => mwebHd.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed; + @override + Future init() async { + await super.init(); + initMwebAddresses(); + } + Future ensureMwebAddressUpToIndexExists(int index) async { Uint8List scan = Uint8List.fromList(scanSecret); Uint8List spend = Uint8List.fromList(spendPubkey); @@ -66,10 +73,30 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with } Future initMwebAddresses() async { - for (int i = 0; i < 4; i++) { + print("Initializing MWEB address timer!"); + Timer.periodic(const Duration(seconds: 2), (timer) async { + print("Generating MWEB addresses..."); await generateNumAddresses(250); await Future.delayed(const Duration(milliseconds: 1500)); - } + + if (mwebAddrs.length > 1000) { + // convert mwebAddrs to BitcoinAddressRecords: + List mwebAddresses = mwebAddrs + .asMap() + .entries + .map((e) => BitcoinAddressRecord( + e.value, + index: e.key, + type: SegwitAddresType.mweb, + network: network, + )) + .toList(); + // add them to the list of all addresses: + addAddresses(mwebAddresses); + print("MWEB addresses initialized ${mwebAddrs.length}"); + timer.cancel(); + } + }); } @override @@ -126,6 +153,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with } if (mwebEnabled) { + await ensureMwebAddressUpToIndexExists(1); return mwebAddrs[0]; } From 2573ca6b6886f061545d1a377bbd3cc8d5672f24 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 10 Sep 2024 15:18:20 -0700 Subject: [PATCH 151/203] [skip ci] minor --- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 5 +++++ .../widgets/unspent_coins_list_item.dart | 16 ++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index b657ce60dd..51944a71cf 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -75,6 +75,10 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Future initMwebAddresses() async { print("Initializing MWEB address timer!"); Timer.periodic(const Duration(seconds: 2), (timer) async { + if (super.allAddresses.length > 1000) { + timer.cancel(); + return; + } print("Generating MWEB addresses..."); await generateNumAddresses(250); await Future.delayed(const Duration(milliseconds: 1500)); @@ -95,6 +99,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with addAddresses(mwebAddresses); print("MWEB addresses initialized ${mwebAddrs.length}"); timer.cancel(); + return; } }); } 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 42e701bb4f..236d06f4ee 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,10 +103,10 @@ class UnspentCoinsListItem extends StatelessWidget { ), maxLines: 1, ), - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceEvenly, - // children: [ - if (isChange || true) + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if (isChange) Container( height: 17, padding: EdgeInsets.only(left: 6, right: 6), @@ -123,7 +123,7 @@ class UnspentCoinsListItem extends StatelessWidget { ), ), ), - if (address.toLowerCase().contains("mweb") || true) + if (address.toLowerCase().contains("mweb")) Container( height: 17, padding: EdgeInsets.only(left: 6, right: 6), @@ -141,7 +141,7 @@ class UnspentCoinsListItem extends StatelessWidget { ), ), ), - if (isSilentPayment || true) + if (isSilentPayment) Container( height: 17, padding: EdgeInsets.only(left: 6, right: 6), @@ -158,8 +158,8 @@ class UnspentCoinsListItem extends StatelessWidget { ), ), ), - // ], - // ), + ], + ), ], ), ), From 1e73a517fdd2e74006820055e0facb287cd549e9 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 11 Sep 2024 08:50:50 -0700 Subject: [PATCH 152/203] minor code cleanup --- cw_bitcoin/lib/litecoin_wallet.dart | 39 +++---------------- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 10 +---- 2 files changed, 7 insertions(+), 42 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index c5ddad44a4..7de93bdf3d 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -407,8 +407,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } - 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; @@ -416,6 +414,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { tx.confirmations = confirmations; } + bool isNew = transactionHistory.transactions[tx.id] == null; + if (!(tx.outputAddresses?.contains(utxo.address) ?? false)) { tx.outputAddresses?.add(utxo.address); isNew = true; @@ -458,22 +458,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { print("SCANNING FROM HEIGHT: $restoreHeight"); final req = UtxosRequest(scanSecret: scanSecret, fromHeight: restoreHeight); - // process old utxos: - // for (final utxo in mwebUtxosBox.values) { - // if (utxo.address.isEmpty) { - // continue; - // } - - // // if (walletInfo.restoreHeight > utxo.height) { - // // continue; - // // } - // // await handleIncoming(utxo, _stub); - - // if (utxo.height > walletInfo.restoreHeight) { - // await walletInfo.updateRestoreHeight(utxo.height); - // } - // } - // process new utxos as they come in: _utxoStream?.cancel(); _utxoStream = _stub.utxos(req).listen((Utxo sUtxo) async { @@ -490,12 +474,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // return; // } - // if (utxo.address.isEmpty) { - // await updateUnspent(); - // await updateBalance(); - // initDone = true; - // } - await updateUnspent(); await updateBalance(); @@ -521,6 +499,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { .where((tx) => tx.direction == TransactionDirection.outgoing && tx.isPending) .map(checkPendingTransaction))) .any((x) => x)); + final outputIds = mwebUtxosBox.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId).toList(); @@ -531,10 +510,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final height = await electrumClient.getCurrentBlockChainTip(); if (height == null || status.blockHeaderHeight != height) return; if (status.mwebUtxosHeight != height) return; + int amount = 0; Set inputAddresses = {}; var output = convert.AccumulatorSink(); var input = sha256.startChunkedConversion(output); + for (final outputId in spent) { final utxo = mwebUtxosBox.get(outputId); await mwebUtxosBox.delete(outputId); @@ -549,6 +530,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { inputAddresses.add(utxo.address); input.add(hex.decode(outputId)); } + if (inputAddresses.isEmpty) return; input.close(); var digest = output.events.single; @@ -565,7 +547,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { inputAddresses: inputAddresses.toList(), outputAddresses: [], ); - print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@2"); transactionHistory.addOne(tx); await transactionHistory.save(); @@ -674,14 +655,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } catch (_) {} // 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; - // }); - await updateUnspent(); for (var addressRecord in walletAddresses.allAddresses) { diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 51944a71cf..04a7ab5cc3 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:bech32/bech32.dart'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; @@ -15,10 +14,6 @@ import 'package:mobx/mobx.dart'; part 'litecoin_wallet_addresses.g.dart'; -String encodeMwebAddress(List scriptPubKey) { - return bech32.encode(Bech32("ltcmweb1", scriptPubKey), 250); -} - class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalletAddresses; abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store { @@ -32,10 +27,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with super.initialAddresses, super.initialRegularAddressIndex, super.initialChangeAddressIndex, - }) : super(walletInfo) { - // start generating mweb addresses in the background: - // initMwebAddresses(); - } + }) : super(walletInfo) {} final Bip32Slip10Secp256k1 mwebHd; bool mwebEnabled; From a673d3977fb42065afbb215679d3d2c2f581f0bb Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 11 Sep 2024 13:00:10 -0700 Subject: [PATCH 153/203] hopefully prevents send issue --- cw_bitcoin/lib/litecoin_wallet.dart | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 7de93bdf3d..e6de07ef14 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -224,6 +224,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } + Future waitForMwebAddresses() async { + // ensure that we have the full 1000 mweb addresses generated before continuing: + final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; + while (mwebAddrs.length < 1000) { + print("waiting for mweb addresses to finish generating..."); + await Future.delayed(const Duration(milliseconds: 1000)); + } + } + @action @override Future startSync() async { @@ -250,11 +259,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return; } - final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; - while (mwebAddrs.length < 1000) { - print("waiting for mweb addresses to finish generating..."); - await Future.delayed(const Duration(milliseconds: 1000)); - } + await waitForMwebAddresses(); await getStub(); await updateUnspent(); @@ -800,6 +805,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (!mwebEnabled) { return tx; } + await waitForMwebAddresses(); await getStub(); final resp = await _stub.create(CreateRequest( From c94c10cc0541d349cec4b45d454f977429ee0458 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 11 Sep 2024 14:17:06 -0700 Subject: [PATCH 154/203] [skip ci] wip address changes --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 13 +++++++++++++ cw_bitcoin/lib/electrum_wallet_snapshot.dart | 10 ++++++++++ cw_bitcoin/lib/litecoin_wallet.dart | 6 ++++++ cw_bitcoin/lib/litecoin_wallet_addresses.dart | 13 +++++++++++-- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 314b8768ad..1f078db943 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -39,6 +39,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { Map? initialChangeAddressIndex, List? initialSilentAddresses, int initialSilentAddressIndex = 0, + List? initialMwebAddresses, Bip32Slip10Secp256k1? masterHd, BitcoinAddressType? initialAddressPageType, }) : _addresses = ObservableList.of((initialAddresses ?? []).toSet()), @@ -59,6 +60,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { silentAddresses = ObservableList.of( (initialSilentAddresses ?? []).toSet()), currentSilentAddressIndex = initialSilentAddressIndex, + mwebAddresses = + ObservableList.of((initialMwebAddresses ?? []).toSet()), super(walletInfo) { if (masterHd != null) { silentAddress = SilentPaymentOwner.fromPrivateKeys( @@ -101,6 +104,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final ObservableList receiveAddresses; final ObservableList changeAddresses; final ObservableList silentAddresses; + final ObservableList mwebAddresses; final BasedUtxoNetwork network; final Bip32Slip10Secp256k1 mainHd; final Bip32Slip10Secp256k1 sideHd; @@ -607,6 +611,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { updateAddressesByMatch(); } + @action + void addMwebAddresses(Iterable addresses) { + final addressesSet = this.mwebAddresses.toSet(); + addressesSet.addAll(addresses); + this.mwebAddresses.clear(); + this.mwebAddresses.addAll(addressesSet); + updateAddressesByMatch(); + } + void _validateAddresses() { _addresses.forEach((element) async { if (!element.isHidden && diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index fa58be2382..f71510db13 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -23,6 +23,7 @@ class ElectrumWalletSnapshot { required this.addressPageType, required this.silentAddresses, required this.silentAddressIndex, + required this.mwebAddresses, this.passphrase, this.derivationType, this.derivationPath, @@ -44,6 +45,8 @@ class ElectrumWalletSnapshot { List addresses; List silentAddresses; + List mwebAddresses; + ElectrumBalance balance; Map regularAddressIndex; Map changeAddressIndex; @@ -71,6 +74,12 @@ class ElectrumWalletSnapshot { .map((addr) => BitcoinSilentPaymentAddressRecord.fromJSON(addr, network: network)) .toList(); + final mwebAddressTmp = data['mweb_addresses'] as List? ?? []; + final mwebAddresses = mwebAddressTmp + .whereType() + .map((addr) => BitcoinAddressRecord.fromJSON(addr, network: network)) + .toList(); + final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ?? ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0); var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; @@ -113,6 +122,7 @@ class ElectrumWalletSnapshot { derivationPath: derivationPath, silentAddresses: silentAddresses, silentAddressIndex: silentAddressIndex, + mwebAddresses: mwebAddresses, ); } } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index e6de07ef14..99bb1cddbb 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -57,6 +57,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { String? passphrase, String? addressPageType, List? initialAddresses, + List? initialMwebAddresses, ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, @@ -69,6 +70,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unspentCoinsInfo: unspentCoinsInfo, network: LitecoinNetwork.mainnet, initialAddresses: initialAddresses, + initialMwebAddresses: initialMwebAddresses, initialBalance: initialBalance, seedBytes: seedBytes, encryptionFileUtils: encryptionFileUtils, @@ -81,6 +83,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, + initialMwebAddresses: initialMwebAddresses, mainHd: hd, sideHd: accountHD.childKey(Bip32KeyIndex(1)), network: network, @@ -111,6 +114,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { String? passphrase, String? addressPageType, List? initialAddresses, + List? initialMwebAddresses, ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex}) async { @@ -134,6 +138,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, initialAddresses: initialAddresses, + initialMwebAddresses: initialMwebAddresses, initialBalance: initialBalance, encryptionFileUtils: encryptionFileUtils, passphrase: passphrase, @@ -213,6 +218,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, initialAddresses: snp?.addresses, + initialMwebAddresses: snp?.mwebAddresses, initialBalance: snp?.balance, seedBytes: seedBytes!, passphrase: passphrase, diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 04a7ab5cc3..57e1d439cc 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -25,15 +25,23 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required this.mwebHd, required this.mwebEnabled, super.initialAddresses, + super.initialMwebAddresses, super.initialRegularAddressIndex, super.initialChangeAddressIndex, - }) : super(walletInfo) {} + }) : super(walletInfo) { + for (int i = 0; i < mwebAddresses.length; i++) { + mwebAddrs.add(mwebAddresses[i].address); + } + print("initialized with ${mwebAddrs.length} mweb addresses"); + if (mwebAddrs.length < 1000) { + initMwebAddresses(); + } + } final Bip32Slip10Secp256k1 mwebHd; bool mwebEnabled; int mwebTopUpIndex = 1000; List mwebAddrs = []; - static Timer? mwebTopUpTimer; List get scanSecret => mwebHd.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw; List get spendPubkey => @@ -89,6 +97,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with .toList(); // add them to the list of all addresses: addAddresses(mwebAddresses); + addMwebAddresses(mwebAddresses); print("MWEB addresses initialized ${mwebAddrs.length}"); timer.cancel(); return; From cd307bf6e82be86239b669c7b258df72624877df Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 11 Sep 2024 14:28:58 -0700 Subject: [PATCH 155/203] [skip ci] save --- cw_bitcoin/lib/litecoin_wallet.dart | 1 - cw_bitcoin/lib/litecoin_wallet_addresses.dart | 82 +++++++++++-------- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 99bb1cddbb..0633df4197 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -70,7 +70,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unspentCoinsInfo: unspentCoinsInfo, network: LitecoinNetwork.mainnet, initialAddresses: initialAddresses, - initialMwebAddresses: initialMwebAddresses, initialBalance: initialBalance, seedBytes: seedBytes, encryptionFileUtils: encryptionFileUtils, diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 57e1d439cc..6ff9f955ea 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -33,9 +33,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with mwebAddrs.add(mwebAddresses[i].address); } print("initialized with ${mwebAddrs.length} mweb addresses"); - if (mwebAddrs.length < 1000) { - initMwebAddresses(); - } + initMwebAddresses(); } final Bip32Slip10Secp256k1 mwebHd; @@ -73,36 +71,56 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with } Future initMwebAddresses() async { - print("Initializing MWEB address timer!"); - Timer.periodic(const Duration(seconds: 2), (timer) async { - if (super.allAddresses.length > 1000) { - timer.cancel(); - return; - } + print("Initializing MWEB addresses!"); + + if (mwebAddrs.length < 1000) { print("Generating MWEB addresses..."); - await generateNumAddresses(250); - await Future.delayed(const Duration(milliseconds: 1500)); - - if (mwebAddrs.length > 1000) { - // convert mwebAddrs to BitcoinAddressRecords: - List mwebAddresses = mwebAddrs - .asMap() - .entries - .map((e) => BitcoinAddressRecord( - e.value, - index: e.key, - type: SegwitAddresType.mweb, - network: network, - )) - .toList(); - // add them to the list of all addresses: - addAddresses(mwebAddresses); - addMwebAddresses(mwebAddresses); - print("MWEB addresses initialized ${mwebAddrs.length}"); - timer.cancel(); - return; - } - }); + await ensureMwebAddressUpToIndexExists(1020); + List addressRecords = mwebAddrs + .asMap() + .entries + .map((e) => BitcoinAddressRecord( + e.value, + index: e.key, + type: SegwitAddresType.mweb, + network: network, + )) + .toList(); + addAddresses(addressRecords); + addMwebAddresses(addressRecords); + print("added ${addressRecords.length} mweb addresses"); + return; + } + + // Timer.periodic(const Duration(seconds: 2), (timer) async { + // if (super.allAddresses.length > 1000) { + // timer.cancel(); + // return; + // } + // print("Generating MWEB addresses..."); + // await generateNumAddresses(250); + // await Future.delayed(const Duration(milliseconds: 1500)); + + // if (mwebAddrs.length > 1000) { + // // convert mwebAddrs to BitcoinAddressRecords: + // List mwebAddresses = mwebAddrs + // .asMap() + // .entries + // .map((e) => BitcoinAddressRecord( + // e.value, + // index: e.key, + // type: SegwitAddresType.mweb, + // network: network, + // )) + // .toList(); + // // add them to the list of all addresses: + // addAddresses(mwebAddresses); + // addMwebAddresses(mwebAddresses); + // print("MWEB addresses initialized ${mwebAddrs.length}"); + // timer.cancel(); + // return; + // } + // }); } @override From 554df09c993c326a5eb699916ae97213c6203ab3 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 11 Sep 2024 16:14:04 -0700 Subject: [PATCH 156/203] save mweb addresses, auto-restart sync process if it gets stuck [skip ci] --- cw_bitcoin/lib/electrum_wallet.dart | 1 + cw_bitcoin/lib/electrum_wallet_addresses.dart | 5 ++++ cw_bitcoin/lib/electrum_wallet_snapshot.dart | 3 ++- cw_bitcoin/lib/litecoin_wallet.dart | 26 +++++++++++++++++-- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index b812907ad5..1babeaa6a2 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1150,6 +1150,7 @@ abstract class ElectrumWalletBase 'derivationPath': walletInfo.derivationInfo?.derivationPath, 'silent_addresses': walletAddresses.silentAddresses.map((addr) => addr.toJSON()).toList(), 'silent_address_index': walletAddresses.currentSilentAddressIndex.toString(), + 'mweb_addresses': walletAddresses.mwebAddresses.map((addr) => addr.toJSON()).toList(), }); int feeRate(TransactionPriority priority) { diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 1f078db943..dd4f931ad4 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -481,6 +481,11 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { foundAddress = addressRecord; } }); + mwebAddresses.forEach((addressRecord) { + if (addressRecord.address == address) { + foundAddress = addressRecord; + } + }); if (foundAddress != null) { foundAddress!.setNewName(label); diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index f71510db13..25cc5637ed 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -59,10 +59,11 @@ class ElectrumWalletSnapshot { final path = await pathForWallet(name: name, type: type); final jsonSource = await encryptionFileUtils.read(path: path, password: password); final data = json.decode(jsonSource) as Map; - final addressesTmp = data['addresses'] as List? ?? []; final mnemonic = data['mnemonic'] as String?; final xpub = data['xpub'] as String?; final passphrase = data['passphrase'] as String? ?? ''; + + final addressesTmp = data['addresses'] as List? ?? []; final addresses = addressesTmp .whereType() .map((addr) => BitcoinAddressRecord.fromJSON(addr, network: network)) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 0633df4197..52ff974fac 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -272,10 +272,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { _syncTimer?.cancel(); // delay the timer by a second so we don't overrride the restoreheight if one is set - Timer(const Duration(seconds: 1), () async { + Timer(const Duration(seconds: 2), () async { _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { if (syncStatus is FailedSyncStatus) return; - final nodeHeight = await electrumClient.getCurrentBlockChainTip() ?? 0; + + final nodeHeight = + await electrumClient.getCurrentBlockChainTip() ?? 0; // current block height of our node final resp = await _stub.status(StatusRequest()); if (resp.blockHeaderHeight < nodeHeight) { @@ -308,6 +310,26 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } }); + + // setup a watch dog to restart the sync process if it gets stuck: + List lastFewProgresses = []; + Timer.periodic(const Duration(seconds: 10), (timer) async { + if (syncStatus is! SyncingSyncStatus) return; + if (syncStatus.progress() > 0.98) return; + lastFewProgresses.add(syncStatus.progress()); + if (lastFewProgresses.length < 4) return; + // limit list size to 4: + while(lastFewProgresses.length > 4) { + lastFewProgresses.removeAt(0); + } + // if the progress is the same over the last 40 seconds, restart the sync: + if (lastFewProgresses.every((p) => p == lastFewProgresses.first)) { + print("mweb syncing is stuck, restarting..."); + await stopSync(); + startSync(); + timer.cancel(); + } + }); }); // this runs in the background and processes new utxos as they come in: processMwebUtxos(); From f8d76fb2e1108f82032192527a249875ddba1c8a Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 11 Sep 2024 18:17:45 -0700 Subject: [PATCH 157/203] address generation issues mostly resolved --- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 50 +++---------------- 1 file changed, 7 insertions(+), 43 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 6ff9f955ea..8f5aefcc82 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -33,7 +33,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with mwebAddrs.add(mwebAddresses[i].address); } print("initialized with ${mwebAddrs.length} mweb addresses"); - initMwebAddresses(); } final Bip32Slip10Secp256k1 mwebHd; @@ -47,26 +46,22 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with @override Future init() async { + await initMwebAddresses(); await super.init(); - initMwebAddresses(); } - Future ensureMwebAddressUpToIndexExists(int index) async { - Uint8List scan = Uint8List.fromList(scanSecret); - Uint8List spend = Uint8List.fromList(spendPubkey); - while (mwebAddrs.length <= (index + 1)) { - final address = await CwMweb.address(scan, spend, mwebAddrs.length); - mwebAddrs.add(address!); - } + @computed + @override + List get allAddresses { + return List.from(super.allAddresses)..addAll(mwebAddresses); } - Future generateNumAddresses(int num) async { + Future ensureMwebAddressUpToIndexExists(int index) async { Uint8List scan = Uint8List.fromList(scanSecret); Uint8List spend = Uint8List.fromList(spendPubkey); - for (int i = 0; i < num; i++) { + while (mwebAddrs.length <= (index + 1)) { final address = await CwMweb.address(scan, spend, mwebAddrs.length); mwebAddrs.add(address!); - await Future.delayed(Duration.zero); } } @@ -86,41 +81,10 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with network: network, )) .toList(); - addAddresses(addressRecords); addMwebAddresses(addressRecords); print("added ${addressRecords.length} mweb addresses"); return; } - - // Timer.periodic(const Duration(seconds: 2), (timer) async { - // if (super.allAddresses.length > 1000) { - // timer.cancel(); - // return; - // } - // print("Generating MWEB addresses..."); - // await generateNumAddresses(250); - // await Future.delayed(const Duration(milliseconds: 1500)); - - // if (mwebAddrs.length > 1000) { - // // convert mwebAddrs to BitcoinAddressRecords: - // List mwebAddresses = mwebAddrs - // .asMap() - // .entries - // .map((e) => BitcoinAddressRecord( - // e.value, - // index: e.key, - // type: SegwitAddresType.mweb, - // network: network, - // )) - // .toList(); - // // add them to the list of all addresses: - // addAddresses(mwebAddresses); - // addMwebAddresses(mwebAddresses); - // print("MWEB addresses initialized ${mwebAddrs.length}"); - // timer.cancel(); - // return; - // } - // }); } @override From 8aa95a2eb7943b078658f28ceb9b16c8478acbaf Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 11 Sep 2024 18:44:25 -0700 Subject: [PATCH 158/203] more performance fixes --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 4 ++++ cw_bitcoin/lib/litecoin_wallet_addresses.dart | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index dd4f931ad4..2de86d7803 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -627,6 +627,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { void _validateAddresses() { _addresses.forEach((element) async { + if (element.type == SegwitAddresType.mweb) { + // this would add a ton of startup lag for mweb addresses since we have 1000 of them + return; + } if (!element.isHidden && element.address != await getAddressAsync(index: element.index, hd: mainHd, addressType: element.type)) { diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 8f5aefcc82..0bdfafdcda 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -59,18 +59,24 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Future ensureMwebAddressUpToIndexExists(int index) async { Uint8List scan = Uint8List.fromList(scanSecret); Uint8List spend = Uint8List.fromList(spendPubkey); + int count = 0; while (mwebAddrs.length <= (index + 1)) { final address = await CwMweb.address(scan, spend, mwebAddrs.length); mwebAddrs.add(address!); + count++; + // sleep for a bit to avoid making the main thread unresponsive: + if (count > 50) { + count = 0; + await Future.delayed(Duration(milliseconds: 100)); + } } } Future initMwebAddresses() async { - print("Initializing MWEB addresses!"); - if (mwebAddrs.length < 1000) { print("Generating MWEB addresses..."); await ensureMwebAddressUpToIndexExists(1020); + print("done generating MWEB addresses"); List addressRecords = mwebAddrs .asMap() .entries @@ -81,6 +87,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with network: network, )) .toList(); + print("converted to list"); addMwebAddresses(addressRecords); print("added ${addressRecords.length} mweb addresses"); return; @@ -105,6 +112,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType, }) async { + print("getting address for index $index"); if (addressType == SegwitAddresType.mweb) { await ensureMwebAddressUpToIndexExists(index); } From ec25c2bad59dfc97903dea9497974d7162137f63 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Wed, 11 Sep 2024 20:44:32 -0700 Subject: [PATCH 159/203] [skip ci] --- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 0bdfafdcda..dd590f1241 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -87,7 +87,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with network: network, )) .toList(); - print("converted to list"); addMwebAddresses(addressRecords); print("added ${addressRecords.length} mweb addresses"); return; @@ -112,7 +111,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType, }) async { - print("getting address for index $index"); if (addressType == SegwitAddresType.mweb) { await ensureMwebAddressUpToIndexExists(index); } From 9dd696a474bffe331f1b248ac4b3c825324b369b Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 12 Sep 2024 11:36:29 -0700 Subject: [PATCH 160/203] this should maybe be refactored, pt.1 --- cw_bitcoin/lib/electrum_balance.dart | 26 ++- cw_core/lib/balance.dart | 9 +- .../screens/dashboard/pages/balance_page.dart | 151 +++++++++++++++--- .../dashboard/balance_view_model.dart | 52 ++++++ 4 files changed, 214 insertions(+), 24 deletions(-) diff --git a/cw_bitcoin/lib/electrum_balance.dart b/cw_bitcoin/lib/electrum_balance.dart index 15d6843d87..ce6b737784 100644 --- a/cw_bitcoin/lib/electrum_balance.dart +++ b/cw_bitcoin/lib/electrum_balance.dart @@ -7,7 +7,14 @@ class ElectrumBalance extends Balance { required this.confirmed, required this.unconfirmed, required this.frozen, - }) : super(confirmed, unconfirmed); + this.secondConfirmed, + this.secondUnconfirmed, + }) : super( + confirmed, + unconfirmed, + secondAvailable: secondConfirmed, + secondAdditional: secondUnconfirmed, + ); static ElectrumBalance? fromJSON(String? jsonSource) { if (jsonSource == null) { @@ -25,6 +32,8 @@ class ElectrumBalance extends Balance { int confirmed; int unconfirmed; final int frozen; + int? secondConfirmed; + int? secondUnconfirmed; @override String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed - frozen); @@ -38,6 +47,17 @@ class ElectrumBalance extends Balance { return frozenFormatted == '0.0' ? '' : frozenFormatted; } - String toJSON() => - json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed, 'frozen': frozen}); + @override + String get formattedSecondAvailableBalance => bitcoinAmountToString(amount: secondConfirmed ?? 0); + + @override + String get formattedSecondAdditionalBalance => bitcoinAmountToString(amount: secondUnconfirmed ?? 0); + + String toJSON() => json.encode({ + 'confirmed': confirmed, + 'unconfirmed': unconfirmed, + 'frozen': frozen, + 'secondConfirmed': secondConfirmed, + 'secondUnconfirmed': secondUnconfirmed + }); } diff --git a/cw_core/lib/balance.dart b/cw_core/lib/balance.dart index 431aff5151..579a6da8fa 100644 --- a/cw_core/lib/balance.dart +++ b/cw_core/lib/balance.dart @@ -1,13 +1,16 @@ abstract class Balance { - const Balance(this.available, this.additional); + const Balance(this.available, this.additional, {this.secondAvailable, this.secondAdditional}); final int available; final int additional; - String get formattedAvailableBalance; + final int? secondAvailable; + final int? secondAdditional; + String get formattedAvailableBalance; String get formattedAdditionalBalance; - String get formattedUnAvailableBalance => ''; + String get formattedSecondAvailableBalance => ''; + String get formattedSecondAdditionalBalance => ''; } diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index 7847c92e21..2b662758a4 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -131,7 +131,7 @@ class CryptoBalanceWidget extends StatelessWidget { builder: (_) { if (dashboardViewModel.getMoneroError != null) { return Padding( - padding: const EdgeInsets.fromLTRB(16,0,16,16), + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), child: DashBoardRoundedCardWidget( title: "Invalid monero bindings", subTitle: dashboardViewModel.getMoneroError.toString(), @@ -146,13 +146,12 @@ class CryptoBalanceWidget extends StatelessWidget { builder: (_) { if (dashboardViewModel.getWowneroError != null) { return Padding( - padding: const EdgeInsets.fromLTRB(16,0,16,16), - child: DashBoardRoundedCardWidget( - title: "Invalid wownero bindings", - subTitle: dashboardViewModel.getWowneroError.toString(), - onTap: () {}, - ) - ); + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), + child: DashBoardRoundedCardWidget( + title: "Invalid wownero bindings", + subTitle: dashboardViewModel.getWowneroError.toString(), + onTap: () {}, + )); } return Container(); }, @@ -273,6 +272,19 @@ class CryptoBalanceWidget extends StatelessWidget { currency: balance.asset, hasAdditionalBalance: dashboardViewModel.balanceViewModel.hasAdditionalBalance, + hasSecondAdditionalBalance: + dashboardViewModel.balanceViewModel.hasSecondAdditionalBalance, + hasSecondAvailableBalance: + dashboardViewModel.balanceViewModel.hasSecondAvailableBalance, + secondAdditionalBalance: balance.secondAdditionalBalance, + secondAdditionalFiatBalance: balance.fiatSecondAdditionalBalance, + secondAvailableBalance: balance.secondAvailableBalance, + secondAvailableFiatBalance: balance.fiatSecondAvailableBalance, + secondAdditionalBalanceLabel: + '${dashboardViewModel.balanceViewModel.additionalBalanceLabel}', + secondAvailableBalanceLabel: + '${dashboardViewModel.balanceViewModel.availableBalanceLabel}', + isTestnet: dashboardViewModel.isTestnet, ); }); @@ -286,16 +298,15 @@ class CryptoBalanceWidget extends StatelessWidget { if (dashboardViewModel.isMoneroWalletBrokenReasons.isNotEmpty) ...[ SizedBox(height: 10), Padding( - padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), - child: DashBoardRoundedCardWidget( - customBorder: 30, - title: "This wallet has encountered an issue", - subTitle: "Here are the things that you should note:\n - " - +dashboardViewModel.isMoneroWalletBrokenReasons.join("\n - ") - +"\n\nPlease restart your wallet and if it doesn't help contact our support.", - onTap: () {}, - ) - ) + padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), + child: DashBoardRoundedCardWidget( + customBorder: 30, + title: "This wallet has encountered an issue", + subTitle: "Here are the things that you should note:\n - " + + dashboardViewModel.isMoneroWalletBrokenReasons.join("\n - ") + + "\n\nPlease restart your wallet and if it doesn't help contact our support.", + onTap: () {}, + )) ], if (dashboardViewModel.showSilentPaymentsCard) ...[ SizedBox(height: 10), @@ -494,10 +505,18 @@ class BalanceRowWidget extends StatelessWidget { required this.additionalBalanceLabel, required this.additionalBalance, required this.additionalFiatBalance, + required this.secondAvailableBalanceLabel, + required this.secondAvailableBalance, + required this.secondAvailableFiatBalance, + required this.secondAdditionalBalanceLabel, + required this.secondAdditionalBalance, + required this.secondAdditionalFiatBalance, required this.frozenBalance, required this.frozenFiatBalance, required this.currency, required this.hasAdditionalBalance, + required this.hasSecondAvailableBalance, + required this.hasSecondAdditionalBalance, required this.isTestnet, super.key, }); @@ -508,10 +527,18 @@ class BalanceRowWidget extends StatelessWidget { final String additionalBalanceLabel; final String additionalBalance; final String additionalFiatBalance; + final String secondAvailableBalanceLabel; + final String secondAvailableBalance; + final String secondAvailableFiatBalance; + final String secondAdditionalBalanceLabel; + final String secondAdditionalBalance; + final String secondAdditionalFiatBalance; final String frozenBalance; final String frozenFiatBalance; final CryptoCurrency currency; final bool hasAdditionalBalance; + final bool hasSecondAvailableBalance; + final bool hasSecondAdditionalBalance; final bool isTestnet; // void _showBalanceDescription(BuildContext context) { @@ -759,6 +786,94 @@ class BalanceRowWidget extends StatelessWidget { ), ], ), + if (hasSecondAvailableBalance) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 24), + Text( + '${additionalBalanceLabel}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.labelTextColor, + height: 1, + ), + ), + SizedBox(height: 8), + AutoSizeText( + additionalBalance, + style: TextStyle( + fontSize: 20, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.assetTitleColor, + height: 1, + ), + maxLines: 1, + textAlign: TextAlign.center, + ), + SizedBox(height: 4), + if (!isTestnet) + Text( + '${additionalFiatBalance}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.textColor, + height: 1, + ), + ), + ], + ), + if (hasSecondAdditionalBalance) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 24), + Text( + '${additionalBalanceLabel}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.labelTextColor, + height: 1, + ), + ), + SizedBox(height: 8), + AutoSizeText( + additionalBalance, + style: TextStyle( + fontSize: 20, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.assetTitleColor, + height: 1, + ), + maxLines: 1, + textAlign: TextAlign.center, + ), + SizedBox(height: 4), + if (!isTestnet) + Text( + '${additionalFiatBalance}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.textColor, + height: 1, + ), + ), + ], + ), ], ), ), diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 045b552614..88249b7e17 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -21,10 +21,14 @@ class BalanceRecord { const BalanceRecord( {required this.availableBalance, required this.additionalBalance, + required this.secondAvailableBalance, + required this.secondAdditionalBalance, required this.frozenBalance, required this.fiatAvailableBalance, required this.fiatAdditionalBalance, required this.fiatFrozenBalance, + required this.fiatSecondAvailableBalance, + required this.fiatSecondAdditionalBalance, required this.asset, required this.formattedAssetTitle}); final String fiatAdditionalBalance; @@ -33,6 +37,10 @@ class BalanceRecord { final String additionalBalance; final String availableBalance; final String frozenBalance; + final String secondAvailableBalance; + final String secondAdditionalBalance; + final String fiatSecondAdditionalBalance; + final String fiatSecondAvailableBalance; final CryptoCurrency asset; final String formattedAssetTitle; } @@ -243,9 +251,13 @@ abstract class BalanceViewModelBase with Store { availableBalance: '---', additionalBalance: '---', frozenBalance: '---', + secondAvailableBalance: '---', + secondAdditionalBalance: '---', fiatAdditionalBalance: isFiatDisabled ? '' : '---', fiatAvailableBalance: isFiatDisabled ? '' : '---', fiatFrozenBalance: isFiatDisabled ? '' : '---', + fiatSecondAvailableBalance: isFiatDisabled ? '' : '---', + fiatSecondAdditionalBalance: isFiatDisabled ? '' : '---', asset: key, formattedAssetTitle: _formatterAsset(key))); } @@ -274,15 +286,31 @@ abstract class BalanceViewModelBase with Store { ' ' + _getFiatBalance(price: price, cryptoAmount: getFormattedFrozenBalance(value))); + final secondAdditionalFiatBalance = isFiatDisabled + ? '' + : (fiatCurrency.toString() + + ' ' + + _getFiatBalance(price: price, cryptoAmount: value.formattedSecondAdditionalBalance)); + + final secondAvailableFiatBalance = isFiatDisabled + ? '' + : (fiatCurrency.toString() + + ' ' + + _getFiatBalance(price: price, cryptoAmount: value.formattedSecondAvailableBalance)); + return MapEntry( key, BalanceRecord( availableBalance: value.formattedAvailableBalance, additionalBalance: value.formattedAdditionalBalance, frozenBalance: getFormattedFrozenBalance(value), + secondAvailableBalance: value.formattedSecondAvailableBalance, + secondAdditionalBalance: value.formattedSecondAdditionalBalance, fiatAdditionalBalance: additionalFiatBalance, fiatAvailableBalance: availableFiatBalance, fiatFrozenBalance: frozenFiatBalance, + fiatSecondAvailableBalance: secondAvailableFiatBalance, + fiatSecondAdditionalBalance: secondAdditionalFiatBalance, asset: key, formattedAssetTitle: _formatterAsset(key))); }); @@ -291,6 +319,12 @@ abstract class BalanceViewModelBase with Store { @computed bool get hasAdditionalBalance => _hasAdditionBalanceForWalletType(wallet.type); + @computed + bool get hasSecondAdditionalBalance => _hasSecondAddtionalBalanceForWalletType(wallet.type); + + @computed + bool get hasSecondAvailableBalance => _hasSecondAvailableBalanceForWalletType(wallet.type); + bool _hasAdditionBalanceForWalletType(WalletType type) { switch (type) { case WalletType.ethereum: @@ -303,6 +337,24 @@ abstract class BalanceViewModelBase with Store { } } + bool _hasSecondAddtionalBalanceForWalletType(WalletType type) { + switch (type) { + case WalletType.litecoin: + return true; + default: + return false; + } + } + + bool _hasSecondAvailableBalanceForWalletType(WalletType type) { + switch (type) { + case WalletType.litecoin: + return true; + default: + return false; + } + } + @computed List get formattedBalances { final balance = balances.values.toList(); From 6a4327a53b7ed81a372c1f9fae5d061cbc57e87c Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 12 Sep 2024 13:15:20 -0700 Subject: [PATCH 161/203] separate mweb balances, pt.2 --- cw_bitcoin/lib/litecoin_wallet.dart | 16 +++-- .../screens/dashboard/pages/balance_page.dart | 17 +++-- .../dashboard/balance_view_model.dart | 63 ++++++++++++++----- 3 files changed, 67 insertions(+), 29 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 52ff974fac..ec73cf98f4 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -319,7 +319,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { lastFewProgresses.add(syncStatus.progress()); if (lastFewProgresses.length < 4) return; // limit list size to 4: - while(lastFewProgresses.length > 4) { + while (lastFewProgresses.length > 4) { lastFewProgresses.removeAt(0); } // if the progress is the same over the last 40 seconds, restart the sync: @@ -676,12 +676,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { int confirmed = balance.confirmed; int unconfirmed = balance.unconfirmed; + int confirmedMweb = 0; + int unconfirmedMweb = 0; try { mwebUtxosBox.values.forEach((utxo) { if (utxo.height > 0) { - confirmed += utxo.value.toInt(); + confirmedMweb += utxo.value.toInt(); } else { - unconfirmed += utxo.value.toInt(); + unconfirmedMweb += utxo.value.toInt(); } }); } catch (_) {} @@ -733,7 +735,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } - return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); + return ElectrumBalance( + confirmed: confirmed, + unconfirmed: unconfirmed, + frozen: balance.frozen, + secondConfirmed: confirmedMweb, + secondUnconfirmed: unconfirmedMweb, + ); } @override diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index 2b662758a4..a7b2a99fa3 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -281,10 +281,9 @@ class CryptoBalanceWidget extends StatelessWidget { secondAvailableBalance: balance.secondAvailableBalance, secondAvailableFiatBalance: balance.fiatSecondAvailableBalance, secondAdditionalBalanceLabel: - '${dashboardViewModel.balanceViewModel.additionalBalanceLabel}', + '${dashboardViewModel.balanceViewModel.secondAdditionalBalanceLabel}', secondAvailableBalanceLabel: - '${dashboardViewModel.balanceViewModel.availableBalanceLabel}', - + '${dashboardViewModel.balanceViewModel.secondAvailableBalanceLabel}', isTestnet: dashboardViewModel.isTestnet, ); }); @@ -792,7 +791,7 @@ class BalanceRowWidget extends StatelessWidget { children: [ SizedBox(height: 24), Text( - '${additionalBalanceLabel}', + '${secondAvailableBalanceLabel}', textAlign: TextAlign.center, style: TextStyle( fontSize: 12, @@ -804,7 +803,7 @@ class BalanceRowWidget extends StatelessWidget { ), SizedBox(height: 8), AutoSizeText( - additionalBalance, + secondAvailableBalance, style: TextStyle( fontSize: 20, fontFamily: 'Lato', @@ -818,7 +817,7 @@ class BalanceRowWidget extends StatelessWidget { SizedBox(height: 4), if (!isTestnet) Text( - '${additionalFiatBalance}', + '${secondAvailableFiatBalance}', textAlign: TextAlign.center, style: TextStyle( fontSize: 12, @@ -836,7 +835,7 @@ class BalanceRowWidget extends StatelessWidget { children: [ SizedBox(height: 24), Text( - '${additionalBalanceLabel}', + '${secondAdditionalBalanceLabel}', textAlign: TextAlign.center, style: TextStyle( fontSize: 12, @@ -848,7 +847,7 @@ class BalanceRowWidget extends StatelessWidget { ), SizedBox(height: 8), AutoSizeText( - additionalBalance, + secondAdditionalBalance, style: TextStyle( fontSize: 20, fontFamily: 'Lato', @@ -862,7 +861,7 @@ class BalanceRowWidget extends StatelessWidget { SizedBox(height: 4), if (!isTestnet) Text( - '${additionalFiatBalance}', + '${secondAdditionalFiatBalance}', textAlign: TextAlign.center, style: TextStyle( fontSize: 12, diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 88249b7e17..eafeb5f958 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -166,6 +166,26 @@ abstract class BalanceViewModelBase with Store { } } + @computed + String get secondAvailableBalanceLabel { + switch (wallet.type) { + case WalletType.litecoin: + return "T: mweb confirmed"; + default: + return S.current.confirmed; + } + } + + @computed + String get secondAdditionalBalanceLabel { + switch (wallet.type) { + case WalletType.litecoin: + return "T: mweb unconfirmed"; + default: + return S.current.unconfirmed; + } + } + @computed bool get hasMultiBalance => appStore.wallet!.type == WalletType.haven; @@ -215,6 +235,17 @@ abstract class BalanceViewModelBase with Store { return walletBalance.formattedAdditionalBalance; } + @computed + String get secondAdditionalBalance { + final walletBalance = _walletBalance; + + if (displayMode == BalanceDisplayMode.hiddenBalance) { + return '---'; + } + + return walletBalance.formattedSecondAdditionalBalance; + } + @computed String get availableFiatBalance { final walletBalance = _walletBalance; @@ -317,15 +348,15 @@ abstract class BalanceViewModelBase with Store { } @computed - bool get hasAdditionalBalance => _hasAdditionBalanceForWalletType(wallet.type); + bool get hasAdditionalBalance => _hasAdditionalBalanceForWalletType(wallet.type); @computed - bool get hasSecondAdditionalBalance => _hasSecondAddtionalBalanceForWalletType(wallet.type); + bool get hasSecondAdditionalBalance => _hasSecondAdditionalBalanceForWalletType(wallet.type); @computed bool get hasSecondAvailableBalance => _hasSecondAvailableBalanceForWalletType(wallet.type); - bool _hasAdditionBalanceForWalletType(WalletType type) { + bool _hasAdditionalBalanceForWalletType(WalletType type) { switch (type) { case WalletType.ethereum: case WalletType.polygon: @@ -337,22 +368,22 @@ abstract class BalanceViewModelBase with Store { } } - bool _hasSecondAddtionalBalanceForWalletType(WalletType type) { - switch (type) { - case WalletType.litecoin: - return true; - default: - return false; - } + bool _hasSecondAdditionalBalanceForWalletType(WalletType type) { + // return _walletBalance.secondAdditional != null && _walletBalance.secondAdditional! != 0; + // if (wallet.type == WalletType.litecoin && settingsStore.mwebEnabled) { + // return true; + // } + // return false; + return true; } bool _hasSecondAvailableBalanceForWalletType(WalletType type) { - switch (type) { - case WalletType.litecoin: - return true; - default: - return false; - } + // return _walletBalance.secondAdditional != null && _walletBalance.secondAdditional! != 0; + // if (wallet.type == WalletType.litecoin && settingsStore.mwebEnabled) { + // return true; + // } + // return false; + return true; } @computed From 5c9a902b1ff5e10f2b9d1c722ff8df442cd29d4d Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 12 Sep 2024 22:53:04 -0700 Subject: [PATCH 162/203] [skip ci] save --- cw_bitcoin/lib/litecoin_wallet.dart | 115 +++++++++++++--------------- 1 file changed, 55 insertions(+), 60 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index ec73cf98f4..0cde7ab038 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -244,9 +244,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { print("STARTING SYNC - MWEB ENABLED: $mwebEnabled"); syncStatus = SyncronizingSyncStatus(); await subscribeForUpdates(); - await updateTransactions(); - await updateFeeRates(); + await updateFeeRates(); _feeRatesTimer?.cancel(); _feeRatesTimer = Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); @@ -265,74 +264,70 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } await waitForMwebAddresses(); - await getStub(); + await processMwebUtxos(); + await updateTransactions(); await updateUnspent(); await updateBalance(); _syncTimer?.cancel(); - // delay the timer by a second so we don't overrride the restoreheight if one is set - Timer(const Duration(seconds: 2), () async { - _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { - if (syncStatus is FailedSyncStatus) return; - - final nodeHeight = - await electrumClient.getCurrentBlockChainTip() ?? 0; // current block height of our node - final resp = await _stub.status(StatusRequest()); - - if (resp.blockHeaderHeight < nodeHeight) { - int h = resp.blockHeaderHeight; - syncStatus = SyncingSyncStatus(nodeHeight - h, h / nodeHeight); - } else if (resp.mwebHeaderHeight < nodeHeight) { - int h = resp.mwebHeaderHeight; - syncStatus = SyncingSyncStatus(nodeHeight - h, h / nodeHeight); - } else if (resp.mwebUtxosHeight < nodeHeight) { - syncStatus = SyncingSyncStatus(1, 0.999); - } else { - // prevent unnecessary reaction triggers: - if (syncStatus is! SyncedSyncStatus) { - syncStatus = SyncedSyncStatus(); - } + _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { + if (syncStatus is FailedSyncStatus) return; + + final nodeHeight = + await electrumClient.getCurrentBlockChainTip() ?? 0; // current block height of our node + final resp = await _stub.status(StatusRequest()); + + if (resp.blockHeaderHeight < nodeHeight) { + int h = resp.blockHeaderHeight; + syncStatus = SyncingSyncStatus(nodeHeight - h, h / nodeHeight); + } else if (resp.mwebHeaderHeight < nodeHeight) { + int h = resp.mwebHeaderHeight; + syncStatus = SyncingSyncStatus(nodeHeight - h, h / nodeHeight); + } else if (resp.mwebUtxosHeight < nodeHeight) { + syncStatus = SyncingSyncStatus(1, 0.999); + } else { + // prevent unnecessary reaction triggers: + if (syncStatus is! SyncedSyncStatus) { + syncStatus = SyncedSyncStatus(); + } - if (resp.mwebUtxosHeight > walletInfo.restoreHeight) { - await walletInfo.updateRestoreHeight(resp.mwebUtxosHeight); - await checkMwebUtxosSpent(); - // update the confirmations for each transaction: - for (final transaction in transactionHistory.transactions.values) { - if (transaction.isPending) continue; - int txHeight = transaction.height ?? resp.mwebUtxosHeight; - final confirmations = (resp.mwebUtxosHeight - txHeight) + 1; - if (transaction.confirmations == confirmations) continue; - transaction.confirmations = confirmations; - transactionHistory.addOne(transaction); - } - await transactionHistory.save(); + if (resp.mwebUtxosHeight > walletInfo.restoreHeight) { + await walletInfo.updateRestoreHeight(resp.mwebUtxosHeight); + await checkMwebUtxosSpent(); + // update the confirmations for each transaction: + for (final transaction in transactionHistory.transactions.values) { + if (transaction.isPending) continue; + int txHeight = transaction.height ?? resp.mwebUtxosHeight; + final confirmations = (resp.mwebUtxosHeight - txHeight) + 1; + if (transaction.confirmations == confirmations) continue; + transaction.confirmations = confirmations; + transactionHistory.addOne(transaction); } + await transactionHistory.save(); } - }); + } + }); - // setup a watch dog to restart the sync process if it gets stuck: - List lastFewProgresses = []; - Timer.periodic(const Duration(seconds: 10), (timer) async { - if (syncStatus is! SyncingSyncStatus) return; - if (syncStatus.progress() > 0.98) return; - lastFewProgresses.add(syncStatus.progress()); - if (lastFewProgresses.length < 4) return; - // limit list size to 4: - while (lastFewProgresses.length > 4) { - lastFewProgresses.removeAt(0); - } - // if the progress is the same over the last 40 seconds, restart the sync: - if (lastFewProgresses.every((p) => p == lastFewProgresses.first)) { - print("mweb syncing is stuck, restarting..."); - await stopSync(); - startSync(); - timer.cancel(); - } - }); + // setup a watch dog to restart the sync process if it gets stuck: + List lastFewProgresses = []; + Timer.periodic(const Duration(seconds: 10), (timer) async { + if (syncStatus is! SyncingSyncStatus) return; + if (syncStatus.progress() > 0.98) return; + lastFewProgresses.add(syncStatus.progress()); + if (lastFewProgresses.length < 4) return; + // limit list size to 4: + while (lastFewProgresses.length > 4) { + lastFewProgresses.removeAt(0); + } + // if the progress is the same over the last 40 seconds, restart the sync: + if (lastFewProgresses.every((p) => p == lastFewProgresses.first)) { + print("mweb syncing is stuck, restarting..."); + await stopSync(); + startSync(); + timer.cancel(); + } }); - // this runs in the background and processes new utxos as they come in: - processMwebUtxos(); } @action From 245f4d665d70693d8225d206566575492a9b4687 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 13 Sep 2024 09:00:26 -0700 Subject: [PATCH 163/203] add translations [skip ci] --- .../dashboard/balance_view_model.dart | 4 ++-- 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_hy.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_vi.arb | 18 ++++++++++-------- res/values/strings_yo.arb | 2 ++ res/values/strings_zh.arb | 2 ++ 29 files changed, 66 insertions(+), 10 deletions(-) diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index eafeb5f958..467b94241b 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -170,7 +170,7 @@ abstract class BalanceViewModelBase with Store { String get secondAvailableBalanceLabel { switch (wallet.type) { case WalletType.litecoin: - return "T: mweb confirmed"; + return S.current.mweb_confirmed; default: return S.current.confirmed; } @@ -180,7 +180,7 @@ abstract class BalanceViewModelBase with Store { String get secondAdditionalBalanceLabel { switch (wallet.type) { case WalletType.litecoin: - return "T: mweb unconfirmed"; + return S.current.mweb_unconfirmed; default: return S.current.unconfirmed; } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index ae4344222f..d9c810c7f1 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -396,6 +396,8 @@ "monero_light_theme": " ضوء مونيرو", "moonpay_alert_text": "يجب أن تكون قيمة المبلغ أكبر من أو تساوي ${minAmount} ${fiatCurrency}", "more_options": "المزيد من الخيارات", + "mweb_confirmed": "أكد MWEB", + "mweb_unconfirmed": "غير مؤكد MWEB", "name": "ﻢﺳﺍ", "nano_current_rep": "الممثل الحالي", "nano_gpt_thanks_message": "شكرا لاستخدام nanogpt! تذكر أن تعود إلى المتصفح بعد اكتمال معاملتك!", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 5a4fe8d6dc..955fb71cd5 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Лека тема Monero", "moonpay_alert_text": "Сумата трябва да бъде най-малко ${minAmount} ${fiatCurrency}", "more_options": "Още настройки", + "mweb_confirmed": "Потвърден MWeb", + "mweb_unconfirmed": "Непотвърден mweb", "name": "Име", "nano_current_rep": "Настоящ представител", "nano_gpt_thanks_message": "Благодаря, че използвахте Nanogpt! Не забравяйте да се върнете обратно към браузъра, след като транзакцията ви приключи!", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 7469233f1e..b6cf8b6ec1 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Světlé téma Monero", "moonpay_alert_text": "Částka musí být větší nebo rovna ${minAmount} ${fiatCurrency}", "more_options": "Více možností", + "mweb_confirmed": "Potvrzený mweb", + "mweb_unconfirmed": "Nepotvrzené mWeb", "name": "název", "nano_current_rep": "Současný zástupce", "nano_gpt_thanks_message": "Děkujeme za používání Nanogpt! Nezapomeňte se po dokončení transakce vydat zpět do prohlížeče!", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 558e2ccf81..bd861cc144 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Monero Light-Thema", "moonpay_alert_text": "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein", "more_options": "Weitere Optionen", + "mweb_confirmed": "Bestätigt MWeb", + "mweb_unconfirmed": "Unbestätigter MWeb", "name": "Name", "nano_current_rep": "Aktueller Vertreter", "nano_gpt_thanks_message": "Danke, dass du Nanogpt benutzt hast! Denken Sie daran, nach Abschluss Ihrer Transaktion zurück zum Browser zu gehen!", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 0fc657cbd8..0f5a0dec1a 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Monero Light Theme", "moonpay_alert_text": "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}", "more_options": "More Options", + "mweb_confirmed": "Confirmed MWEB", + "mweb_unconfirmed": "Unconfirmed MWEB", "name": "Name", "nano_current_rep": "Current Representative", "nano_gpt_thanks_message": "Thanks for using NanoGPT! Remember to head back to the browser after your transaction completes!", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 4eba224566..3663e536fe 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Tema ligero de Monero", "moonpay_alert_text": "El valor de la cantidad debe ser mayor o igual a ${minAmount} ${fiatCurrency}", "more_options": "Más Opciones", + "mweb_confirmed": "Confirmado mweb", + "mweb_unconfirmed": "Mweb no confirmado", "name": "Nombre", "nano_current_rep": "Representante actual", "nano_gpt_thanks_message": "¡Gracias por usar nanogpt! ¡Recuerde regresar al navegador después de que se complete su transacción!", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index e01a97ea26..e54eede313 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Thème de lumière Monero", "moonpay_alert_text": "Le montant doit être au moins égal à ${minAmount} ${fiatCurrency}", "more_options": "Plus d'options", + "mweb_confirmed": "Confirmé MWEB", + "mweb_unconfirmed": "Mweb non confirmé", "name": "Nom", "nano_current_rep": "Représentant actuel", "nano_gpt_thanks_message": "Merci d'avoir utilisé Nanogpt! N'oubliez pas de retourner au navigateur une fois votre transaction terminée!", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 0a5192803d..0c52351626 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Jigon Hasken Monero", "moonpay_alert_text": "Darajar adadin dole ne ya zama fiye ko daidai da ${minAmount} ${fiatCurrency}", "more_options": "Ƙarin Zaɓuɓɓuka", + "mweb_confirmed": "Tabbatar da Mweb", + "mweb_unconfirmed": "Myconfired", "name": "Suna", "nano_current_rep": "Wakilin Yanzu", "nano_gpt_thanks_message": "Na gode da amfani da Nanogpt! Ka tuna da komawa zuwa mai bincike bayan ma'amalar ka ta cika!", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index f56c11b203..60e01f2268 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -396,6 +396,8 @@ "monero_light_theme": "मोनेरो लाइट थीम", "moonpay_alert_text": "राशि का मूल्य अधिक है या करने के लिए बराबर होना चाहिए ${minAmount} ${fiatCurrency}", "more_options": "और विकल्प", + "mweb_confirmed": "MWEB की पुष्टि की", + "mweb_unconfirmed": "अपुष्ट MWEB", "name": "नाम", "nano_current_rep": "वर्तमान प्रतिनिधि", "nano_gpt_thanks_message": "Nanogpt का उपयोग करने के लिए धन्यवाद! अपने लेन -देन के पूरा होने के बाद ब्राउज़र पर वापस जाना याद रखें!", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index fd78fcc6af..1a46552d36 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Monero lagana tema", "moonpay_alert_text": "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}", "more_options": "Više opcija", + "mweb_confirmed": "Potvrđen MWeb", + "mweb_unconfirmed": "Nepotvrđeni mWeb", "name": "Ime", "nano_current_rep": "Trenutni predstavnik", "nano_gpt_thanks_message": "Hvala što ste koristili nanogpt! Ne zaboravite da se vratite u preglednik nakon što vam se transakcija završi!", diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index 21b940a63a..f4b60272b0 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -387,6 +387,8 @@ "monero_light_theme": "Monero պայծառ տեսք", "moonpay_alert_text": "Գումարի արժեքը պետք է լինի հավասար կամ ավելի քան ${minAmount} ${fiatCurrency}", "more_options": "Այլ տարբերակներ", + "mweb_confirmed": "Հաստատված MWEB", + "mweb_unconfirmed": "Չկարգավորված Mweb", "name": "Անուն", "nano_current_rep": "Ընթացիկ ներկայացուցիչ", "nano_gpt_thanks_message": "Շնորհակալություն NanoGPT-ն օգտագործելու համար: Հիշեք վերադառնալ դիտարկիչ ձեր փոխանցումն ավարտելուց հետո", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 97265a6414..2b7eed1df5 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Tema Cahaya Monero", "moonpay_alert_text": "Nilai jumlah harus lebih atau sama dengan ${minAmount} ${fiatCurrency}", "more_options": "Opsi Lainnya", + "mweb_confirmed": "Mengkonfirmasi mWeb", + "mweb_unconfirmed": "MWEB yang belum dikonfirmasi", "name": "Nama", "nano_current_rep": "Perwakilan saat ini", "nano_gpt_thanks_message": "Terima kasih telah menggunakan Nanogpt! Ingatlah untuk kembali ke browser setelah transaksi Anda selesai!", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 268a1c7485..1638959733 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -397,6 +397,8 @@ "monero_light_theme": "Tema leggero Monero", "moonpay_alert_text": "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}", "more_options": "Altre opzioni", + "mweb_confirmed": "MWeb confermato", + "mweb_unconfirmed": "MWeb non confermato", "name": "Nome", "nano_current_rep": "Rappresentante attuale", "nano_gpt_thanks_message": "Grazie per aver usato il nanogpt! Ricorda di tornare al browser dopo il completamento della transazione!", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index be1b822be6..9d4ce29a27 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -397,6 +397,8 @@ "monero_light_theme": "モネロ ライト テーマ", "moonpay_alert_text": "金額の値は以上でなければなりません ${minAmount} ${fiatCurrency}", "more_options": "その他のオプション", + "mweb_confirmed": "確認されたMWEB", + "mweb_unconfirmed": "未確認のMWEB", "name": "名前", "nano_current_rep": "現在の代表", "nano_gpt_thanks_message": "NanoGptを使用してくれてありがとう!トランザクションが完了したら、ブラウザに戻ることを忘れないでください!", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index bf0229ed22..1d605e160c 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -396,6 +396,8 @@ "monero_light_theme": "모네로 라이트 테마", "moonpay_alert_text": "금액은 다음보다 크거나 같아야합니다 ${minAmount} ${fiatCurrency}", "more_options": "추가 옵션", + "mweb_confirmed": "확인 mweb", + "mweb_unconfirmed": "확인되지 않은 mweb", "name": "이름", "nano_current_rep": "현재 대표", "nano_gpt_thanks_message": "Nanogpt를 사용해 주셔서 감사합니다! 거래가 완료된 후 브라우저로 돌아가는 것을 잊지 마십시오!", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index cfd6569c10..a7db770c93 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Monero Light အပြင်အဆင်", "moonpay_alert_text": "ပမာဏ၏တန်ဖိုးသည် ${minAmount} ${fiatCurrency} နှင့် ပိုနေရမည်", "more_options": "နောက်ထပ် ရွေးချယ်စရာများ", + "mweb_confirmed": "အတည်ပြုလိုက် mweb", + "mweb_unconfirmed": "အတည်မပြုနိုင်သော mweb", "name": "နာမည်", "nano_current_rep": "လက်ရှိကိုယ်စားလှယ်", "nano_gpt_thanks_message": "nanogpt ကိုသုံးပြီးကျေးဇူးတင်ပါတယ် သင်၏ငွေပေးငွေယူပြီးနောက် browser သို့ပြန်သွားရန်သတိရပါ။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index b244f4017f..a1749f89ba 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Monero Light-thema", "moonpay_alert_text": "Waarde van het bedrag moet meer of gelijk zijn aan ${minAmount} ${fiatCurrency}", "more_options": "Meer opties", + "mweb_confirmed": "Bevestigde MWEB", + "mweb_unconfirmed": "Onbevestigde MWEB", "name": "Naam", "nano_current_rep": "Huidige vertegenwoordiger", "nano_gpt_thanks_message": "Bedankt voor het gebruik van Nanogpt! Vergeet niet om terug te gaan naar de browser nadat uw transactie is voltooid!", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 8763afe9c5..553a4c9728 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Lekki motyw Monero", "moonpay_alert_text": "Wartość kwoty musi być większa lub równa ${minAmount} ${fiatCurrency}", "more_options": "Więcej opcji", + "mweb_confirmed": "Potwierdził MWEB", + "mweb_unconfirmed": "Niepotwierdzone MWEB", "name": "Nazwa", "nano_current_rep": "Obecny przedstawiciel", "nano_gpt_thanks_message": "Dzięki za użycie Nanogpt! Pamiętaj, aby wrócić do przeglądarki po zakończeniu transakcji!", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index e470e66ded..0b772fa7e5 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -397,6 +397,8 @@ "monero_light_theme": "Monero Light Theme", "moonpay_alert_text": "O valor do montante deve ser maior ou igual a ${minAmount} ${fiatCurrency}", "more_options": "Mais opções", + "mweb_confirmed": "MWEB confirmado", + "mweb_unconfirmed": "MWEB não confirmado", "name": "Nome", "nano_current_rep": "Representante atual", "nano_gpt_thanks_message": "Obrigado por usar o Nanogpt! Lembre -se de voltar para o navegador após a conclusão da transação!", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 6453d012ff..171dfde242 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Светлая тема Monero", "moonpay_alert_text": "Сумма должна быть больше или равна ${minAmount} ${fiatCurrency}", "more_options": "Дополнительные параметры", + "mweb_confirmed": "Подтверждено MWEB", + "mweb_unconfirmed": "Неподтвержденная MWEB", "name": "Имя", "nano_current_rep": "Нынешний представитель", "nano_gpt_thanks_message": "Спасибо за использование Nanogpt! Не забудьте вернуться в браузер после завершения транзакции!", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index cde37e3b24..494320ce5d 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -396,6 +396,8 @@ "monero_light_theme": "ธีมแสง Monero", "moonpay_alert_text": "มูลค่าของจำนวนต้องมากกว่าหรือเท่ากับ ${minAmount} ${fiatCurrency}", "more_options": "ตัวเลือกเพิ่มเติม", + "mweb_confirmed": "MWEB ยืนยันแล้ว", + "mweb_unconfirmed": "mweb ที่ไม่ได้รับการยืนยัน", "name": "ชื่อ", "nano_current_rep": "ตัวแทนปัจจุบัน", "nano_gpt_thanks_message": "ขอบคุณที่ใช้ Nanogpt! อย่าลืมกลับไปที่เบราว์เซอร์หลังจากการทำธุรกรรมของคุณเสร็จสิ้น!", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index c8d4262cec..3f6bf5e410 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Monero Light Theme", "moonpay_alert_text": "Ang halaga ay dapat na higit pa o katumbas ng ${minAmount} ${fiatCurrency}", "more_options": "Higit pang mga Pagpipilian", + "mweb_confirmed": "Nakumpirma na MWeb", + "mweb_unconfirmed": "Hindi nakumpirma si Mweb", "name": "Pangalan", "nano_current_rep": "Kasalukuyang Representative", "nano_gpt_thanks_message": "Salamat sa paggamit ng NanoGPT! Tandaan na bumalik sa browser matapos makumpleto ang iyong transaksyon!", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 3fe0a91be3..e4194e62e0 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Monero Hafif Tema", "moonpay_alert_text": "Tutar ${minAmount} ${fiatCurrency} miktarına eşit veya daha fazla olmalıdır", "more_options": "Daha Fazla Seçenek", + "mweb_confirmed": "Onaylanmış mweb", + "mweb_unconfirmed": "Doğrulanmamış mweb", "name": "İsim", "nano_current_rep": "Mevcut temsilci", "nano_gpt_thanks_message": "Nanogpt kullandığınız için teşekkürler! İşleminiz tamamlandıktan sonra tarayıcıya geri dönmeyi unutmayın!", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 4c7d7b1e02..8f7387a1f7 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -396,6 +396,8 @@ "monero_light_theme": "Легка тема Monero", "moonpay_alert_text": "Значення суми має бути більшим або дорівнювати ${minAmount} ${fiatCurrency}", "more_options": "Більше параметрів", + "mweb_confirmed": "Підтвердив Mweb", + "mweb_unconfirmed": "Неперевірений MWEB", "name": "Ім'я", "nano_current_rep": "Поточний представник", "nano_gpt_thanks_message": "Дякуємо за використання наногпта! Не забудьте повернутися до браузера після завершення транзакції!", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 50f191d823..af021737d6 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -396,6 +396,8 @@ "monero_light_theme": "مونیرو لائٹ تھیم", "moonpay_alert_text": "رقم کی قدر ${minAmount} ${fiatCurrency} کے برابر یا زیادہ ہونی چاہیے۔", "more_options": "مزید زرائے", + "mweb_confirmed": "تصدیق شدہ MWEB", + "mweb_unconfirmed": "غیر مصدقہ MWEB", "name": "ﻡﺎﻧ", "nano_current_rep": "موجودہ نمائندہ", "nano_gpt_thanks_message": "نانوگپٹ استعمال کرنے کا شکریہ! اپنے لین دین کی تکمیل کے بعد براؤزر کی طرف واپس جانا یاد رکھیں!", diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb index d2c98fe2b8..e0f4a6664c 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -140,8 +140,8 @@ "confirm": "Xác nhận", "confirm_delete_template": "Thao tác này sẽ xóa mẫu này. Bạn có muốn tiếp tục không?", "confirm_delete_wallet": "Thao tác này sẽ xóa ví này. Bạn có muốn tiếp tục không?", - "confirm_fee_deduction": "Xác nhận Khấu trừ Phí", "confirm_fee_dedction_content": "Bạn có đồng ý trừ phí từ đầu ra không?", + "confirm_fee_deduction": "Xác nhận Khấu trừ Phí", "confirm_sending": "Xác nhận gửi", "confirm_silent_payments_switch_node": "Nút hiện tại của bạn không hỗ trợ thanh toán im lặng\\nCake Wallet sẽ chuyển sang một nút tương thích chỉ để quét", "confirmations": "Xác nhận", @@ -298,7 +298,7 @@ "fiat_balance": "Số dư Fiat", "field_required": "Trường này là bắt buộc", "fill_code": "Vui lòng điền mã xác minh được gửi đến email của bạn", - "filter_by": "Lọc theo", + "filter_by": "Lọc theo", "first_wallet_text": "Ví tuyệt vời cho Monero, Bitcoin, Ethereum, Litecoin, và Haven", "fixed_pair_not_supported": "Cặp tỷ giá cố định này không được hỗ trợ với các sàn giao dịch đã chọn", "fixed_rate": "Tỷ giá cố định", @@ -386,6 +386,8 @@ "monero_light_theme": "Chủ đề sáng Monero", "moonpay_alert_text": "Giá trị số tiền phải lớn hơn hoặc bằng ${minAmount} ${fiatCurrency}", "more_options": "Thêm tùy chọn", + "mweb_confirmed": "Xác nhận MWEB", + "mweb_unconfirmed": "MWEB chưa được xác nhận", "name": "Tên", "nano_current_rep": "Đại diện hiện tại", "nano_gpt_thanks_message": "Cảm ơn bạn đã sử dụng NanoGPT! Hãy nhớ quay lại trình duyệt sau khi giao dịch của bạn hoàn tất!", @@ -398,7 +400,7 @@ "new_subaddress_label_name": "Tên nhãn", "new_subaddress_title": "Địa chỉ mới", "new_template": "Mẫu mới", - "new_wallet": "Ví mới", + "new_wallet": "Ví mới", "newConnection": "Kết nối mới", "no_cards_found": "Không tìm thấy thẻ", "no_id_needed": "Không cần ID!", @@ -498,7 +500,7 @@ "red_dark_theme": "Chủ đề tối đỏ", "red_light_theme": "Chủ đề sáng đỏ", "redeemed": "Đã đổi", - "refund_address": "Địa chỉ hoàn tiền", + "refund_address": "Địa chỉ hoàn tiền", "reject": "Từ chối", "remaining": "còn lại", "remove": "Gỡ bỏ", @@ -598,7 +600,7 @@ "seedtype": "Loại hạt giống", "seedtype_legacy": "Di sản (25 từ)", "seedtype_polyseed": "Polyseed (16 từ)", - "seedtype_wownero": "Wownero (14 từ)", + "seedtype_wownero": "Wownero (14 từ)", "select_backup_file": "Chọn tệp sao lưu", "select_buy_provider_notice": "Chọn nhà cung cấp mua ở trên. Bạn có thể bỏ qua màn hình này bằng cách thiết lập nhà cung cấp mua mặc định trong cài đặt ứng dụng.", "select_destination": "Vui lòng chọn đích cho tệp sao lưu.", @@ -698,7 +700,7 @@ "support_description_guides": "Tài liệu và hỗ trợ cho các vấn đề phổ biến", "support_description_live_chat": "Miễn phí và nhanh chóng! Các đại diện hỗ trợ được đào tạo sẵn sàng hỗ trợ", "support_description_other_links": "Tham gia cộng đồng của chúng tôi hoặc liên hệ với chúng tôi hoặc các đối tác của chúng tôi qua các phương pháp khác", - "support_title_guides": "Hướng dẫn Cake Wallet", + "support_title_guides": "Hướng dẫn Cake Wallet", "support_title_live_chat": "Hỗ trợ trực tiếp", "support_title_other_links": "Liên kết hỗ trợ khác", "sweeping_wallet": "Quét ví", @@ -798,7 +800,7 @@ "trongrid_history": "Lịch sử TronGrid", "trusted": "Đã tin cậy", "tx_commit_exception_no_dust_on_change": "Giao dịch bị từ chối với số tiền này. Với số tiền này bạn có thể gửi ${min} mà không cần đổi tiền lẻ hoặc ${max} trả lại tiền lẻ.", - "tx_commit_failed": "Giao dịch không thành công. Vui lòng liên hệ với hỗ trợ.", + "tx_commit_failed": "Giao dịch không thành công. Vui lòng liên hệ với hỗ trợ.", "tx_invalid_input": "Bạn đang sử dụng loại đầu vào sai cho loại thanh toán này", "tx_no_dust_exception": "Giao dịch bị từ chối vì gửi một số tiền quá nhỏ. Vui lòng thử tăng số tiền.", "tx_not_enough_inputs_exception": "Không đủ đầu vào có sẵn. Vui lòng chọn thêm dưới Coin Control", @@ -897,4 +899,4 @@ "you_will_get": "Chuyển đổi thành", "you_will_send": "Chuyển đổi từ", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 2da577e8cd..96307465fd 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -397,6 +397,8 @@ "monero_light_theme": "Monero Light Akori", "moonpay_alert_text": "Iye owó kò gbọ́dọ̀ kéré ju ${minAmount} ${fiatCurrency}", "more_options": "Ìyàn àfikún", + "mweb_confirmed": "Jẹrisi Mweb", + "mweb_unconfirmed": "Ajopo Mweb", "name": "Oruko", "nano_current_rep": "Aṣoju lọwọlọwọ", "nano_gpt_thanks_message": "O ṣeun fun lilo Nonnogt! Ranti lati tẹle pada si ẹrọ lilọ kiri ayelujara lẹhin iṣowo rẹ pari!", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 837d4162fa..fa74a52ec6 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -396,6 +396,8 @@ "monero_light_theme": "门罗币浅色主题", "moonpay_alert_text": "金额的价值必须大于或等于 ${minAmount} ${fiatCurrency}", "more_options": "更多选项", + "mweb_confirmed": "确认的MWEB", + "mweb_unconfirmed": "未经证实的MWEB", "name": "姓名", "nano_current_rep": "当前代表", "nano_gpt_thanks_message": "感谢您使用Nanogpt!事务完成后,请记住回到浏览器!", From 3a969acc6d3564397fe6d051d86489c9f538fea2 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 13 Sep 2024 11:49:03 -0700 Subject: [PATCH 164/203] fix sending with mweb amounts --- cw_bitcoin/lib/electrum_balance.dart | 15 +++++++++----- cw_bitcoin/lib/electrum_wallet.dart | 2 +- cw_bitcoin/lib/litecoin_wallet.dart | 2 ++ cw_bitcoin/lib/litecoin_wallet_addresses.dart | 10 ++++++++++ cw_core/lib/balance.dart | 2 ++ .../screens/receive/widgets/address_list.dart | 5 ++++- .../dashboard/balance_view_model.dart | 20 ++++++++----------- lib/view_model/send/send_view_model.dart | 2 +- 8 files changed, 38 insertions(+), 20 deletions(-) diff --git a/cw_bitcoin/lib/electrum_balance.dart b/cw_bitcoin/lib/electrum_balance.dart index ce6b737784..fb0f059d81 100644 --- a/cw_bitcoin/lib/electrum_balance.dart +++ b/cw_bitcoin/lib/electrum_balance.dart @@ -7,8 +7,8 @@ class ElectrumBalance extends Balance { required this.confirmed, required this.unconfirmed, required this.frozen, - this.secondConfirmed, - this.secondUnconfirmed, + this.secondConfirmed = 0, + this.secondUnconfirmed = 0, }) : super( confirmed, unconfirmed, @@ -32,8 +32,8 @@ class ElectrumBalance extends Balance { int confirmed; int unconfirmed; final int frozen; - int? secondConfirmed; - int? secondUnconfirmed; + int secondConfirmed = 0; + int secondUnconfirmed = 0; @override String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed - frozen); @@ -51,7 +51,12 @@ class ElectrumBalance extends Balance { String get formattedSecondAvailableBalance => bitcoinAmountToString(amount: secondConfirmed ?? 0); @override - String get formattedSecondAdditionalBalance => bitcoinAmountToString(amount: secondUnconfirmed ?? 0); + String get formattedSecondAdditionalBalance => + bitcoinAmountToString(amount: secondUnconfirmed ?? 0); + + @override + String get formattedFullAvailableBalance => + bitcoinAmountToString(amount: confirmed + (secondConfirmed ?? 0) - frozen); String toJSON() => json.encode({ 'confirmed': confirmed, diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 1babeaa6a2..8fbb50ec3d 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -869,7 +869,7 @@ abstract class ElectrumWalletBase final totalAmount = amount + fee; - if (totalAmount > balance[currency]!.confirmed) { + if (totalAmount > (balance[currency]!.confirmed + balance[currency]!.secondConfirmed)) { throw BitcoinTransactionWrongBalanceException(); } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 0cde7ab038..8b2861af0f 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -676,8 +676,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { try { mwebUtxosBox.values.forEach((utxo) { if (utxo.height > 0) { + // confirmed += utxo.value.toInt(); confirmedMweb += utxo.value.toInt(); } else { + // unconfirmed += utxo.value.toInt(); unconfirmedMweb += utxo.value.toInt(); } }); diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index dd590f1241..3a78565167 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -122,6 +122,10 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Future getChangeAddress({List? outputs, UtxoDetails? utxoDetails}) async { // use regular change address on peg in, otherwise use mweb for change address: + if (!mwebEnabled) { + return super.getChangeAddress(); + } + if (outputs != null && utxoDetails != null) { // check if this is a PEGIN: bool outputsToMweb = false; @@ -134,6 +138,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with outputsToMweb = true; } } + // TODO: this doesn't respect coin control because it doesn't know which available inputs are selected utxoDetails.availableInputs.forEach((element) { if (element.address.contains("mweb")) { comesFromMweb = true; @@ -144,6 +149,11 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with if (isPegIn && mwebEnabled) { return super.getChangeAddress(); } + + // use regular change address if it's not an mweb tx: + if (!comesFromMweb && !outputsToMweb) { + return super.getChangeAddress(); + } } if (mwebEnabled) { diff --git a/cw_core/lib/balance.dart b/cw_core/lib/balance.dart index 579a6da8fa..09ca8efbb0 100644 --- a/cw_core/lib/balance.dart +++ b/cw_core/lib/balance.dart @@ -13,4 +13,6 @@ abstract class Balance { String get formattedUnAvailableBalance => ''; String get formattedSecondAvailableBalance => ''; String get formattedSecondAdditionalBalance => ''; + String get formattedFullAvailableBalance => ''; + String get formattedFullUnAvailableBalance => ''; } diff --git a/lib/src/screens/receive/widgets/address_list.dart b/lib/src/screens/receive/widgets/address_list.dart index 8dfbedec14..de01f6879a 100644 --- a/lib/src/screens/receive/widgets/address_list.dart +++ b/lib/src/screens/receive/widgets/address_list.dart @@ -1,3 +1,6 @@ + +import 'dart:math'; + import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; @@ -35,7 +38,7 @@ class AddressList extends StatelessWidget { separatorBuilder: (context, _) => const HorizontalSectionDivider(), shrinkWrap: true, physics: NeverScrollableScrollPhysics(), - itemCount: addressListViewModel.items.length, + itemCount: min(addressListViewModel.items.length, 100),// TODO: don't show all 1000 mweb addresses itemBuilder: (context, index) { final item = addressListViewModel.items[index]; Widget cell = Container(); diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 467b94241b..2d917c8201 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -369,21 +369,17 @@ abstract class BalanceViewModelBase with Store { } bool _hasSecondAdditionalBalanceForWalletType(WalletType type) { - // return _walletBalance.secondAdditional != null && _walletBalance.secondAdditional! != 0; - // if (wallet.type == WalletType.litecoin && settingsStore.mwebEnabled) { - // return true; - // } - // return false; - return true; + if (wallet.type == WalletType.litecoin /*&& settingsStore.mwebEnabled*/) { + return true; + } + return false; } bool _hasSecondAvailableBalanceForWalletType(WalletType type) { - // return _walletBalance.secondAdditional != null && _walletBalance.secondAdditional! != 0; - // if (wallet.type == WalletType.litecoin && settingsStore.mwebEnabled) { - // return true; - // } - // return false; - return true; + if (wallet.type == WalletType.litecoin /*&& settingsStore.mwebEnabled*/) { + return true; + } + return false; } @computed diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 22c0834556..04a914a845 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -217,7 +217,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor PendingTransaction? pendingTransaction; @computed - String get balance => wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance; + String get balance => wallet.balance[selectedCryptoCurrency]!.formattedFullAvailableBalance; @computed bool get isFiatDisabled => balanceViewModel.isFiatDisabled; From 4503ad540166e179fbf930347036402851ed5577 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 13 Sep 2024 13:03:02 -0700 Subject: [PATCH 165/203] works for simple mweb-mweb case, further testing needed --- cw_bitcoin/lib/litecoin_wallet.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 8b2861af0f..47b014bb98 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -676,13 +676,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { try { mwebUtxosBox.values.forEach((utxo) { if (utxo.height > 0) { - // confirmed += utxo.value.toInt(); confirmedMweb += utxo.value.toInt(); } else { - // unconfirmed += utxo.value.toInt(); unconfirmedMweb += utxo.value.toInt(); } }); + if (confirmedMweb > 0 && unconfirmedMweb > 0) { + unconfirmedMweb = -1 * (confirmedMweb - unconfirmedMweb); + } } catch (_) {} // update unspent balances: @@ -872,7 +873,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final addresses = {}; transaction.inputAddresses?.forEach((id) async { final utxo = mwebUtxosBox.get(id); - await mwebUtxosBox.delete(id); + // await mwebUtxosBox.delete(id); if (utxo == null) return; final addressRecord = walletAddresses.allAddresses .firstWhere((addressRecord) => addressRecord.address == utxo.address); From 17695d1878cc15700964799ce18fc0b821dd017b Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 13 Sep 2024 13:59:10 -0700 Subject: [PATCH 166/203] found an edge case --- cw_bitcoin/lib/litecoin_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 47b014bb98..3f82cb54ba 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -681,7 +681,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unconfirmedMweb += utxo.value.toInt(); } }); - if (confirmedMweb > 0 && unconfirmedMweb > 0) { + if (/*confirmedMweb > 0 &&*/ unconfirmedMweb > 0) { unconfirmedMweb = -1 * (confirmedMweb - unconfirmedMweb); } } catch (_) {} From d3cf3728873f3eb665048e9f1f5826f7618019d6 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 19 Sep 2024 11:17:23 -0700 Subject: [PATCH 167/203] [skip ci] make failed broadcast error message less serious --- lib/view_model/send/send_view_model.dart | 3 +++ res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 3 ++- res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_hy.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 1 + res/values/strings_ko.arb | 1 + res/values/strings_my.arb | 1 + res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 1 + res/values/strings_th.arb | 1 + res/values/strings_tl.arb | 1 + res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 1 + res/values/strings_vi.arb | 1 + res/values/strings_yo.arb | 1 + res/values/strings_zh.arb | 1 + 29 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 04a914a845..2c8827c51d 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -668,6 +668,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor return S.current.tx_no_dust_exception; } if (error is TransactionCommitFailed) { + if (error.errorMessage != null && error.errorMessage!.contains("no peers replied")) { + return S.current.tx_commit_failed_no_peers; + } return "${S.current.tx_commit_failed}${error.errorMessage != null ? "\n\n${error.errorMessage}" : ""}"; } if (error is TransactionCommitFailedDustChange) { diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index d9c810c7f1..cb9e986f93 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -810,6 +810,7 @@ "trusted": "موثوق به", "tx_commit_exception_no_dust_on_change": "يتم رفض المعاملة مع هذا المبلغ. باستخدام هذه العملات المعدنية ، يمكنك إرسال ${min} دون تغيير أو ${max} الذي يعيد التغيير.", "tx_commit_failed": "فشل ارتكاب المعاملة. يرجى الاتصال بالدعم.", + "tx_commit_failed_no_peers": "فشل المعاملة في البث ، يرجى المحاولة مرة أخرى في ثانية أو نحو ذلك", "tx_invalid_input": "أنت تستخدم نوع الإدخال الخاطئ لهذا النوع من الدفع", "tx_no_dust_exception": "يتم رفض المعاملة عن طريق إرسال مبلغ صغير جدًا. يرجى محاولة زيادة المبلغ.", "tx_not_enough_inputs_exception": "لا يكفي المدخلات المتاحة. الرجاء تحديد المزيد تحت التحكم في العملة", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 955fb71cd5..670baed502 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -810,6 +810,7 @@ "trusted": "Надежден", "tx_commit_exception_no_dust_on_change": "Сделката се отхвърля с тази сума. С тези монети можете да изпратите ${min} без промяна или ${max}, която връща промяна.", "tx_commit_failed": "Компетацията на транзакцията не успя. Моля, свържете се с поддръжката.", + "tx_commit_failed_no_peers": "Сделката не успя да излъчи, моля, опитайте отново след секунда или така", "tx_invalid_input": "Използвате грешен тип вход за този тип плащане", "tx_no_dust_exception": "Сделката се отхвърля чрез изпращане на сума твърде малка. Моля, опитайте да увеличите сумата.", "tx_not_enough_inputs_exception": "Няма достатъчно налични входове. Моля, изберете повече под контрол на монети", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index b6cf8b6ec1..2f431d194b 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -810,6 +810,7 @@ "trusted": "Důvěřovat", "tx_commit_exception_no_dust_on_change": "Transakce je zamítnuta s touto částkou. S těmito mincemi můžete odeslat ${min} bez změny nebo ${max}, které se vrátí změna.", "tx_commit_failed": "Transakce COMPORT selhala. Kontaktujte prosím podporu.", + "tx_commit_failed_no_peers": "Transakce se nepodařilo vysílat, zkuste to prosím znovu za vteřinu", "tx_invalid_input": "Pro tento typ platby používáte nesprávný typ vstupu", "tx_no_dust_exception": "Transakce je zamítnuta odesláním příliš malé. Zkuste prosím zvýšit částku.", "tx_not_enough_inputs_exception": "Není k dispozici dostatek vstupů. Vyberte prosím více pod kontrolou mincí", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index bd861cc144..71cdbb64fd 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -471,8 +471,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": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.", "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_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", @@ -811,6 +811,7 @@ "trusted": "Vertrauenswürdige", "tx_commit_exception_no_dust_on_change": "Die Transaktion wird diesen Betrag abgelehnt. Mit diesen Münzen können Sie ${min} ohne Veränderung oder ${max} senden, die Änderungen zurückgeben.", "tx_commit_failed": "Transaktionsausschüsse ist fehlgeschlagen. Bitte wenden Sie sich an Support.", + "tx_commit_failed_no_peers": "Transaktion konnte nicht übertragen werden. Bitte versuchen Sie es in einer Sekunde oder so erneut", "tx_invalid_input": "Sie verwenden den falschen Eingangstyp für diese Art von Zahlung", "tx_no_dust_exception": "Die Transaktion wird abgelehnt, indem eine Menge zu klein gesendet wird. Bitte versuchen Sie, die Menge zu erhöhen.", "tx_not_enough_inputs_exception": "Nicht genügend Eingänge verfügbar. Bitte wählen Sie mehr unter Münzkontrolle aus", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 0f5a0dec1a..2af446cde6 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -811,6 +811,7 @@ "trusted": "Trusted", "tx_commit_exception_no_dust_on_change": "The transaction is rejected with this amount. With these coins you can send ${min} without change or ${max} that returns change.", "tx_commit_failed": "Transaction commit failed. Please contact support.", + "tx_commit_failed_no_peers": "Transaction failed to broadcast, please try again in a second or so", "tx_invalid_input": "You are using the wrong input type for this type of payment", "tx_no_dust_exception": "The transaction is rejected by sending an amount too small. Please try increasing the amount.", "tx_not_enough_inputs_exception": "Not enough inputs available. Please select more under Coin Control", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 3663e536fe..a6c7586d32 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -811,6 +811,7 @@ "trusted": "de confianza", "tx_commit_exception_no_dust_on_change": "La transacción se rechaza con esta cantidad. Con estas monedas puede enviar ${min} sin cambios o ${max} que devuelve el cambio.", "tx_commit_failed": "La confirmación de transacción falló. Póngase en contacto con el soporte.", + "tx_commit_failed_no_peers": "La transacción no se transmitió, intente nuevamente en un segundo más o menos", "tx_invalid_input": "Está utilizando el tipo de entrada incorrecto para este tipo de pago", "tx_no_dust_exception": "La transacción se rechaza enviando una cantidad demasiado pequeña. Intente aumentar la cantidad.", "tx_not_enough_inputs_exception": "No hay suficientes entradas disponibles. Seleccione más bajo control de monedas", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index e54eede313..0abbef5ed4 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -810,6 +810,7 @@ "trusted": "de confiance", "tx_commit_exception_no_dust_on_change": "La transaction est rejetée avec ce montant. Avec ces pièces, vous pouvez envoyer ${min} sans changement ou ${max} qui renvoie le changement.", "tx_commit_failed": "La validation de la transaction a échoué. Veuillez contacter l'assistance.", + "tx_commit_failed_no_peers": "La transaction n'a pas été diffusée, veuillez réessayer dans une seconde environ", "tx_invalid_input": "Vous utilisez le mauvais type d'entrée pour ce type de paiement", "tx_no_dust_exception": "La transaction est rejetée en envoyant un montant trop faible. Veuillez essayer d'augmenter le montant.", "tx_not_enough_inputs_exception": "Pas assez d'entrées disponibles. Veuillez sélectionner plus sous Control Control", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 0c52351626..47d0d0019f 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -812,6 +812,7 @@ "trusted": "Amintacce", "tx_commit_exception_no_dust_on_change": "An ƙi ma'amala da wannan adadin. Tare da waɗannan tsabar kudi Zaka iya aika ${min}, ba tare da canji ba ko ${max} wanda ya dawo canzawa.", "tx_commit_failed": "Ma'amala ya kasa. Da fatan za a tuntuɓi goyan baya.", + "tx_commit_failed_no_peers": "Kasuwanci ya kasa watsa, don Allah sake gwadawa a cikin na biyu ko", "tx_invalid_input": "Kuna amfani da nau'in shigar da ba daidai ba don wannan nau'in biyan kuɗi", "tx_no_dust_exception": "An ƙi ma'amala ta hanyar aika adadin ƙarami. Da fatan za a gwada ƙara adadin.", "tx_not_enough_inputs_exception": "Bai isa ba hanyoyin da ake samu. Da fatan za selectiari a karkashin Kwarewar Coin", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 60e01f2268..8f1ff971e2 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -812,6 +812,7 @@ "trusted": "भरोसा", "tx_commit_exception_no_dust_on_change": "लेनदेन को इस राशि से खारिज कर दिया जाता है। इन सिक्कों के साथ आप चेंज या ${min} के बिना ${max} को भेज सकते हैं जो परिवर्तन लौटाता है।", "tx_commit_failed": "लेन -देन प्रतिबद्ध विफल। कृपया संपर्क समर्थन करें।", + "tx_commit_failed_no_peers": "लेन -देन प्रसारित करने में विफल रहा, कृपया एक या दो सेकंड में पुनः प्रयास करें", "tx_invalid_input": "आप इस प्रकार के भुगतान के लिए गलत इनपुट प्रकार का उपयोग कर रहे हैं", "tx_no_dust_exception": "लेनदेन को बहुत छोटी राशि भेजकर अस्वीकार कर दिया जाता है। कृपया राशि बढ़ाने का प्रयास करें।", "tx_not_enough_inputs_exception": "पर्याप्त इनपुट उपलब्ध नहीं है। कृपया सिक्का नियंत्रण के तहत अधिक चुनें", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 1a46552d36..44a2bb9dd2 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -810,6 +810,7 @@ "trusted": "vjerovao", "tx_commit_exception_no_dust_on_change": "Transakcija se odbija s tim iznosom. Pomoću ovih kovanica možete poslati ${min} bez promjene ili ${max} koja vraća promjenu.", "tx_commit_failed": "Obveza transakcije nije uspjela. Molimo kontaktirajte podršku.", + "tx_commit_failed_no_peers": "Transakcija nije uspjela emitirati, pokušajte ponovo u sekundi ili tako", "tx_invalid_input": "Koristite pogrešnu vrstu ulaza za ovu vrstu plaćanja", "tx_no_dust_exception": "Transakcija se odbija slanjem iznosa premalo. Pokušajte povećati iznos.", "tx_not_enough_inputs_exception": "Nema dovoljno unosa. Molimo odaberite više pod kontrolom novčića", diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index f4b60272b0..f933cddf0f 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -802,6 +802,7 @@ "trusted": "Վստահելի", "tx_commit_exception_no_dust_on_change": "Փոխանցումը մերժվել է այս գումարով: Այս արժույթներով կարող եք ուղարկել ${min} առանց փոփոխության կամ ${max} որը վերադարձնում է փոփոխությունը", "tx_commit_failed": "Փոխանցումը ձախողվել է: Խնդրում ենք դիմել աջակցությանը", + "tx_commit_failed_no_peers": "Գործարքը չի հաջողվել հեռարձակել, խնդրում ենք կրկին փորձել մեկ վայրկյանում", "tx_invalid_input": "Դուք օգտագործում եք սխալ մուտքային տիպ այս տեսակի վճարման համար", "tx_no_dust_exception": "Փոխանցումը մերժվել է շատ փոքր գումարով: Խնդրում ենք փորձել ավելացնել գումարը", "tx_not_enough_inputs_exception": "Չկան բավարար մուտքեր: Խնդրում ենք ընտրել ավելին Coin Control֊ում", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 2b7eed1df5..c569057101 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -813,6 +813,7 @@ "trusted": "Dipercayai", "tx_commit_exception_no_dust_on_change": "Transaksi ditolak dengan jumlah ini. Dengan koin ini Anda dapat mengirim ${min} tanpa perubahan atau ${max} yang mengembalikan perubahan.", "tx_commit_failed": "Transaksi Gagal. Silakan hubungi Dukungan.", + "tx_commit_failed_no_peers": "Transaksi gagal untuk disiarkan, silakan coba lagi sebentar lagi", "tx_invalid_input": "Anda menggunakan jenis input yang salah untuk jenis pembayaran ini", "tx_no_dust_exception": "Transaksi ditolak dengan mengirimkan jumlah yang terlalu kecil. Silakan coba tingkatkan jumlahnya.", "tx_not_enough_inputs_exception": "Tidak cukup input yang tersedia. Pilih lebih banyak lagi di bawah Kontrol Koin", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 1638959733..0af8257591 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -812,6 +812,7 @@ "trusted": "di fiducia", "tx_commit_exception_no_dust_on_change": "La transazione viene respinta con questo importo. Con queste monete è possibile inviare ${min} senza modifiche o ${max} che restituisce il cambiamento.", "tx_commit_failed": "Commit di transazione non riuscita. Si prega di contattare il supporto.", + "tx_commit_failed_no_peers": "La transazione non è riuscita a trasmettere, riprovare in un secondo o giù di lì", "tx_invalid_input": "Stai usando il tipo di input sbagliato per questo tipo di pagamento", "tx_no_dust_exception": "La transazione viene respinta inviando un importo troppo piccolo. Per favore, prova ad aumentare l'importo.", "tx_not_enough_inputs_exception": "Input non sufficienti disponibili. Seleziona di più sotto il controllo delle monete", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 9d4ce29a27..d7f52a8569 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -811,6 +811,7 @@ "trusted": "信頼できる", "tx_commit_exception_no_dust_on_change": "この金額ではトランザクションは拒否されます。 これらのコインを使用すると、おつりなしの ${min} またはおつりを返す ${max} を送信できます。", "tx_commit_failed": "トランザクションコミットは失敗しました。サポートに連絡してください。", + "tx_commit_failed_no_peers": "トランザクションはブロードキャストに失敗しました。一瞬かそこらで再試行してください", "tx_invalid_input": "このタイプの支払いに間違った入力タイプを使用しています", "tx_no_dust_exception": "トランザクションは、小さすぎる金額を送信することにより拒否されます。量を増やしてみてください。", "tx_not_enough_inputs_exception": "利用可能な入力が十分ではありません。コイン制御下でもっと選択してください", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 1d605e160c..dcceb0e67e 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -811,6 +811,7 @@ "trusted": "신뢰할 수 있는", "tx_commit_exception_no_dust_on_change": "이 금액으로 거래가 거부되었습니다. 이 코인을 사용하면 거스름돈 없이 ${min}를 보내거나 거스름돈을 반환하는 ${max}를 보낼 수 있습니다.", "tx_commit_failed": "거래 커밋이 실패했습니다. 지원에 연락하십시오.", + "tx_commit_failed_no_peers": "트랜잭션이 방송에 실패했는데 1 초 정도 후에 다시 시도하십시오.", "tx_invalid_input": "이 유형의 지불에 잘못 입력 유형을 사용하고 있습니다.", "tx_no_dust_exception": "너무 작은 금액을 보내면 거래가 거부됩니다. 금액을 늘리십시오.", "tx_not_enough_inputs_exception": "사용 가능한 입력이 충분하지 않습니다. 코인 컨트롤에서 더 많은 것을 선택하십시오", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index a7db770c93..57391d8ab2 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -810,6 +810,7 @@ "trusted": "ယုံတယ်။", "tx_commit_exception_no_dust_on_change": "အဆိုပါငွေပေးငွေယူကဒီပမာဏနှင့်အတူပယ်ချခံရသည်။ ဤဒင်္ဂါးပြားများနှင့်အတူပြောင်းလဲမှုကိုပြန်လည်ပြောင်းလဲခြင်းသို့မဟုတ် ${min} မပါဘဲ ${max} ပေးပို့နိုင်သည်။", "tx_commit_failed": "ငွေပေးငွေယူကျူးလွန်မှုပျက်ကွက်။ ကျေးဇူးပြုပြီးပံ့ပိုးမှုဆက်သွယ်ပါ။", + "tx_commit_failed_no_peers": "ငွေပေးငွေယူထုတ်လွှင့်ရန်ပျက်ကွက်ပါက ကျေးဇူးပြု. ဒုတိယသို့မဟုတ်ထိုအတိုင်းထပ်မံကြိုးစားပါ", "tx_invalid_input": "သင်သည်ဤငွေပေးချေမှုအမျိုးအစားအတွက်မှားယွင်းသော input type ကိုအသုံးပြုနေသည်", "tx_no_dust_exception": "ငွေပမာဏကိုသေးငယ်လွန်းသောငွေပမာဏကိုပေးပို့ခြင်းဖြင့်ပယ်ဖျက်ခြင်းကိုငြင်းပယ်သည်။ ကျေးဇူးပြုပြီးငွေပမာဏကိုတိုးမြှင့်ကြိုးစားပါ။", "tx_not_enough_inputs_exception": "အလုံအလောက်သွင်းအားစုများမလုံလောက်။ ကျေးဇူးပြုပြီးဒင်္ဂါးပြားထိန်းချုပ်မှုအောက်တွင်ပိုမိုရွေးချယ်ပါ", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index a1749f89ba..877250861f 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -810,6 +810,7 @@ "trusted": "vertrouwd", "tx_commit_exception_no_dust_on_change": "De transactie wordt afgewezen met dit bedrag. Met deze munten kunt u ${min} verzenden zonder verandering of ${max} die wijziging retourneert.", "tx_commit_failed": "Transactiebewissing is mislukt. Neem contact op met de ondersteuning.", + "tx_commit_failed_no_peers": "De transactie is niet uitgezonden, probeer het opnieuw binnen een seconde of zo", "tx_invalid_input": "U gebruikt het verkeerde invoertype voor dit type betaling", "tx_no_dust_exception": "De transactie wordt afgewezen door een te klein bedrag te verzenden. Probeer het bedrag te verhogen.", "tx_not_enough_inputs_exception": "Niet genoeg ingangen beschikbaar. Selecteer meer onder muntenbesturing", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 553a4c9728..10b0b04753 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -810,6 +810,7 @@ "trusted": "Zaufany", "tx_commit_exception_no_dust_on_change": "Transakcja jest odrzucana z tą kwotą. Za pomocą tych monet możesz wysłać ${min} bez zmiany lub ${max}, które zwraca zmianę.", "tx_commit_failed": "Zatwierdzenie transakcji nie powiodło się. Skontaktuj się z obsługą.", + "tx_commit_failed_no_peers": "Transakcja nie była transmitowana, spróbuj ponownie za około sekundę", "tx_invalid_input": "Używasz niewłaściwego typu wejściowego dla tego rodzaju płatności", "tx_no_dust_exception": "Transakcja jest odrzucana przez wysyłanie zbyt małej ilości. Spróbuj zwiększyć kwotę.", "tx_not_enough_inputs_exception": "Za mało dostępnych danych wejściowych. Wybierz więcej pod kontrolą monet", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 0b772fa7e5..71182438d5 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -812,6 +812,7 @@ "trusted": "confiável", "tx_commit_exception_no_dust_on_change": "A transação é rejeitada com esse valor. Com essas moedas, você pode enviar ${min} sem alteração ou ${max} que retorna alterações.", "tx_commit_failed": "A confirmação da transação falhou. Entre em contato com o suporte.", + "tx_commit_failed_no_peers": "A transação não foi transmitida, tente novamente em um segundo", "tx_invalid_input": "Você está usando o tipo de entrada errado para este tipo de pagamento", "tx_no_dust_exception": "A transação é rejeitada enviando uma quantia pequena demais. Por favor, tente aumentar o valor.", "tx_not_enough_inputs_exception": "Não há entradas disponíveis. Selecione mais sob controle de moedas", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 171dfde242..4f8bf0d408 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -811,6 +811,7 @@ "trusted": "доверенный", "tx_commit_exception_no_dust_on_change": "Транзакция отклоняется с этой суммой. С этими монетами вы можете отправлять ${min} без изменения или ${max}, которые возвращают изменение.", "tx_commit_failed": "Комплект транзакции не удался. Пожалуйста, свяжитесь с поддержкой.", + "tx_commit_failed_no_peers": "Транзакция не смогла передать, попробуйте еще раз через секунду или около того", "tx_invalid_input": "Вы используете неправильный тип ввода для этого типа оплаты", "tx_no_dust_exception": "Транзакция отклоняется путем отправки слишком маленькой суммы. Пожалуйста, попробуйте увеличить сумму.", "tx_not_enough_inputs_exception": "Недостаточно входов доступны. Пожалуйста, выберите больше под контролем монет", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 494320ce5d..ab94b443ea 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -810,6 +810,7 @@ "trusted": "มั่นคง", "tx_commit_exception_no_dust_on_change": "ธุรกรรมถูกปฏิเสธด้วยจำนวนเงินนี้ ด้วยเหรียญเหล่านี้คุณสามารถส่ง ${min} โดยไม่ต้องเปลี่ยนแปลงหรือ ${max} ที่ส่งคืนการเปลี่ยนแปลง", "tx_commit_failed": "การทำธุรกรรมล้มเหลว กรุณาติดต่อฝ่ายสนับสนุน", + "tx_commit_failed_no_peers": "การทำธุรกรรมล้มเหลวในการออกอากาศโปรดลองอีกครั้งในวินาทีหรือมากกว่านั้น", "tx_invalid_input": "คุณกำลังใช้ประเภทอินพุตที่ไม่ถูกต้องสำหรับการชำระเงินประเภทนี้", "tx_no_dust_exception": "การทำธุรกรรมถูกปฏิเสธโดยการส่งจำนวนน้อยเกินไป โปรดลองเพิ่มจำนวนเงิน", "tx_not_enough_inputs_exception": "มีอินพุตไม่เพียงพอ โปรดเลือกเพิ่มเติมภายใต้การควบคุมเหรียญ", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 3f6bf5e410..8fa6bab3d0 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -810,6 +810,7 @@ "trusted": "Pinagkakatiwalaan", "tx_commit_exception_no_dust_on_change": "Ang transaksyon ay tinanggihan sa halagang ito. Sa mga barya na ito maaari kang magpadala ng ${min} nang walang sukli o ${max} na nagbabalik ng sukli.", "tx_commit_failed": "Nabigo ang transaksyon. Mangyaring makipag-ugnay sa suporta.", + "tx_commit_failed_no_peers": "Nabigo ang transaksyon na mag -broadcast, mangyaring subukang muli sa isang segundo o higit pa", "tx_invalid_input": "Gumagamit ka ng maling uri ng pag-input para sa ganitong uri ng pagbabayad", "tx_no_dust_exception": "Ang transaksyon ay tinanggihan sa pamamagitan ng pagpapadala ng isang maliit na halaga. Mangyaring subukang dagdagan ang halaga.", "tx_not_enough_inputs_exception": "Hindi sapat na magagamit ang mga input. Mangyaring pumili ng higit pa sa ilalim ng Coin Control", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index e4194e62e0..f65eeb37ff 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -810,6 +810,7 @@ "trusted": "Güvenilir", "tx_commit_exception_no_dust_on_change": "İşlem bu miktarla reddedilir. Bu madeni paralarla değişiklik yapmadan ${min} veya değişikliği döndüren ${max} gönderebilirsiniz.", "tx_commit_failed": "İşlem taahhüdü başarısız oldu. Lütfen Destek ile iletişime geçin.", + "tx_commit_failed_no_peers": "İşlem yayın yapamadı, lütfen bir saniye içinde tekrar deneyin", "tx_invalid_input": "Bu tür ödeme için yanlış giriş türünü kullanıyorsunuz", "tx_no_dust_exception": "İşlem, çok küçük bir miktar gönderilerek reddedilir. Lütfen miktarı artırmayı deneyin.", "tx_not_enough_inputs_exception": "Yeterli giriş yok. Lütfen madeni para kontrolü altında daha fazlasını seçin", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 8f7387a1f7..ae2e817f5b 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -811,6 +811,7 @@ "trusted": "довіряють", "tx_commit_exception_no_dust_on_change": "Транзакція відхилена цією сумою. За допомогою цих монет ви можете надіслати ${min} без змін або ${max}, що повертає зміни.", "tx_commit_failed": "Транзакційна комісія не вдалося. Будь ласка, зв'яжіться з підтримкою.", + "tx_commit_failed_no_peers": "Транзакція не вдалося транслювати, спробуйте ще раз за секунду або близько того", "tx_invalid_input": "Ви використовуєте неправильний тип введення для цього типу оплати", "tx_no_dust_exception": "Угода відхиляється, відправивши суму занадто мала. Будь ласка, спробуйте збільшити суму.", "tx_not_enough_inputs_exception": "Недостатньо доступних входів. Виберіть більше під контролем монети", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index af021737d6..1d369ebdcf 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -812,6 +812,7 @@ "trusted": "قابل اعتماد", "tx_commit_exception_no_dust_on_change": "اس رقم سے لین دین کو مسترد کردیا گیا ہے۔ ان سککوں کے ذریعہ آپ بغیر کسی تبدیلی کے ${min} یا ${max} بھیج سکتے ہیں جو لوٹتے ہیں۔", "tx_commit_failed": "ٹرانزیکشن کمٹ ناکام ہوگیا۔ براہ کرم سپورٹ سے رابطہ کریں۔", + "tx_commit_failed_no_peers": "ٹرانزیکشن نشر کرنے میں ناکام ، براہ کرم ایک سیکنڈ یا اس میں دوبارہ کوشش کریں", "tx_invalid_input": "آپ اس قسم کی ادائیگی کے لئے غلط ان پٹ کی قسم استعمال کررہے ہیں", "tx_no_dust_exception": "لین دین کو بہت چھوٹی رقم بھیج کر مسترد کردیا جاتا ہے۔ براہ کرم رقم میں اضافہ کرنے کی کوشش کریں۔", "tx_not_enough_inputs_exception": "کافی ان پٹ دستیاب نہیں ہے۔ براہ کرم سکے کے کنٹرول میں مزید منتخب کریں", diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb index e0f4a6664c..155269a75f 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -801,6 +801,7 @@ "trusted": "Đã tin cậy", "tx_commit_exception_no_dust_on_change": "Giao dịch bị từ chối với số tiền này. Với số tiền này bạn có thể gửi ${min} mà không cần đổi tiền lẻ hoặc ${max} trả lại tiền lẻ.", "tx_commit_failed": "Giao dịch không thành công. Vui lòng liên hệ với hỗ trợ.", + "tx_commit_failed_no_peers": "Giao dịch không phát sóng, vui lòng thử lại trong một giây hoặc lâu hơn", "tx_invalid_input": "Bạn đang sử dụng loại đầu vào sai cho loại thanh toán này", "tx_no_dust_exception": "Giao dịch bị từ chối vì gửi một số tiền quá nhỏ. Vui lòng thử tăng số tiền.", "tx_not_enough_inputs_exception": "Không đủ đầu vào có sẵn. Vui lòng chọn thêm dưới Coin Control", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 96307465fd..f6103c51c4 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -811,6 +811,7 @@ "trusted": "A ti fọkàn ẹ̀ tán", "tx_commit_exception_no_dust_on_change": "Iṣowo naa ti kọ pẹlu iye yii. Pẹlu awọn owó wọnyi o le firanṣẹ ${min} laisi ayipada tabi ${max} ni iyipada iyipada.", "tx_commit_failed": "Idunadura iṣowo kuna. Jọwọ kan si atilẹyin.", + "tx_commit_failed_no_peers": "Idunadura kuna lati wa igbohungbe, jọwọ gbiyanju lẹẹkansi ni iṣẹju keji tabi bẹẹ", "tx_invalid_input": "O nlo iru titẹ nkan ti ko tọ fun iru isanwo yii", "tx_no_dust_exception": "Iṣowo naa ni kọ nipa fifiranṣẹ iye ti o kere ju. Jọwọ gbiyanju pọ si iye naa.", "tx_not_enough_inputs_exception": "Ko to awọn titẹsi to. Jọwọ yan diẹ sii labẹ iṣakoso owo", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index fa74a52ec6..ebf446aee8 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -810,6 +810,7 @@ "trusted": "值得信赖", "tx_commit_exception_no_dust_on_change": "交易被此金额拒绝。使用这些硬币,您可以发送${min}无需更改或返回${max}的变化。", "tx_commit_failed": "交易承诺失败。请联系支持。", + "tx_commit_failed_no_peers": "交易无法广播,请在一秒钟左右的时间内重试", "tx_invalid_input": "您正在使用错误的输入类型进行此类付款", "tx_no_dust_exception": "通过发送太小的金额来拒绝交易。请尝试增加金额。", "tx_not_enough_inputs_exception": "没有足够的输入。请在硬币控制下选择更多", From c49f2ed5f136a17c14085a28831cf97d379234ed Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 19 Sep 2024 12:08:21 -0700 Subject: [PATCH 168/203] minor --- cw_mweb/lib/cw_mweb.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index ab7dfda10b..096d6dd9a6 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'dart:typed_data'; import 'package:grpc/grpc.dart'; @@ -43,7 +42,7 @@ class CwMweb { } catch (e) { print("Attempt $i failed: $e"); await stop(); // call stop so we create a new instance before retrying - await Future.delayed(const Duration(seconds: 2)); // wait before retrying + await Future.delayed(const Duration(seconds: 4)); // wait before retrying } } throw Exception("Failed to connect after $maxRetries attempts"); From 8d4443e5040a1b4dc23c48a3214e4ff0ab0c6bd0 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 19 Sep 2024 15:35:47 -0700 Subject: [PATCH 169/203] capture all grpc errors and much better error handling overall --- cw_bitcoin/lib/litecoin_wallet.dart | 40 ++++++++------ cw_mweb/lib/cw_mweb.dart | 83 ++++++++++++++++++++++++++--- 2 files changed, 100 insertions(+), 23 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 3f82cb54ba..73b00b24e4 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -34,6 +34,7 @@ import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_keys_file.dart'; import 'package:flutter/foundation.dart'; +import 'package:grpc/grpc.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/wallet_type.dart'; @@ -264,11 +265,17 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } await waitForMwebAddresses(); - await getStub(); - await processMwebUtxos(); - await updateTransactions(); - await updateUnspent(); - await updateBalance(); + try { + await getStub(); + await processMwebUtxos(); + await updateTransactions(); + await updateUnspent(); + await updateBalance(); + } catch (e) { + print("failed to start mweb sync: $e"); + syncStatus = FailedSyncStatus(); + return; + } _syncTimer?.cancel(); _syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async { @@ -276,7 +283,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final nodeHeight = await electrumClient.getCurrentBlockChainTip() ?? 0; // current block height of our node - final resp = await _stub.status(StatusRequest()); + final resp = await CwMweb.status(StatusRequest()); if (resp.blockHeaderHeight < nodeHeight) { int h = resp.blockHeaderHeight; @@ -487,7 +494,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // process new utxos as they come in: _utxoStream?.cancel(); - _utxoStream = _stub.utxos(req).listen((Utxo sUtxo) async { + ResponseStream? responseStream = await CwMweb.utxos(req); + if (responseStream == null) { + throw Exception("failed to get utxos stream!"); + } + _utxoStream = responseStream.listen((Utxo sUtxo) async { final utxo = MwebUtxo( address: sUtxo.address, blockTime: sUtxo.blockTime, @@ -530,10 +541,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final outputIds = mwebUtxosBox.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId).toList(); - final resp = await _stub.spent(SpentRequest(outputId: outputIds)); + final resp = await CwMweb.spent(SpentRequest(outputId: outputIds)); final spent = resp.outputId; if (spent.isEmpty) return; - final status = await _stub.status(StatusRequest()); + final status = await CwMweb.status(StatusRequest()); final height = await electrumClient.getCurrentBlockChainTip(); if (height == null || status.blockHeaderHeight != height) return; if (status.mwebUtxosHeight != height) return; @@ -599,9 +610,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (outputId.isEmpty) { return false; } - final resp = await _stub.spent(SpentRequest(outputId: outputId)); + final resp = await CwMweb.spent(SpentRequest(outputId: outputId)); if (!setEquals(resp.outputId.toSet(), target)) return false; - final status = await _stub.status(StatusRequest()); + final status = await CwMweb.status(StatusRequest()); if (!tx.isPending) return false; tx.height = status.mwebUtxosHeight; tx.confirmations = 1; @@ -799,7 +810,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 resp = await _stub.create(CreateRequest( + final resp = await CwMweb.create(CreateRequest( rawTx: txb.buildTransaction((a, b, c, d) => '').toBytes(), scanSecret: scanSecret, spendSecret: spendSecret, @@ -841,7 +852,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { await waitForMwebAddresses(); await getStub(); - final resp = await _stub.create(CreateRequest( + final resp = await CwMweb.create(CreateRequest( rawTx: hex.decode(tx.hex), scanSecret: scanSecret, spendSecret: spendSecret, @@ -927,8 +938,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } Future getStatusRequest() async { - await getStub(); - final resp = await _stub.status(StatusRequest()); + final resp = await CwMweb.status(StatusRequest()); return resp; } diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index 096d6dd9a6..7953292ece 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -9,8 +9,13 @@ class CwMweb { static RpcClient? _rpcClient; static ClientChannel? _clientChannel; static int? _port; + static const TIMEOUT_DURATION = Duration(seconds: 5); static Future _initializeClient() async { + await stop(); + // wait a few seconds to make sure the server is stopped + await Future.delayed(const Duration(seconds: 5)); + final appDir = await getApplicationSupportDirectory(); _port = await CwMwebPlatform.instance.start(appDir.path); if (_port == null || _port == 0) { @@ -18,8 +23,12 @@ class CwMweb { } print("Attempting to connect to server on port: $_port"); - _clientChannel = ClientChannel('127.0.0.1', - port: _port!, + // wait for the server to finish starting up before we try to connect to it: + await Future.delayed(const Duration(seconds: 5)); + + _clientChannel = ClientChannel('127.0.0.1', port: _port!, channelShutdownHandler: () { + print("Channel shutdown!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + }, options: const ChannelOptions( credentials: ChannelCredentials.insecure(), keepAlive: ClientKeepAliveOptions(permitWithoutCalls: true), @@ -34,27 +43,35 @@ class CwMweb { await _initializeClient(); } final status = await _rpcClient! - .status(StatusRequest(), options: CallOptions(timeout: const Duration(seconds: 3))); + .status(StatusRequest(), options: CallOptions(timeout: TIMEOUT_DURATION)); if (status.blockTime == 0) { throw Exception("blockTime shouldn't be 0! (this connection is likely broken)"); } return _rpcClient!; } catch (e) { print("Attempt $i failed: $e"); - await stop(); // call stop so we create a new instance before retrying - await Future.delayed(const Duration(seconds: 4)); // wait before retrying + _rpcClient = null; } } throw Exception("Failed to connect after $maxRetries attempts"); } static Future stop() async { - await CwMwebPlatform.instance.stop(); - await cleanup(); + try { + await CwMwebPlatform.instance.stop(); + await cleanup(); + } catch (e) { + print("Error stopping server: $e"); + } } static Future address(Uint8List scanSecret, Uint8List spendPub, int index) async { - return CwMwebPlatform.instance.address(scanSecret, spendPub, index); + try { + return CwMwebPlatform.instance.address(scanSecret, spendPub, index); + } catch (e) { + print("Error getting address: $e"); + return null; + } } static Future cleanup() async { @@ -63,4 +80,54 @@ class CwMweb { _clientChannel = null; _port = null; } + + // wrappers that handle the connection issues: + static Future spent(SpentRequest request) async { + try { + if (_rpcClient == null) { + await _initializeClient(); + } + return await _rpcClient!.spent(request, options: CallOptions(timeout: TIMEOUT_DURATION)); + } catch (e) { + print("Error getting spent: $e"); + return SpentResponse(); + } + } + + static Future status(StatusRequest request) async { + try { + if (_rpcClient == null) { + await _initializeClient(); + } + return await _rpcClient!.status(request, options: CallOptions(timeout: TIMEOUT_DURATION)); + } catch (e) { + print("Error getting status: $e"); + return StatusResponse(); + } + } + + static Future create(CreateRequest request) async { + try { + if (_rpcClient == null) { + await _initializeClient(); + } + return await _rpcClient!.create(request, options: CallOptions(timeout: TIMEOUT_DURATION)); + } catch (e) { + print("Error getting create: $e"); + return CreateResponse(); + } + } + + static Future?> utxos(UtxosRequest request) async { + try { + if (_rpcClient == null) { + await _initializeClient(); + } + // this is a stream, so we should have an effectively infinite timeout: + return _rpcClient!.utxos(request, options: CallOptions(timeout: const Duration(days: 99))); + } catch (e) { + print("Error getting utxos: $e"); + return null; + } + } } From 0cd21f2fb611c95cef8df5fb7b1cc52471a2ae0b Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 19 Sep 2024 15:37:31 -0700 Subject: [PATCH 170/203] [skip ci] minor --- cw_mweb/lib/cw_mweb.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index 7953292ece..293210c2bc 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -124,7 +124,7 @@ class CwMweb { await _initializeClient(); } // this is a stream, so we should have an effectively infinite timeout: - return _rpcClient!.utxos(request, options: CallOptions(timeout: const Duration(days: 99))); + return _rpcClient!.utxos(request, options: CallOptions(timeout: const Duration(days: 1000 * 365))); } catch (e) { print("Error getting utxos: $e"); return null; From 7ee8949a7180e4d431c9899d6770f871b92260d5 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 19 Sep 2024 17:15:37 -0700 Subject: [PATCH 171/203] prevent transactions with < 6 confirmations from being used + hide mweb balances if mweb is off --- cw_bitcoin/lib/litecoin_wallet.dart | 30 +++++++++++++++++++ .../dashboard/balance_view_model.dart | 4 +-- .../dashboard/dashboard_view_model.dart | 5 ++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 73b00b24e4..8df7f62cac 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -859,6 +859,36 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000, )); final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); + + // check if any of the inputs of this transaction are hog-ex: + tx2.inputs.forEach((txInput) { + bool isHogEx = true; + + final utxo = unspentCoins + .firstWhere((utxo) => utxo.hash == txInput.txId && utxo.vout == txInput.txIndex); + + if (txInput.sequence.isEmpty) { + isHogEx = false; + } + + // TODO: detect actual hog-ex inputs + // print(txInput.sequence); + // print(txInput.txIndex); + // print(utxo.value); + + if (!isHogEx) { + return; + } + + int confirmations = utxo.confirmations ?? 0; + if (confirmations < 6) { + throw Exception( + "A transaction input has less than 6 confirmations, please try again later."); + } + }); + + throw Exception("Not finished!"); + tx.hexOverride = tx2 .copyWith( witnesses: tx2.inputs.asMap().entries.map((e) { diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 2d917c8201..255bcc511e 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -369,14 +369,14 @@ abstract class BalanceViewModelBase with Store { } bool _hasSecondAdditionalBalanceForWalletType(WalletType type) { - if (wallet.type == WalletType.litecoin /*&& settingsStore.mwebEnabled*/) { + if (wallet.type == WalletType.litecoin && settingsStore.mwebEnabled) { return true; } return false; } bool _hasSecondAvailableBalanceForWalletType(WalletType type) { - if (wallet.type == WalletType.litecoin /*&& settingsStore.mwebEnabled*/) { + if (wallet.type == WalletType.litecoin && settingsStore.mwebEnabled) { return true; } return false; diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 6e07afb3a0..a45bd3fe26 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -132,8 +132,8 @@ abstract class DashboardViewModelBase with Store { FilterItem( value: () => tradeFilterStore.displayLetsExchange, caption: ExchangeProviderDescription.letsExchange.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.letsExchange)), + onChanged: () => tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.letsExchange)), FilterItem( value: () => tradeFilterStore.displayStealthEx, caption: ExchangeProviderDescription.stealthEx.title, @@ -443,6 +443,7 @@ abstract class DashboardViewModelBase with Store { settingsStore.hasEnabledMwebBefore = true; } + settingsStore.mwebEnabled = active; mwebScanningActive = active; bitcoin!.setMwebEnabled(wallet, active); } From 03a3fb8d744b3e3ee04523072138e47032f9091b Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Thu, 19 Sep 2024 17:16:17 -0700 Subject: [PATCH 172/203] fix --- cw_bitcoin/lib/litecoin_wallet.dart | 2 -- cw_mweb/ios/Classes/CwMwebPlugin.swift | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 8df7f62cac..c12adb560f 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -887,8 +887,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } }); - throw Exception("Not finished!"); - tx.hexOverride = tx2 .copyWith( witnesses: tx2.inputs.asMap().entries.map((e) { diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index f1fd78cd89..1face6f148 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -13,20 +13,20 @@ public static func register(with registrar: FlutterPluginRegistrar) { private static var port: Int = 0 private static var dataDir: String? - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) async { switch call.method { case "getPlatformVersion": result("iOS " + UIDevice.current.systemVersion) break case "start": - stopServer() + await stopServer() let args = call.arguments as? [String: String] let dataDir = args?["dataDir"] CwMwebPlugin.dataDir = dataDir - startServer(result: result) + await startServer(result: result) break case "stop": - stopServer() + await stopServer() result(nil) break case "address": From f2ddbcf740e93937d748f0d239c261d16c8b33fe Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 20 Sep 2024 11:56:04 -0700 Subject: [PATCH 173/203] merge fixes pt.1 [skip ci] --- cw_bitcoin/lib/electrum_wallet.dart | 15 +++------------ cw_bitcoin/lib/litecoin_wallet.dart | 3 ++- cw_bitcoin/lib/pending_bitcoin_transaction.dart | 4 ++-- cw_bitcoin/pubspec.yaml | 2 +- cw_bitcoin_cash/pubspec.yaml | 2 +- cw_mweb/ios/Classes/CwMwebPlugin.swift | 8 ++++---- lib/bitcoin/cw_bitcoin.dart | 9 ++------- pubspec_base.yaml | 2 +- 8 files changed, 16 insertions(+), 29 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 7f02fbb267..5664c54294 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -170,25 +170,16 @@ abstract class ElectrumWalletBase Set get addressesSet => walletAddresses.allAddresses.map((addr) => addr.address).toSet(); List get scriptHashes => walletAddresses.addressesByReceiveType + .where((addr) => RegexUtils.addressTypeFromStr(addr.address, network) is! MwebAddress) .map((addr) => (addr as BitcoinAddressRecord).getScriptHash(network)) .toList(); List get publicScriptHashes => walletAddresses.allAddresses .where((addr) => !addr.isHidden) + .where((addr) => RegexUtils.addressTypeFromStr(addr.address, network) is! MwebAddress) .map((addr) => addr.getScriptHash(network)) .toList(); - // TODO: remove this - // 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(); - String get xpub => accountHD.publicKey.toExtended; @override @@ -1908,7 +1899,7 @@ abstract class ElectrumWalletBase Future fetchBalances() async { final addresses = walletAddresses.allAddresses - .where((address) => addressTypeFromStr(address.address, network) is! MwebAddress) + .where((address) => RegexUtils.addressTypeFromStr(address.address, network) is! MwebAddress) .toList(); final balanceFutures = >>[]; for (var i = 0; i < addresses.length; i++) { diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 89a790d347..0169358862 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -906,7 +906,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]); }).toList()) .toHex(); - tx.outputs = resp.outputId; + tx.outputAddresses = resp.outputId; + return tx ..addListener((transaction) async { final addresses = {}; diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index 57f3e0e76d..7f109647a8 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -39,7 +39,7 @@ class PendingBitcoinTransaction with PendingTransaction { bool isMweb; String? idOverride; String? hexOverride; - List? outputs; + List? outputAddresses; @override String get id => idOverride ?? _tx.txId(); @@ -139,6 +139,6 @@ class PendingBitcoinTransaction with PendingTransaction { isPending: true, confirmations: 0, inputAddresses: _tx.inputs.map((input) => input.txId).toList(), - outputAddresses: outputs, + outputAddresses: outputAddresses, fee: fee); } diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 06ecdf1d5e..7e33d82601 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -63,7 +63,7 @@ dependency_overrides: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base - ref: cake-update-v7 + ref: cake-update-v8 pointycastle: 3.7.4 ffi: 2.1.0 diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index a3b113d9f4..cd1e52f510 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -42,7 +42,7 @@ dependency_overrides: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base - ref: cake-update-v7 + ref: cake-update-v8 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index 1face6f148..f1fd78cd89 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -13,20 +13,20 @@ public static func register(with registrar: FlutterPluginRegistrar) { private static var port: Int = 0 private static var dataDir: String? - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) async { + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "getPlatformVersion": result("iOS " + UIDevice.current.systemVersion) break case "start": - await stopServer() + stopServer() let args = call.arguments as? [String: String] let dataDir = args?["dataDir"] CwMwebPlugin.dataDir = dataDir - await startServer(result: result) + startServer(result: result) break case "stop": - await stopServer() + stopServer() result(nil) break case "address": diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 65e0e3a424..9a67d9cd00 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -614,12 +614,7 @@ class CWBitcoin extends Bitcoin { bool txIsMweb(TransactionInfo txInfo) { final tx = txInfo as ElectrumTransactionInfo; - List addresses = - (transaction.inputAddresses ?? []) + (transaction.outputAddresses ?? []); - for (var address in addresses) { - if (address.toLowerCase().contains("mweb")) { - return true; - } - } + // return tx.isMweb; + return false; } } diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 6f1b595cd7..27a01dda61 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -135,7 +135,7 @@ dependency_overrides: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base - ref: cake-update-v7 + ref: cake-update-v8 ffi: 2.1.0 flutter_icons: From 6bcda3b5cfcbb1bb85fb82ab5df347b22897b57e Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Fri, 20 Sep 2024 14:15:38 -0700 Subject: [PATCH 174/203] fix mweb tags --- lib/bitcoin/cw_bitcoin.dart | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 9a67d9cd00..7d4e1bcbd0 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -614,7 +614,26 @@ class CWBitcoin extends Bitcoin { bool txIsMweb(TransactionInfo txInfo) { final tx = txInfo as ElectrumTransactionInfo; - // return tx.isMweb; - return false; + List inputAddresses = tx.inputAddresses ?? []; + List outputAddresses = tx.outputAddresses ?? []; + bool inputAddressesContainMweb = false; + bool outputAddressesContainMweb = false; + + for (var address in inputAddresses) { + if (address.toLowerCase().contains('mweb')) { + inputAddressesContainMweb = true; + break; + } + } + + for (var address in outputAddresses) { + if (address.toLowerCase().contains('mweb')) { + outputAddressesContainMweb = true; + break; + } + } + + // TODO: this could be improved: + return inputAddressesContainMweb || outputAddressesContainMweb; } } From fb6cea2df7ab631d2df6aec480fa36329a9450b9 Mon Sep 17 00:00:00 2001 From: fossephate Date: Sun, 22 Sep 2024 22:29:37 -0700 Subject: [PATCH 175/203] fix --- cw_bitcoin/lib/litecoin_wallet.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 0169358862..6aee77ec9a 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -438,6 +438,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { confirmations: confirmations, inputAddresses: [], outputAddresses: [utxo.outputId], + isReplaced: false, ); } @@ -584,6 +585,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { confirmations: 1, inputAddresses: inputAddresses.toList(), outputAddresses: [], + isReplaced: false, ); transactionHistory.addOne(tx); From b3e346ddfd1ea40639ce7b4caa639a13f2a64181 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 23 Sep 2024 09:55:27 -0700 Subject: [PATCH 176/203] [skip ci] fix tag spacing --- lib/src/screens/dashboard/widgets/transaction_raw.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/screens/dashboard/widgets/transaction_raw.dart b/lib/src/screens/dashboard/widgets/transaction_raw.dart index 89be273ffe..b18131f3d0 100644 --- a/lib/src/screens/dashboard/widgets/transaction_raw.dart +++ b/lib/src/screens/dashboard/widgets/transaction_raw.dart @@ -60,7 +60,7 @@ class TransactionRow extends StatelessWidget { fontWeight: FontWeight.w500, color: Theme.of(context).extension()!.textColor, )), - ...tags.map((tag) => TxTag(tag: tag)).toList(), + ...tags.map((tag) => Row(children: [SizedBox(width: 8), TxTag(tag: tag)])), ], ), Text(formattedAmount, From 8d7bad37f4251020ca66ac6db14e89a3f7fb6b36 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 23 Sep 2024 10:07:39 -0700 Subject: [PATCH 177/203] fix transaction history not showing up --- 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 6aee77ec9a..533fb346ba 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -254,6 +254,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (!mwebEnabled) { try { await updateAllUnspents(); + await updateTransactions(); await updateBalance(); syncStatus = SyncedSyncStatus(); } catch (e, s) { @@ -909,7 +910,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { }).toList()) .toHex(); tx.outputAddresses = resp.outputId; - + return tx ..addListener((transaction) async { final addresses = {}; From e48cd0df413b49603fd68da59184930eedb1034b Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 23 Sep 2024 14:26:56 -0700 Subject: [PATCH 178/203] fix mweb crash on non-fully deleted mweb cache, sync status ETA, other connection fixes --- cw_bitcoin/lib/litecoin_wallet.dart | 25 +++--- cw_bitcoin/lib/litecoin_wallet_service.dart | 18 +++- cw_core/lib/sync_status.dart | 89 ++++++++++++++++++- lib/core/sync_status_title.dart | 15 +++- .../dashboard/dashboard_view_model.dart | 1 + 5 files changed, 126 insertions(+), 22 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 533fb346ba..405c1bc89d 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -97,6 +97,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { late final Bip32Slip10Secp256k1 mwebHd; late final Box mwebUtxosBox; Timer? _syncTimer; + Timer? _stuckSyncTimer; Timer? _feeRatesTimer; StreamSubscription? _utxoStream; late RpcClient _stub; @@ -314,26 +315,27 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } await transactionHistory.save(); } + return; } }); // setup a watch dog to restart the sync process if it gets stuck: List lastFewProgresses = []; - Timer.periodic(const Duration(seconds: 10), (timer) async { + _stuckSyncTimer?.cancel(); + _stuckSyncTimer = Timer.periodic(const Duration(seconds: 10), (timer) async { if (syncStatus is! SyncingSyncStatus) return; - if (syncStatus.progress() > 0.98) return; + if (syncStatus.progress() > 0.98) return; // don't check if we're close to synced lastFewProgresses.add(syncStatus.progress()); - if (lastFewProgresses.length < 4) return; - // limit list size to 4: - while (lastFewProgresses.length > 4) { + if (lastFewProgresses.length < 10) return; + // limit list size to 10: + while (lastFewProgresses.length > 10) { lastFewProgresses.removeAt(0); } - // if the progress is the same over the last 40 seconds, restart the sync: + // if the progress is the same over the last 100 seconds, restart the sync: if (lastFewProgresses.every((p) => p == lastFewProgresses.first)) { print("mweb syncing is stuck, restarting..."); + syncStatus = LostConnectionSyncStatus(); await stopSync(); - startSync(); - timer.cancel(); } }); } @@ -870,10 +872,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final utxo = unspentCoins .firstWhere((utxo) => utxo.hash == txInput.txId && utxo.vout == txInput.txIndex); - if (txInput.sequence.isEmpty) { - isHogEx = false; - } - // TODO: detect actual hog-ex inputs // print(txInput.sequence); // print(txInput.txIndex); @@ -949,6 +947,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future close() async { await super.close(); + await stopSync(); + _stuckSyncTimer?.cancel(); + _feeRatesTimer?.cancel(); _syncTimer?.cancel(); _utxoStream?.cancel(); } diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index 67a503de74..c659dd6581 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -21,7 +21,8 @@ class LitecoinWalletService extends WalletService< BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials, BitcoinNewWalletCredentials> { - LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan, this.isDirect); + LitecoinWalletService( + this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan, this.isDirect); final Box walletInfoSource; final Box unspentCoinsInfoSource; @@ -66,6 +67,7 @@ class LitecoinWalletService extends WalletService< @override Future openWallet(String name, String password) async { + final walletInfo = walletInfoSource.values .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!; @@ -103,13 +105,21 @@ class LitecoinWalletService extends WalletService< .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 there are no more litecoin wallets left, cleanup the neutrino db and other files created by mwebd: if (walletInfoSource.values.where((info) => info.type == WalletType.litecoin).isEmpty) { - final appDir = await getApplicationSupportDirectory(); - File neturinoDb = File('${appDir.path}/neutrino.db'); + final appDirPath = (await getApplicationSupportDirectory()).path; + File neturinoDb = File('$appDirPath/neutrino.db'); + File blockHeaders = File('$appDirPath/block_headers.bin'); + File regFilterHeaders = File('$appDirPath/reg_filter_headers.bin'); if (neturinoDb.existsSync()) { neturinoDb.deleteSync(); } + if (blockHeaders.existsSync()) { + blockHeaders.deleteSync(); + } + if (regFilterHeaders.existsSync()) { + regFilterHeaders.deleteSync(); + } } } diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index 788309d8c6..81c4a8564d 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -1,6 +1,10 @@ abstract class SyncStatus { const SyncStatus(); double progress(); + + String formattedProgress() { + return "${(progress() * 100).toStringAsFixed(2)}%"; + } } class StartingScanSyncStatus extends SyncStatus { @@ -12,10 +16,12 @@ class StartingScanSyncStatus extends SyncStatus { } class SyncingSyncStatus extends SyncStatus { - SyncingSyncStatus(this.blocksLeft, this.ptc); + SyncingSyncStatus(this.blocksLeft, this.ptc) { + updateEtaHistory(blocksLeft); + } - final double ptc; - final int blocksLeft; + double ptc; + int blocksLeft; @override double progress() => ptc; @@ -32,6 +38,83 @@ class SyncingSyncStatus extends SyncStatus { // sum 1 because if at the chain tip, will say "0 blocks left" return SyncingSyncStatus(left + 1, ptc); } + + void updateEtaHistory(int blocksLeft) { + blockHistory[DateTime.now()] = blocksLeft; + + // Keep only the last 5 entries to limit memory usage + if (blockHistory.length > 5) { + var oldestKey = blockHistory.keys.reduce((a, b) => a.isBefore(b) ? a : b); + blockHistory.remove(oldestKey); + } + } + + static Map blockHistory = {}; + + DateTime? estimatedCompletionTime; + Duration? estimatedCompletionDuration; + + Duration getEtaDuration() { + DateTime now = DateTime.now(); + DateTime? completionTime = calculateETA(); + return completionTime.difference(now); + } + + String? getFormattedEta() { + // throw out any entries that are more than a minute old: + blockHistory.removeWhere( + (key, value) => key.isBefore(DateTime.now().subtract(const Duration(minutes: 1)))); + + if (blockHistory.length < 2) return null; + Duration? duration = getEtaDuration(); + + if (duration.inDays > 0) { + return null; + } + + String twoDigits(int n) => n.toString().padLeft(2, '0'); + + final hours = twoDigits(duration.inHours); + final minutes = twoDigits(duration.inMinutes.remainder(60)); + final seconds = twoDigits(duration.inSeconds.remainder(60)); + if (hours == '00') { + return '${minutes}m${seconds}s'; + } + return '${hours}h${minutes}m${seconds}s'; + } + + // Calculate the rate of block processing (blocks per second) + double calculateRate() { + List timestamps = blockHistory.keys.toList(); + List blockCounts = blockHistory.values.toList(); + + double totalTimeMinutes = 0; + int totalBlocksProcessed = 0; + + for (int i = 0; i < blockCounts.length - 1; i++) { + int blocksProcessed = blockCounts[i + 1] - blockCounts[i]; + Duration timeDifference = timestamps[i].difference(timestamps[i + 1]); + totalTimeMinutes += timeDifference.inSeconds; + totalBlocksProcessed += blocksProcessed; + } + + if (totalTimeMinutes == 0 || totalBlocksProcessed == 0) { + return 0; + } + + return totalBlocksProcessed / totalTimeMinutes; // Blocks per second + } + + // Calculate the ETA + DateTime calculateETA() { + double rate = calculateRate(); + if (rate < 0.01) { + return DateTime.now().add(const Duration(days: 1)); + } + int remainingBlocks = this.blocksLeft; + double timeRemainingSeconds = remainingBlocks / rate; + return DateTime.now().add(Duration(seconds: timeRemainingSeconds.round())); + } } class SyncedSyncStatus extends SyncStatus { diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index 4582f7b1ff..53240875aa 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -3,9 +3,18 @@ import 'package:cw_core/sync_status.dart'; String syncStatusTitle(SyncStatus syncStatus) { if (syncStatus is SyncingSyncStatus) { - return syncStatus.blocksLeft == 1 - ? S.current.block_remaining - : S.current.Blocks_remaining('${syncStatus.blocksLeft}'); + + if (syncStatus.blocksLeft == 1) { + return S.current.block_remaining; + } + + String eta = syncStatus.getFormattedEta() ?? ''; + + if (eta.isEmpty) { + return S.current.Blocks_remaining('${syncStatus.blocksLeft}'); + } else { + return "${syncStatus.formattedProgress()} - $eta"; + } } if (syncStatus is SyncedTipSyncStatus) { diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index b7a9bbbbda..21a167e2a0 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -258,6 +258,7 @@ abstract class DashboardViewModelBase with Store { if (hasMweb) { mwebScanningActive = bitcoin!.getMwebEnabled(wallet); + settingsStore.mwebEnabled = mwebScanningActive; reaction((_) => settingsStore.mwebAlwaysScan, (bool alwaysScan) { if (alwaysScan) { mwebScanningActive = true; From dbc2fb3fb163095b611050cc2da65fcff31572f7 Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 23 Sep 2024 14:29:13 -0700 Subject: [PATCH 179/203] [skip ci] minor code cleanup --- cw_bitcoin/lib/litecoin_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 405c1bc89d..1ca5d928e7 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -697,7 +697,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unconfirmedMweb += utxo.value.toInt(); } }); - if (/*confirmedMweb > 0 &&*/ unconfirmedMweb > 0) { + if (unconfirmedMweb > 0) { unconfirmedMweb = -1 * (confirmedMweb - unconfirmedMweb); } } catch (_) {} From 371fb8ce86e78bd52424e43ed72b324df91a128c Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 23 Sep 2024 14:29:33 -0700 Subject: [PATCH 180/203] [skip ci] minor code cleanup --- cw_bitcoin/lib/litecoin_wallet.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 1ca5d928e7..26e286a662 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -873,9 +873,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { .firstWhere((utxo) => utxo.hash == txInput.txId && utxo.vout == txInput.txIndex); // TODO: detect actual hog-ex inputs - // print(txInput.sequence); - // print(txInput.txIndex); - // print(utxo.value); if (!isHogEx) { return; From a226a338ff6c93d93b7f8c3f420fa93ee84fcbd6 Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 23 Sep 2024 14:41:19 -0700 Subject: [PATCH 181/203] additional cleanup --- cw_core/lib/sync_status.dart | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index 81c4a8564d..c59bff8200 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -41,9 +41,8 @@ class SyncingSyncStatus extends SyncStatus { void updateEtaHistory(int blocksLeft) { blockHistory[DateTime.now()] = blocksLeft; - - // Keep only the last 5 entries to limit memory usage - if (blockHistory.length > 5) { + // keep only the last 10 entries + if (blockHistory.length > 10) { var oldestKey = blockHistory.keys.reduce((a, b) => a.isBefore(b) ? a : b); blockHistory.remove(oldestKey); } @@ -54,9 +53,20 @@ class SyncingSyncStatus extends SyncStatus { DateTime? estimatedCompletionTime; Duration? estimatedCompletionDuration; + + DateTime calculateEta() { + double rate = _calculateBlockRate(); + if (rate < 0.01) { + return DateTime.now().add(const Duration(days: 99)); + } + int remainingBlocks = this.blocksLeft; + double timeRemainingSeconds = remainingBlocks / rate; + return DateTime.now().add(Duration(seconds: timeRemainingSeconds.round())); + } + Duration getEtaDuration() { DateTime now = DateTime.now(); - DateTime? completionTime = calculateETA(); + DateTime? completionTime = calculateEta(); return completionTime.difference(now); } @@ -84,36 +94,26 @@ class SyncingSyncStatus extends SyncStatus { } // Calculate the rate of block processing (blocks per second) - double calculateRate() { + double _calculateBlockRate() { List timestamps = blockHistory.keys.toList(); List blockCounts = blockHistory.values.toList(); - double totalTimeMinutes = 0; + double totalSeconds = 0; int totalBlocksProcessed = 0; for (int i = 0; i < blockCounts.length - 1; i++) { - int blocksProcessed = blockCounts[i + 1] - blockCounts[i]; - Duration timeDifference = timestamps[i].difference(timestamps[i + 1]); - totalTimeMinutes += timeDifference.inSeconds; + int blocksProcessed = blockCounts[i] - blockCounts[i + 1]; + Duration timeDifference = timestamps[i + 1].difference(timestamps[i]); + totalSeconds += timeDifference.inSeconds; totalBlocksProcessed += blocksProcessed; } - if (totalTimeMinutes == 0 || totalBlocksProcessed == 0) { + if (totalSeconds == 0 || totalBlocksProcessed == 0) { return 0; } - - return totalBlocksProcessed / totalTimeMinutes; // Blocks per second - } - - // Calculate the ETA - DateTime calculateETA() { - double rate = calculateRate(); - if (rate < 0.01) { - return DateTime.now().add(const Duration(days: 1)); - } - int remainingBlocks = this.blocksLeft; - double timeRemainingSeconds = remainingBlocks / rate; - return DateTime.now().add(Duration(seconds: timeRemainingSeconds.round())); + + double blocksPerSecond = totalBlocksProcessed / totalSeconds; + return blocksPerSecond; } } From 7435db03a824bdb0ebb05d1d676a39fa7cb7a17b Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 23 Sep 2024 16:18:38 -0700 Subject: [PATCH 182/203] silent payments eta fixes and updates --- cw_bitcoin/lib/electrum_wallet.dart | 8 +++- cw_bitcoin/lib/litecoin_wallet.dart | 6 +-- cw_core/lib/sync_status.dart | 58 ++++++++++++++++++++--------- cw_mweb/lib/cw_mweb.dart | 2 +- 4 files changed, 52 insertions(+), 22 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index c31a4d5cbd..d726d1c05e 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -398,7 +398,13 @@ abstract class ElectrumWalletBase nodeSupportsSilentPayments = false; } - syncStatus = message.syncStatus; + if (message.syncStatus is SyncingSyncStatus) { + var status = message.syncStatus as SyncingSyncStatus; + syncStatus = SyncingSyncStatus(status.blocksLeft, status.ptc); + } else { + syncStatus = message.syncStatus; + } + await walletInfo.updateRestoreHeight(message.height); } }); diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 26e286a662..04b500b434 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -943,12 +943,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future close() async { - await super.close(); - await stopSync(); + _utxoStream?.cancel(); _stuckSyncTimer?.cancel(); _feeRatesTimer?.cancel(); _syncTimer?.cancel(); - _utxoStream?.cancel(); + await stopSync(); + await super.close(); } Future setMwebEnabled(bool enabled) async { diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index c59bff8200..fb9ec30027 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -34,30 +34,27 @@ class SyncingSyncStatus extends SyncStatus { final diff = track - (chainTip - syncHeight); final ptc = diff <= 0 ? 0.0 : diff / track; final left = chainTip - syncHeight; + updateEtaHistory(left + 1); // sum 1 because if at the chain tip, will say "0 blocks left" return SyncingSyncStatus(left + 1, ptc); } - void updateEtaHistory(int blocksLeft) { + static void updateEtaHistory(int blocksLeft) { blockHistory[DateTime.now()] = blocksLeft; - // keep only the last 10 entries - if (blockHistory.length > 10) { - var oldestKey = blockHistory.keys.reduce((a, b) => a.isBefore(b) ? a : b); - blockHistory.remove(oldestKey); + // keep only the last 25 entries + while (blockHistory.length > 25) { + blockHistory.remove(blockHistory.keys.first); } } static Map blockHistory = {}; - - DateTime? estimatedCompletionTime; - Duration? estimatedCompletionDuration; - + static Duration? lastEtaDuration; DateTime calculateEta() { double rate = _calculateBlockRate(); - if (rate < 0.01) { - return DateTime.now().add(const Duration(days: 99)); + if (rate == 0) { + return DateTime.now().add(const Duration(days: 2)); } int remainingBlocks = this.blocksLeft; double timeRemainingSeconds = remainingBlocks / rate; @@ -75,13 +72,40 @@ class SyncingSyncStatus extends SyncStatus { blockHistory.removeWhere( (key, value) => key.isBefore(DateTime.now().subtract(const Duration(minutes: 1)))); - if (blockHistory.length < 2) return null; + // don't show eta if we don't have enough data: + if (blockHistory.length < 3) { + return null; + } + Duration? duration = getEtaDuration(); + // just show the block count if it's really long: if (duration.inDays > 0) { return null; } + // show the blocks count if the eta is less than a minute or we only have a few blocks left: + if (duration.inMinutes < 1 || blocksLeft < 1000) { + return null; + } + + // if our new eta is more than a minute off from the last one, only update the by 1 minute so it doesn't jump all over the place + if (lastEtaDuration != null) { + bool isIncreasing = duration.inSeconds > lastEtaDuration!.inSeconds; + bool diffMoreThanOneMinute = (duration.inSeconds - lastEtaDuration!.inSeconds).abs() > 60; + bool diffMoreThanOneHour = (duration.inSeconds - lastEtaDuration!.inSeconds).abs() > 3600; + if (diffMoreThanOneHour) { + duration = Duration(minutes: lastEtaDuration!.inMinutes + (isIncreasing ? 1 : -1)); + } else if (diffMoreThanOneMinute) { + duration = Duration(seconds: lastEtaDuration!.inSeconds + (isIncreasing ? 1 : -1)); + } else { + // if the diff is less than a minute don't change it: + duration = lastEtaDuration!; + } + } + + lastEtaDuration = duration; + String twoDigits(int n) => n.toString().padLeft(2, '0'); final hours = twoDigits(duration.inHours); @@ -98,21 +122,21 @@ class SyncingSyncStatus extends SyncStatus { List timestamps = blockHistory.keys.toList(); List blockCounts = blockHistory.values.toList(); - double totalSeconds = 0; + double totalTime = 0; int totalBlocksProcessed = 0; for (int i = 0; i < blockCounts.length - 1; i++) { int blocksProcessed = blockCounts[i] - blockCounts[i + 1]; Duration timeDifference = timestamps[i + 1].difference(timestamps[i]); - totalSeconds += timeDifference.inSeconds; + totalTime += timeDifference.inMicroseconds; totalBlocksProcessed += blocksProcessed; } - if (totalSeconds == 0 || totalBlocksProcessed == 0) { + if (totalTime == 0 || totalBlocksProcessed == 0) { return 0; } - - double blocksPerSecond = totalBlocksProcessed / totalSeconds; + + double blocksPerSecond = totalBlocksProcessed / (totalTime / 1000000); return blocksPerSecond; } } diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index 293210c2bc..63ff1bf97e 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -27,7 +27,7 @@ class CwMweb { await Future.delayed(const Duration(seconds: 5)); _clientChannel = ClientChannel('127.0.0.1', port: _port!, channelShutdownHandler: () { - print("Channel shutdown!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + print("Channel is shutting down!"); }, options: const ChannelOptions( credentials: ChannelCredentials.insecure(), From cb74505f5965d38cc162a1ad820e199182bb89ff Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 23 Sep 2024 17:03:58 -0700 Subject: [PATCH 183/203] revert sync eta changes into separate pr --- cw_core/lib/sync_status.dart | 115 ++--------------------------------- 1 file changed, 4 insertions(+), 111 deletions(-) diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index fb9ec30027..627b513b29 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -1,10 +1,6 @@ abstract class SyncStatus { const SyncStatus(); double progress(); - - String formattedProgress() { - return "${(progress() * 100).toStringAsFixed(2)}%"; - } } class StartingScanSyncStatus extends SyncStatus { @@ -16,12 +12,10 @@ class StartingScanSyncStatus extends SyncStatus { } class SyncingSyncStatus extends SyncStatus { - SyncingSyncStatus(this.blocksLeft, this.ptc) { - updateEtaHistory(blocksLeft); - } + SyncingSyncStatus(this.blocksLeft, this.ptc); - double ptc; - int blocksLeft; + final double ptc; + final int blocksLeft; @override double progress() => ptc; @@ -34,111 +28,10 @@ class SyncingSyncStatus extends SyncStatus { final diff = track - (chainTip - syncHeight); final ptc = diff <= 0 ? 0.0 : diff / track; final left = chainTip - syncHeight; - updateEtaHistory(left + 1); // sum 1 because if at the chain tip, will say "0 blocks left" return SyncingSyncStatus(left + 1, ptc); } - - static void updateEtaHistory(int blocksLeft) { - blockHistory[DateTime.now()] = blocksLeft; - // keep only the last 25 entries - while (blockHistory.length > 25) { - blockHistory.remove(blockHistory.keys.first); - } - } - - static Map blockHistory = {}; - static Duration? lastEtaDuration; - - DateTime calculateEta() { - double rate = _calculateBlockRate(); - if (rate == 0) { - return DateTime.now().add(const Duration(days: 2)); - } - int remainingBlocks = this.blocksLeft; - double timeRemainingSeconds = remainingBlocks / rate; - return DateTime.now().add(Duration(seconds: timeRemainingSeconds.round())); - } - - Duration getEtaDuration() { - DateTime now = DateTime.now(); - DateTime? completionTime = calculateEta(); - return completionTime.difference(now); - } - - String? getFormattedEta() { - // throw out any entries that are more than a minute old: - blockHistory.removeWhere( - (key, value) => key.isBefore(DateTime.now().subtract(const Duration(minutes: 1)))); - - // don't show eta if we don't have enough data: - if (blockHistory.length < 3) { - return null; - } - - Duration? duration = getEtaDuration(); - - // just show the block count if it's really long: - if (duration.inDays > 0) { - return null; - } - - // show the blocks count if the eta is less than a minute or we only have a few blocks left: - if (duration.inMinutes < 1 || blocksLeft < 1000) { - return null; - } - - // if our new eta is more than a minute off from the last one, only update the by 1 minute so it doesn't jump all over the place - if (lastEtaDuration != null) { - bool isIncreasing = duration.inSeconds > lastEtaDuration!.inSeconds; - bool diffMoreThanOneMinute = (duration.inSeconds - lastEtaDuration!.inSeconds).abs() > 60; - bool diffMoreThanOneHour = (duration.inSeconds - lastEtaDuration!.inSeconds).abs() > 3600; - if (diffMoreThanOneHour) { - duration = Duration(minutes: lastEtaDuration!.inMinutes + (isIncreasing ? 1 : -1)); - } else if (diffMoreThanOneMinute) { - duration = Duration(seconds: lastEtaDuration!.inSeconds + (isIncreasing ? 1 : -1)); - } else { - // if the diff is less than a minute don't change it: - duration = lastEtaDuration!; - } - } - - lastEtaDuration = duration; - - String twoDigits(int n) => n.toString().padLeft(2, '0'); - - final hours = twoDigits(duration.inHours); - final minutes = twoDigits(duration.inMinutes.remainder(60)); - final seconds = twoDigits(duration.inSeconds.remainder(60)); - if (hours == '00') { - return '${minutes}m${seconds}s'; - } - return '${hours}h${minutes}m${seconds}s'; - } - - // Calculate the rate of block processing (blocks per second) - double _calculateBlockRate() { - List timestamps = blockHistory.keys.toList(); - List blockCounts = blockHistory.values.toList(); - - double totalTime = 0; - int totalBlocksProcessed = 0; - - for (int i = 0; i < blockCounts.length - 1; i++) { - int blocksProcessed = blockCounts[i] - blockCounts[i + 1]; - Duration timeDifference = timestamps[i + 1].difference(timestamps[i]); - totalTime += timeDifference.inMicroseconds; - totalBlocksProcessed += blocksProcessed; - } - - if (totalTime == 0 || totalBlocksProcessed == 0) { - return 0; - } - - double blocksPerSecond = totalBlocksProcessed / (totalTime / 1000000); - return blocksPerSecond; - } } class SyncedSyncStatus extends SyncStatus { @@ -196,4 +89,4 @@ class TimedOutSyncStatus extends NotConnectedSyncStatus { class LostConnectionSyncStatus extends NotConnectedSyncStatus { @override String toString() => 'Reconnecting'; -} +} \ No newline at end of file From ea1d4cbb41d39ed82ed1a00c630a736b3f4d4dff Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 23 Sep 2024 17:20:17 -0700 Subject: [PATCH 184/203] [skip ci] minor --- cw_bitcoin/lib/litecoin_wallet.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 04b500b434..13b442d8d2 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -345,6 +345,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future stopSync() async { _syncTimer?.cancel(); _utxoStream?.cancel(); + _stuckSyncTimer?.cancel(); await CwMweb.stop(); } From 633d5242c2c09bd2afbcc5e9d1d6848a9ddb5fed Mon Sep 17 00:00:00 2001 From: fossephate Date: Mon, 23 Sep 2024 17:20:46 -0700 Subject: [PATCH 185/203] [skip ci] minor --- cw_bitcoin/lib/litecoin_wallet.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 13b442d8d2..a4ac7258be 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -346,6 +346,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { _syncTimer?.cancel(); _utxoStream?.cancel(); _stuckSyncTimer?.cancel(); + _feeRatesTimer?.cancel(); await CwMweb.stop(); } From 6a4eaece989bd840d12b442a7c2edef31745cb92 Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 08:37:44 -0700 Subject: [PATCH 186/203] revert sync status title --- lib/core/sync_status_title.dart | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index 53240875aa..4582f7b1ff 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -3,18 +3,9 @@ import 'package:cw_core/sync_status.dart'; String syncStatusTitle(SyncStatus syncStatus) { if (syncStatus is SyncingSyncStatus) { - - if (syncStatus.blocksLeft == 1) { - return S.current.block_remaining; - } - - String eta = syncStatus.getFormattedEta() ?? ''; - - if (eta.isEmpty) { - return S.current.Blocks_remaining('${syncStatus.blocksLeft}'); - } else { - return "${syncStatus.formattedProgress()} - $eta"; - } + return syncStatus.blocksLeft == 1 + ? S.current.block_remaining + : S.current.Blocks_remaining('${syncStatus.blocksLeft}'); } if (syncStatus is SyncedTipSyncStatus) { From 840287c72dfe00618581df7b45cd63fbb0d96c91 Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 12:57:12 -0700 Subject: [PATCH 187/203] review fixes, additional cleanup --- cw_bitcoin/lib/electrum_balance.dart | 7 +- cw_bitcoin/lib/electrum_wallet.dart | 8 +-- cw_bitcoin/lib/litecoin_wallet.dart | 70 +++++++------------ .../screens/receive/widgets/address_list.dart | 2 +- .../dashboard/balance_view_model.dart | 11 --- lib/view_model/send/send_view_model.dart | 11 ++- .../wallet_address_list_view_model.dart | 20 ++++-- 7 files changed, 54 insertions(+), 75 deletions(-) diff --git a/cw_bitcoin/lib/electrum_balance.dart b/cw_bitcoin/lib/electrum_balance.dart index fb0f059d81..3367511e75 100644 --- a/cw_bitcoin/lib/electrum_balance.dart +++ b/cw_bitcoin/lib/electrum_balance.dart @@ -48,15 +48,14 @@ class ElectrumBalance extends Balance { } @override - String get formattedSecondAvailableBalance => bitcoinAmountToString(amount: secondConfirmed ?? 0); + String get formattedSecondAvailableBalance => bitcoinAmountToString(amount: secondConfirmed); @override - String get formattedSecondAdditionalBalance => - bitcoinAmountToString(amount: secondUnconfirmed ?? 0); + String get formattedSecondAdditionalBalance => bitcoinAmountToString(amount: secondUnconfirmed); @override String get formattedFullAvailableBalance => - bitcoinAmountToString(amount: confirmed + (secondConfirmed ?? 0) - frozen); + bitcoinAmountToString(amount: confirmed + secondConfirmed - frozen); String toJSON() => json.encode({ 'confirmed': confirmed, diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index d726d1c05e..cf46ae2c83 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1357,13 +1357,7 @@ abstract class ElectrumWalletBase List> unspents = []; List updatedUnspentCoins = []; - try { - unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); - } catch (e, s) { - print(e); - print(s); - return []; - } + unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); await Future.wait(unspents.map((unspent) async { try { diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index a4ac7258be..1152603e5f 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -97,7 +97,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { late final Bip32Slip10Secp256k1 mwebHd; late final Box mwebUtxosBox; Timer? _syncTimer; - Timer? _stuckSyncTimer; Timer? _feeRatesTimer; StreamSubscription? _utxoStream; late RpcClient _stub; @@ -233,6 +232,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future waitForMwebAddresses() async { // ensure that we have the full 1000 mweb addresses generated before continuing: + // should no longer be needed, but leaving here just in case final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs; while (mwebAddrs.length < 1000) { print("waiting for mweb addresses to finish generating..."); @@ -243,31 +243,35 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @action @override Future startSync() async { + print("STARTING SYNC @@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + if (syncStatus is SyncronizingSyncStatus) { + return; + } print("STARTING SYNC - MWEB ENABLED: $mwebEnabled"); - syncStatus = SyncronizingSyncStatus(); - await subscribeForUpdates(); + try { + syncStatus = SyncronizingSyncStatus(); + await subscribeForUpdates(); - await updateFeeRates(); - _feeRatesTimer?.cancel(); - _feeRatesTimer = - Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); + await updateFeeRates(); + _feeRatesTimer?.cancel(); + _feeRatesTimer = + Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); - if (!mwebEnabled) { - try { - await updateAllUnspents(); - await updateTransactions(); - await updateBalance(); - syncStatus = SyncedSyncStatus(); - } catch (e, s) { - print(e); - print(s); - syncStatus = FailedSyncStatus(); + if (!mwebEnabled) { + try { + await updateAllUnspents(); + await updateTransactions(); + await updateBalance(); + syncStatus = SyncedSyncStatus(); + } catch (e, s) { + print(e); + print(s); + syncStatus = FailedSyncStatus(); + } + return; } - return; - } - await waitForMwebAddresses(); - try { + await waitForMwebAddresses(); await getStub(); await processMwebUtxos(); await updateTransactions(); @@ -318,26 +322,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return; } }); - - // setup a watch dog to restart the sync process if it gets stuck: - List lastFewProgresses = []; - _stuckSyncTimer?.cancel(); - _stuckSyncTimer = Timer.periodic(const Duration(seconds: 10), (timer) async { - if (syncStatus is! SyncingSyncStatus) return; - if (syncStatus.progress() > 0.98) return; // don't check if we're close to synced - lastFewProgresses.add(syncStatus.progress()); - if (lastFewProgresses.length < 10) return; - // limit list size to 10: - while (lastFewProgresses.length > 10) { - lastFewProgresses.removeAt(0); - } - // if the progress is the same over the last 100 seconds, restart the sync: - if (lastFewProgresses.every((p) => p == lastFewProgresses.first)) { - print("mweb syncing is stuck, restarting..."); - syncStatus = LostConnectionSyncStatus(); - await stopSync(); - } - }); } @action @@ -345,7 +329,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future stopSync() async { _syncTimer?.cancel(); _utxoStream?.cancel(); - _stuckSyncTimer?.cancel(); _feeRatesTimer?.cancel(); await CwMweb.stop(); } @@ -913,7 +896,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final addresses = {}; transaction.inputAddresses?.forEach((id) async { final utxo = mwebUtxosBox.get(id); - // await mwebUtxosBox.delete(id); + // await mwebUtxosBox.delete(id);// gets deleted in checkMwebUtxosSpent if (utxo == null) return; final addressRecord = walletAddresses.allAddresses .firstWhere((addressRecord) => addressRecord.address == utxo.address); @@ -946,7 +929,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future close() async { _utxoStream?.cancel(); - _stuckSyncTimer?.cancel(); _feeRatesTimer?.cancel(); _syncTimer?.cancel(); await stopSync(); diff --git a/lib/src/screens/receive/widgets/address_list.dart b/lib/src/screens/receive/widgets/address_list.dart index de01f6879a..27ec8c33a4 100644 --- a/lib/src/screens/receive/widgets/address_list.dart +++ b/lib/src/screens/receive/widgets/address_list.dart @@ -38,7 +38,7 @@ class AddressList extends StatelessWidget { separatorBuilder: (context, _) => const HorizontalSectionDivider(), shrinkWrap: true, physics: NeverScrollableScrollPhysics(), - itemCount: min(addressListViewModel.items.length, 100),// TODO: don't show all 1000 mweb addresses + itemCount: addressListViewModel.items.length, itemBuilder: (context, index) { final item = addressListViewModel.items[index]; Widget cell = Container(); diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 255bcc511e..c3fb5718af 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -235,17 +235,6 @@ abstract class BalanceViewModelBase with Store { return walletBalance.formattedAdditionalBalance; } - @computed - String get secondAdditionalBalance { - final walletBalance = _walletBalance; - - if (displayMode == BalanceDisplayMode.hiddenBalance) { - return '---'; - } - - return walletBalance.formattedSecondAdditionalBalance; - } - @computed String get availableFiatBalance { final walletBalance = _walletBalance; diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 79f473eb32..4edaad872f 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -217,7 +217,16 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor PendingTransaction? pendingTransaction; @computed - String get balance => wallet.balance[selectedCryptoCurrency]!.formattedFullAvailableBalance; + String get balance { + String fullFormattedBalance = + wallet.balance[selectedCryptoCurrency]!.formattedFullAvailableBalance; + String formattedAvailableBalance = + wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance; + if (fullFormattedBalance.isNotEmpty) { + return fullFormattedBalance; + } + return formattedAvailableBalance; + } @computed bool get isFiatDisabled => balanceViewModel.isFiatDisabled; 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 919d7baad0..b8274ce7cd 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 @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/core/wallet_change_listener_view_model.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; @@ -217,8 +219,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo }) : _baseItems = [], selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type), _cryptoNumberFormat = NumberFormat(_cryptoNumberPattern), - hasAccounts = - appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.wownero || appStore.wallet!.type == WalletType.haven, + hasAccounts = appStore.wallet!.type == WalletType.monero || + appStore.wallet!.type == WalletType.wownero || + appStore.wallet!.type == WalletType.haven, amount = '', _settingsStore = appStore.settingsStore, super(appStore: appStore) { @@ -230,7 +233,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo _init(); selectedCurrency = walletTypeToCryptoCurrency(wallet.type); - hasAccounts = wallet.type == WalletType.monero || wallet.type == WalletType.wownero || wallet.type == WalletType.haven; + hasAccounts = wallet.type == WalletType.monero || + wallet.type == WalletType.wownero || + wallet.type == WalletType.haven; } static const String _cryptoNumberPattern = '0.00000000'; @@ -446,7 +451,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo address: wallet.walletAddresses.address, )); } - + if (wallet.type == WalletType.tron) { final primaryAddress = tron!.getAddress(wallet); @@ -519,8 +524,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo @action Future setAddressType(dynamic option) async { - if (wallet.type == WalletType.bitcoin || - wallet.type == WalletType.litecoin) { + if (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) { await bitcoin!.setAddressType(wallet, option); } } @@ -528,7 +532,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo void _init() { _baseItems = []; - 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) { _baseItems.add(WalletAccountListHeader()); } From 31affa69eaff0685abcbb25f8126d905f1e16d5f Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 13:20:04 -0700 Subject: [PATCH 188/203] [skip ci] minor --- cw_bitcoin/lib/litecoin_wallet.dart | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 1152603e5f..7a52d1f2a1 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -300,11 +300,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } else if (resp.mwebUtxosHeight < nodeHeight) { syncStatus = SyncingSyncStatus(1, 0.999); } else { - // prevent unnecessary reaction triggers: - if (syncStatus is! SyncedSyncStatus) { - syncStatus = SyncedSyncStatus(); - } - if (resp.mwebUtxosHeight > walletInfo.restoreHeight) { await walletInfo.updateRestoreHeight(resp.mwebUtxosHeight); await checkMwebUtxosSpent(); @@ -319,6 +314,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } await transactionHistory.save(); } + + // prevent unnecessary reaction triggers: + if (syncStatus is! SyncedSyncStatus) { + syncStatus = SyncedSyncStatus(); + } return; } }); @@ -488,6 +488,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { throw Exception("failed to get utxos stream!"); } _utxoStream = responseStream.listen((Utxo sUtxo) async { + // we're processing utxos, so our balance could still be innacurate: + if (syncStatus is! SyncronizingSyncStatus && syncStatus is! SyncingSyncStatus) { + syncStatus = SyncronizingSyncStatus(); + } + final utxo = MwebUtxo( address: sUtxo.address, blockTime: sUtxo.blockTime, From 967e33329e5328329ce0908f1147afd1531521f5 Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 13:38:49 -0700 Subject: [PATCH 189/203] [skip ci] minor --- cw_bitcoin/lib/litecoin_wallet.dart | 35 ++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 7a52d1f2a1..d7a2efa93c 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -527,21 +527,29 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return; } - while ((await Future.wait(transactionHistory.transactions.values - .where((tx) => tx.direction == TransactionDirection.outgoing && tx.isPending) - .map(checkPendingTransaction))) - .any((x) => x)); + final pendingOutgoingTransactions = transactionHistory.transactions.values + .where((tx) => tx.direction == TransactionDirection.outgoing && tx.isPending); + // check if any of the pending outgoing transactions are now confirmed: + bool updatedAny = false; + for (final tx in pendingOutgoingTransactions) { + updatedAny = await checkPendingTransaction(tx) || updatedAny; + } + + // get output ids of all the mweb utxos that have > 0 height: final outputIds = mwebUtxosBox.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId).toList(); final resp = await CwMweb.spent(SpentRequest(outputId: outputIds)); final spent = resp.outputId; - if (spent.isEmpty) return; + if (spent.isEmpty) { + return; + } + final status = await CwMweb.status(StatusRequest()); final height = await electrumClient.getCurrentBlockChainTip(); if (height == null || status.blockHeaderHeight != height) return; - if (status.mwebUtxosHeight != height) return; + if (status.mwebUtxosHeight != height) return; // we aren't synced int amount = 0; Set inputAddresses = {}; @@ -583,11 +591,17 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { transactionHistory.addOne(tx); await transactionHistory.save(); + + if (updatedAny) { + await updateBalance(); + } } + // checks if a pending transaction is now confirmed, and updates the tx info accordingly: Future checkPendingTransaction(ElectrumTransactionInfo tx) async { if (!mwebEnabled) return false; if (!tx.isPending) return false; + final outputId = [], target = {}; final isHash = RegExp(r'^[a-f0-9]{64}$').hasMatch; final spendingOutputIds = tx.inputAddresses?.where(isHash) ?? []; @@ -595,6 +609,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { 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); @@ -602,13 +617,17 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { target.add(outputId); } } + if (outputId.isEmpty) { return false; } + final resp = await CwMweb.spent(SpentRequest(outputId: outputId)); - if (!setEquals(resp.outputId.toSet(), target)) return false; + if (!setEquals(resp.outputId.toSet(), target)) { + return false; + } + final status = await CwMweb.status(StatusRequest()); - if (!tx.isPending) return false; tx.height = status.mwebUtxosHeight; tx.confirmations = 1; tx.isPending = false; From 5cb8c4f40637fe112fb92557cdd47a94300691ee Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 14:22:58 -0700 Subject: [PATCH 190/203] [skip ci] minor --- cw_bitcoin/lib/litecoin_wallet.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index d7a2efa93c..a1d1d895fe 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -243,7 +243,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @action @override Future startSync() async { - print("STARTING SYNC @@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); if (syncStatus is SyncronizingSyncStatus) { return; } @@ -251,8 +250,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { try { syncStatus = SyncronizingSyncStatus(); await subscribeForUpdates(); - - await updateFeeRates(); + updateFeeRates(); _feeRatesTimer?.cancel(); _feeRatesTimer = Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); @@ -317,6 +315,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // prevent unnecessary reaction triggers: if (syncStatus is! SyncedSyncStatus) { + // mwebd is synced, but we could still be processing incoming utxos (should only take a few seconds) + // so we wait a few seconds before marking the wallet as synced + await Future.delayed(const Duration(seconds: 3)); syncStatus = SyncedSyncStatus(); } return; @@ -489,9 +490,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } _utxoStream = responseStream.listen((Utxo sUtxo) async { // we're processing utxos, so our balance could still be innacurate: - if (syncStatus is! SyncronizingSyncStatus && syncStatus is! SyncingSyncStatus) { - syncStatus = SyncronizingSyncStatus(); - } + // if (syncStatus is! SyncronizingSyncStatus && syncStatus is! SyncingSyncStatus) { + // syncStatus = SyncronizingSyncStatus(); + // } final utxo = MwebUtxo( address: sUtxo.address, From 29164b26086cc9f35e3625efcaf121af5c3c492d Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 15:10:14 -0700 Subject: [PATCH 191/203] trigger build --- cw_bitcoin/lib/litecoin_wallet.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index a1d1d895fe..d81fa22306 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -251,6 +251,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { syncStatus = SyncronizingSyncStatus(); await subscribeForUpdates(); updateFeeRates(); + _feeRatesTimer?.cancel(); _feeRatesTimer = Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); From ea929982c3558645c0ed3b27d7ae0a731190a5d1 Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 15:56:03 -0700 Subject: [PATCH 192/203] review fixes, pt.2 --- cw_bitcoin/lib/electrum_balance.dart | 7 ++---- cw_bitcoin/lib/litecoin_wallet.dart | 30 ++++++++++++++---------- cw_core/lib/balance.dart | 2 -- lib/view_model/send/send_view_model.dart | 11 +-------- 4 files changed, 21 insertions(+), 29 deletions(-) diff --git a/cw_bitcoin/lib/electrum_balance.dart b/cw_bitcoin/lib/electrum_balance.dart index 3367511e75..60d07e7b10 100644 --- a/cw_bitcoin/lib/electrum_balance.dart +++ b/cw_bitcoin/lib/electrum_balance.dart @@ -36,7 +36,8 @@ class ElectrumBalance extends Balance { int secondUnconfirmed = 0; @override - String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed - frozen); + String get formattedAvailableBalance => + bitcoinAmountToString(amount: confirmed + secondConfirmed - frozen); @override String get formattedAdditionalBalance => bitcoinAmountToString(amount: unconfirmed); @@ -53,10 +54,6 @@ class ElectrumBalance extends Balance { @override String get formattedSecondAdditionalBalance => bitcoinAmountToString(amount: secondUnconfirmed); - @override - String get formattedFullAvailableBalance => - bitcoinAmountToString(amount: confirmed + secondConfirmed - frozen); - String toJSON() => json.encode({ 'confirmed': confirmed, 'unconfirmed': unconfirmed, diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index d81fa22306..5a05cc684e 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -98,9 +98,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { late final Box mwebUtxosBox; Timer? _syncTimer; Timer? _feeRatesTimer; + Timer? _processingTimer; StreamSubscription? _utxoStream; late RpcClient _stub; late bool mwebEnabled; + bool processingUtxos = false; List get scanSecret => mwebHd.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw; List get spendSecret => mwebHd.childKey(Bip32KeyIndex(0x80000001)).privateKey.privKey.raw; @@ -251,7 +253,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { syncStatus = SyncronizingSyncStatus(); await subscribeForUpdates(); updateFeeRates(); - + _feeRatesTimer?.cancel(); _feeRatesTimer = Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); @@ -289,6 +291,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final nodeHeight = await electrumClient.getCurrentBlockChainTip() ?? 0; // current block height of our node final resp = await CwMweb.status(StatusRequest()); + print("MWEB: ${resp}"); if (resp.blockHeaderHeight < nodeHeight) { int h = resp.blockHeaderHeight; @@ -454,12 +457,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return; } - // if our address isn't in the inputs, update the txCount: - final inputAddresses = tx.inputAddresses ?? []; - if (!inputAddresses.contains(utxo.address)) { - addressRecord.txCount++; - } - + // update the txCount: + addressRecord.txCount++; addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); } @@ -491,9 +490,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } _utxoStream = responseStream.listen((Utxo sUtxo) async { // we're processing utxos, so our balance could still be innacurate: - // if (syncStatus is! SyncronizingSyncStatus && syncStatus is! SyncingSyncStatus) { - // syncStatus = SyncronizingSyncStatus(); - // } + if (syncStatus is! SyncronizingSyncStatus && syncStatus is! SyncingSyncStatus) { + syncStatus = SyncronizingSyncStatus(); + processingUtxos = true; + _processingTimer?.cancel(); + _processingTimer = Timer.periodic(const Duration(seconds: 2), (timer) async { + processingUtxos = false; + timer.cancel(); + }); + } final utxo = MwebUtxo( address: sUtxo.address, @@ -535,7 +540,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // check if any of the pending outgoing transactions are now confirmed: bool updatedAny = false; for (final tx in pendingOutgoingTransactions) { - updatedAny = await checkPendingTransaction(tx) || updatedAny; + updatedAny = await isConfirmed(tx) || updatedAny; } // get output ids of all the mweb utxos that have > 0 height: @@ -600,7 +605,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } // checks if a pending transaction is now confirmed, and updates the tx info accordingly: - Future checkPendingTransaction(ElectrumTransactionInfo tx) async { + Future isConfirmed(ElectrumTransactionInfo tx) async { if (!mwebEnabled) return false; if (!tx.isPending) return false; @@ -957,6 +962,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { _utxoStream?.cancel(); _feeRatesTimer?.cancel(); _syncTimer?.cancel(); + _processingTimer?.cancel(); await stopSync(); await super.close(); } diff --git a/cw_core/lib/balance.dart b/cw_core/lib/balance.dart index 09ca8efbb0..579a6da8fa 100644 --- a/cw_core/lib/balance.dart +++ b/cw_core/lib/balance.dart @@ -13,6 +13,4 @@ abstract class Balance { String get formattedUnAvailableBalance => ''; String get formattedSecondAvailableBalance => ''; String get formattedSecondAdditionalBalance => ''; - String get formattedFullAvailableBalance => ''; - String get formattedFullUnAvailableBalance => ''; } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 4edaad872f..cf4cc90a29 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -217,16 +217,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor PendingTransaction? pendingTransaction; @computed - String get balance { - String fullFormattedBalance = - wallet.balance[selectedCryptoCurrency]!.formattedFullAvailableBalance; - String formattedAvailableBalance = - wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance; - if (fullFormattedBalance.isNotEmpty) { - return fullFormattedBalance; - } - return formattedAvailableBalance; - } + String get balance => wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance; @computed bool get isFiatDisabled => balanceViewModel.isFiatDisabled; From efa8324b53aa0970b50451e0312147035c609fca Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 16:05:58 -0700 Subject: [PATCH 193/203] check if still processing utxos before updating sync status [skip ci] --- cw_bitcoin/lib/litecoin_wallet.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 5a05cc684e..6ef8dc7c88 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -319,10 +319,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // prevent unnecessary reaction triggers: if (syncStatus is! SyncedSyncStatus) { - // mwebd is synced, but we could still be processing incoming utxos (should only take a few seconds) - // so we wait a few seconds before marking the wallet as synced - await Future.delayed(const Duration(seconds: 3)); - syncStatus = SyncedSyncStatus(); + // mwebd is synced, but we could still be processing incoming utxos: + if (!processingUtxos) { + syncStatus = SyncedSyncStatus(); + } } return; } From 55d8b96bc8f6745cb43b78d362730941d6e74c09 Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 16:19:45 -0700 Subject: [PATCH 194/203] [skip ci] minor --- cw_bitcoin/lib/litecoin_wallet.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 6ef8dc7c88..c65b5cf68f 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -291,7 +291,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final nodeHeight = await electrumClient.getCurrentBlockChainTip() ?? 0; // current block height of our node final resp = await CwMweb.status(StatusRequest()); - print("MWEB: ${resp}"); if (resp.blockHeaderHeight < nodeHeight) { int h = resp.blockHeaderHeight; From 154eee317b6ac74a1022135eddb6aef13787ca76 Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 16:31:02 -0700 Subject: [PATCH 195/203] balance fix --- lib/view_model/send/send_view_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index cf4cc90a29..79f473eb32 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -217,7 +217,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor PendingTransaction? pendingTransaction; @computed - String get balance => wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance; + String get balance => wallet.balance[selectedCryptoCurrency]!.formattedFullAvailableBalance; @computed bool get isFiatDisabled => balanceViewModel.isFiatDisabled; From d040793dd54c50a24c2618b1876df8d841707f5c Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 16:31:33 -0700 Subject: [PATCH 196/203] minor --- cw_bitcoin/lib/electrum_balance.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/electrum_balance.dart b/cw_bitcoin/lib/electrum_balance.dart index 60d07e7b10..4e37f40b15 100644 --- a/cw_bitcoin/lib/electrum_balance.dart +++ b/cw_bitcoin/lib/electrum_balance.dart @@ -37,7 +37,7 @@ class ElectrumBalance extends Balance { @override String get formattedAvailableBalance => - bitcoinAmountToString(amount: confirmed + secondConfirmed - frozen); + bitcoinAmountToString(amount: confirmed - frozen); @override String get formattedAdditionalBalance => bitcoinAmountToString(amount: unconfirmed); @@ -54,6 +54,10 @@ class ElectrumBalance extends Balance { @override String get formattedSecondAdditionalBalance => bitcoinAmountToString(amount: secondUnconfirmed); + @override + String get formattedFullAvailableBalance => + bitcoinAmountToString(amount: confirmed + secondConfirmed - frozen); + String toJSON() => json.encode({ 'confirmed': confirmed, 'unconfirmed': unconfirmed, From f66e369e456d7f4829167d6e95c4c1d2210685f1 Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 16:33:34 -0700 Subject: [PATCH 197/203] minor --- cw_core/lib/balance.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/cw_core/lib/balance.dart b/cw_core/lib/balance.dart index 579a6da8fa..7350c80f18 100644 --- a/cw_core/lib/balance.dart +++ b/cw_core/lib/balance.dart @@ -13,4 +13,5 @@ abstract class Balance { String get formattedUnAvailableBalance => ''; String get formattedSecondAvailableBalance => ''; String get formattedSecondAdditionalBalance => ''; + String get formattedFullAvailableBalance => formattedAvailableBalance; } From 6f2cb4dff581d72aa8c49f6bd9b0adcf8a933b43 Mon Sep 17 00:00:00 2001 From: fossephate Date: Tue, 24 Sep 2024 16:52:44 -0700 Subject: [PATCH 198/203] [skip ci] minor --- cw_bitcoin/lib/litecoin_wallet.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index c65b5cf68f..e3d0605233 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -700,6 +700,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } await getStub(); + // update unspent balances: + await updateUnspent(); + int confirmed = balance.confirmed; int unconfirmed = balance.unconfirmed; int confirmedMweb = 0; @@ -717,9 +720,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } catch (_) {} - // update unspent balances: - await updateUnspent(); - for (var addressRecord in walletAddresses.allAddresses) { addressRecord.balance = 0; addressRecord.txCount = 0; From 1de4ccd83d37d6f956362ca5937fca8502bd70c2 Mon Sep 17 00:00:00 2001 From: fossephate Date: Wed, 25 Sep 2024 09:19:10 -0700 Subject: [PATCH 199/203] [skip ci] fix test net btc --- cw_bitcoin/lib/electrum_wallet.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index cf46ae2c83..3b07fd225d 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -112,10 +112,17 @@ abstract class ElectrumWalletBase "To create a Wallet you need either a seed or an xpub. This should not happen"); } + print("currency: $currency"); + print("network: $network"); + print("seedBytes: $seedBytes"); + print("xpub: $xpub"); + print("derivationInfo: $derivationInfo"); + if (seedBytes != null) { switch (currency) { case CryptoCurrency.btc: case CryptoCurrency.ltc: + case CryptoCurrency.tbtc: return Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath( _hardenedDerivationPath(derivationInfo?.derivationPath ?? electrum_path)) as Bip32Slip10Secp256k1; From 48061ea3a1db8be67591e5c9cf13f526fd0b61ae Mon Sep 17 00:00:00 2001 From: fossephate Date: Wed, 25 Sep 2024 10:36:37 -0700 Subject: [PATCH 200/203] don't use mwebd for non-mweb tx's --- cw_bitcoin/lib/litecoin_wallet.dart | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index e3d0605233..a099046f33 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -4,6 +4,7 @@ import 'package:convert/convert.dart' as convert; import 'dart:math'; import 'package:collection/collection.dart'; import 'package:crypto/crypto.dart'; +import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/mweb_utxo.dart'; import 'package:cw_mweb/mwebd.pbgrpc.dart'; @@ -819,6 +820,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } if (outputs.length == 1 && outputs[0].toOutput.amount == BigInt.zero) { + // TODO: for some reason we can't type cast BitcoinScriptOutput to BitcoinBaseOutput (even though one implements the other) + // this breaks using the ALL button on litecoin mweb tx's: outputs = [ BitcoinScriptOutput( script: outputs[0].toOutput.scriptPubKey, value: utxos.sumOfUtxosValue()) @@ -880,7 +883,29 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { )); final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); + // check if the transaction doesn't contain any mweb inputs or outputs: + final transactionCredentials = credentials as BitcoinTransactionCredentials; + + bool hasMwebInput = false; + bool hasMwebOutput = false; + + for (final output in transactionCredentials.outputs) { + if (output.extractedAddress?.toLowerCase().contains("mweb") ?? false) { + hasMwebOutput = true; + break; + } + } + + if (tx2.mwebBytes != null && tx2.mwebBytes!.isNotEmpty) { + hasMwebInput = true; + } + + if (!hasMwebInput && !hasMwebOutput) { + return tx; + } + // check if any of the inputs of this transaction are hog-ex: + // this list is only non-mweb inputs: tx2.inputs.forEach((txInput) { bool isHogEx = true; From dcac601b8f46e907bed84062f43350e9a3bb7b05 Mon Sep 17 00:00:00 2001 From: fossephate Date: Wed, 25 Sep 2024 10:42:42 -0700 Subject: [PATCH 201/203] [skip ci] minor cleanup --- cw_bitcoin/lib/electrum_wallet.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 3b07fd225d..d516624244 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -112,12 +112,6 @@ abstract class ElectrumWalletBase "To create a Wallet you need either a seed or an xpub. This should not happen"); } - print("currency: $currency"); - print("network: $network"); - print("seedBytes: $seedBytes"); - print("xpub: $xpub"); - print("derivationInfo: $derivationInfo"); - if (seedBytes != null) { switch (currency) { case CryptoCurrency.btc: From 3724681ba4345a37c2b60fa917799f78f68cd0b3 Mon Sep 17 00:00:00 2001 From: fossephate Date: Wed, 25 Sep 2024 10:52:32 -0700 Subject: [PATCH 202/203] don't show all 1000+ mweb addresses on receive page --- .../wallet_address_list_view_model.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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 b8274ce7cd..17fc4b8490 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 @@ -409,7 +409,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo }); addressList.addAll(receivedAddressItems); } else { - final addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) { + var addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) { final isPrimary = subaddress.id == 0; return WalletAddressListItem( @@ -422,6 +422,16 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo walletTypeToCryptoCurrency(type), subaddress.balance), isChange: subaddress.isChange); }); + + // don't show all 1000+ mweb addresses: + if (wallet.type == WalletType.litecoin && addressItems.length >= 1000) { + // find the index of the last item with a txCount > 0 + final addressItemsList = addressItems.toList(); + final lastItemWithTxCount = addressItemsList.lastWhere((item) => (item.txCount ?? 0) > 0); + final index = addressItemsList.indexOf(lastItemWithTxCount); + // show only up to that index + 20: + addressItems = addressItemsList.sublist(0, index + 20); + } addressList.addAll(addressItems); } } From 2f8c1a0dce741f08f79a34ea95c362bd87ae4314 Mon Sep 17 00:00:00 2001 From: fossephate Date: Thu, 26 Sep 2024 13:58:15 -0700 Subject: [PATCH 203/203] minor cleanup + additional logging --- cw_bitcoin/lib/litecoin_wallet.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index a099046f33..d7e6fef61a 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -292,6 +292,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final nodeHeight = await electrumClient.getCurrentBlockChainTip() ?? 0; // current block height of our node final resp = await CwMweb.status(StatusRequest()); + print("resp.mwebUtxosHeight: ${resp.mwebUtxosHeight}"); + print("resp.mwebHeaderHeight: ${resp.mwebHeaderHeight}"); + print("resp.blockHeaderHeight: ${resp.blockHeaderHeight}"); if (resp.blockHeaderHeight < nodeHeight) { int h = resp.blockHeaderHeight; @@ -820,13 +823,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } if (outputs.length == 1 && outputs[0].toOutput.amount == BigInt.zero) { - // TODO: for some reason we can't type cast BitcoinScriptOutput to BitcoinBaseOutput (even though one implements the other) - // this breaks using the ALL button on litecoin mweb tx's: outputs = [ BitcoinScriptOutput( script: outputs[0].toOutput.scriptPubKey, value: utxos.sumOfUtxosValue()) ]; } + // https://github.com/ltcmweb/mwebd?tab=readme-ov-file#fee-estimation final preOutputSum = outputs.fold(BigInt.zero, (acc, output) => acc + output.toOutput.amount);