mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Merge branch 'cake-tech:dev' into dev
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature or Enhancement Request ✨
|
||||
url: https://github.com/cake-tech/cake_wallet/discussions/new?category=feature-requests
|
||||
about: Suggest an idea for Cake Wallet
|
||||
- name: Not sure where to start?
|
||||
url: https://docs.cakewallet.com
|
||||
about: Start by reading checking out the guides!
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
name: Lint
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
jobs:
|
||||
# lint:
|
||||
# runs-on: [Linux, amd64, android]
|
||||
# container:
|
||||
# image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.41.9-ndkr28-go1.24.1-ruststablenightly
|
||||
# env:
|
||||
# STORE_PASS: test@cake_wallet
|
||||
# KEY_PASS: test@cake_wallet
|
||||
# MONEROC_CACHE_DIR_ROOT: /opt/generic_cache
|
||||
# BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
# ANDROID_AVD_HOME: /root/.android/avd
|
||||
# volumes:
|
||||
# - /opt/cw_cache_android/root/.cache:/root/.cache
|
||||
# - /opt/cw_cache_android/root/.android/avd/:/root/.android/avd
|
||||
# - /opt/cw_cache_android/root/.ccache:/root/.ccache
|
||||
# - /opt/cw_cache_android/root/.pub-cache/:/root/.pub-cache
|
||||
# - /opt/cw_cache_android/root/.gradle/:/root/.gradle
|
||||
# - /opt/cw_cache_android/root/.android/:/root/.android
|
||||
# - /opt/cw_cache_android/root/go/pkg:/root/go/pkg
|
||||
# - /opt/cw_cache_android/opt/generic_cache:/opt/generic_cache
|
||||
# - /dev/kvm:/dev/kvm
|
||||
# - /var/run/docker.sock:/var/run/docker.sock
|
||||
# strategy:
|
||||
# matrix:
|
||||
# api-level: [ 29 ]
|
||||
#
|
||||
# steps:
|
||||
# - name: Fix github actions messing up $HOME...
|
||||
# run: "echo HOME=/root | sudo tee -a $GITHUB_ENV"
|
||||
# - uses: actions/checkout@v4
|
||||
# with:
|
||||
# ref: ${{ inputs.ref }}
|
||||
#
|
||||
# - name: configure git
|
||||
# run: |
|
||||
# git config --global --add safe.directory '*'
|
||||
# git config --global user.email "ci@cakewallet.com"
|
||||
# git config --global user.name "CakeWallet CI"
|
||||
#
|
||||
# - name: Fetch prebuilt Torch
|
||||
# run: |
|
||||
# set -x -e
|
||||
# pushd scripts
|
||||
# wget https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz -O torch_dart.tar.gz
|
||||
# rm -rf torch_dart
|
||||
# mkdir torch_dart
|
||||
# pushd torch_dart
|
||||
# tar -xzf ../torch_dart.tar.gz
|
||||
# popd
|
||||
# rm ./torch_dart.tar.gz
|
||||
# popd
|
||||
#
|
||||
# - name: Fetch prebuilt Reown
|
||||
# run: |
|
||||
# set -x -e
|
||||
# pushd scripts
|
||||
# # cleaning
|
||||
# rm -rf reown_flutter
|
||||
# rm -f reown_flutter.tar.gz
|
||||
#
|
||||
# wget https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz -O reown_flutter.tar.gz
|
||||
# mkdir reown_flutter
|
||||
# pushd reown_flutter
|
||||
# tar -xzf ../reown_flutter.tar.gz
|
||||
# popd
|
||||
# rm ./reown_flutter.tar.gz
|
||||
# popd
|
||||
#
|
||||
# - name: Build Bitbox Flutter
|
||||
# run: |
|
||||
# set -x -e
|
||||
# pushd scripts
|
||||
# ./build_bitbox_flutter.sh
|
||||
# popd
|
||||
#
|
||||
# - name: Prepare Zcash
|
||||
# run: |
|
||||
# set -x -e
|
||||
# scripts/prepare_zcash.sh
|
||||
#
|
||||
# - name: Execute Build and Setup Commands
|
||||
# run: |
|
||||
# pushd scripts/android
|
||||
# source ./app_env.sh cakewallet
|
||||
# export GITHUB_HUH=yeah
|
||||
# ./app_config.sh
|
||||
# popd
|
||||
#
|
||||
# - name: Install Flutter dependencies
|
||||
# run: |
|
||||
# flutter pub get
|
||||
#
|
||||
# - name: Build generated code
|
||||
# run: |
|
||||
# flutter --version
|
||||
# flutter clean
|
||||
# rm -rf .dart_tool
|
||||
# rm pubspec.lock
|
||||
# flutter pub get
|
||||
# ./model_generator.sh async
|
||||
#
|
||||
# - name: Generate localization
|
||||
# run: |
|
||||
# dart run tool/generate_localization.dart
|
||||
#
|
||||
# - name: Lint
|
||||
# run: |
|
||||
# ABORT_ON_CHANGE=yeah scripts/lint.sh
|
||||
@@ -11,11 +11,10 @@ jobs:
|
||||
- name: Check for http package usage
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
GIT_GREP_OUT="$(git grep package:http | (grep .dart: || test $? = 1) | (grep -v proxy_wrapper.dart || test $? = 1) | (grep -v very_insecure_http_do_not_use || test $? = 1) || true)"
|
||||
GIT_GREP_OUT="$(git grep package:http | (grep .dart: || test $? = 1) | (grep -v proxy_wrapper.dart || test $? = 1) | (grep -v very_insecure_http_do_not_use || test $? = 1) | (grep -v '^cw_custom_lints/' || test $? = 1) || true)"
|
||||
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
|
||||
echo "$GIT_GREP_OUT"
|
||||
echo "There are .dart files which use http imports"
|
||||
echo "Using http package breaks proxy integration"
|
||||
echo "Please use ProxyWrapper.getHttpClient() from package:cw_core/utils/proxy_wrapper.dart"
|
||||
exit 1
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
- name: Check for print() statements in dart code (use printV() instead)
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
GIT_GREP_OUT="$(git grep ' print(' | (grep .dart: || test $? = 1) | (grep -v print_verbose.dart || test $? = 1) | (grep -v print_verbose_dummy.dart || test $? = 1) || true)"
|
||||
GIT_GREP_OUT="$(git grep ' print(' | (grep .dart: || test $? = 1) | (grep -v print_verbose.dart || test $? = 1) | (grep -v print_verbose_dummy.dart || test $? = 1) | (grep -v '^tool/' || test $? = 1) | (grep -v '^cw_custom_lints/' || test $? = 1) || true)"
|
||||
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
|
||||
echo "$GIT_GREP_OUT"
|
||||
echo "There are .dart files which use print() statements"
|
||||
|
||||
@@ -179,9 +179,6 @@ jobs:
|
||||
with:
|
||||
path: ${{ github.workspace }}/build/linux/x64/release/cakewallet_linux.zip
|
||||
name: cakewallet_linux
|
||||
- name: Verify lint
|
||||
run: |
|
||||
exec env ABORT_ON_CHANGE=true ./scripts/lint.sh
|
||||
|
||||
- name: Prepare virtual desktop
|
||||
if: ${{ contains(env.message, 'run tests') }}
|
||||
|
||||
+113
-54
@@ -1,4 +1,3 @@
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
|
||||
analyzer:
|
||||
@@ -9,6 +8,7 @@ analyzer:
|
||||
cw_core/lib/**.g.dart,
|
||||
cw_haven/lib/**.g.dart,
|
||||
cw_monero/lib/**.g.dart,
|
||||
cw_mweb/lib/**.g.dart,
|
||||
lib/generated/*.dart,
|
||||
cw_monero/ios/External/**,
|
||||
cw_shared_external/**,
|
||||
@@ -18,65 +18,124 @@ analyzer:
|
||||
strict-casts: true
|
||||
strict-raw-types: true
|
||||
|
||||
plugins:
|
||||
cw_custom_lints:
|
||||
path: cw_custom_lints
|
||||
|
||||
linter:
|
||||
rules:
|
||||
- cancel_subscriptions
|
||||
- always_declare_return_types
|
||||
- prefer_final_fields
|
||||
- always_put_control_body_on_new_line
|
||||
- always_put_required_named_parameters_first
|
||||
- always_use_package_imports
|
||||
- annotate_overrides
|
||||
- annotate_redeclares
|
||||
- avoid_empty_else
|
||||
- avoid_function_literals_in_foreach_calls
|
||||
- avoid_init_to_null
|
||||
- avoid_multiple_declarations_per_line
|
||||
- avoid_positional_boolean_parameters
|
||||
- avoid_relative_lib_imports
|
||||
- avoid_renaming_method_parameters
|
||||
- avoid_return_types_on_setters
|
||||
- avoid_returning_null_for_void
|
||||
- avoid_slow_async_io
|
||||
- avoid_types_as_parameter_names
|
||||
- avoid_types_on_closure_parameters
|
||||
- avoid_unnecessary_containers
|
||||
- avoid_unused_constructor_parameters
|
||||
- avoid_void_async
|
||||
- await_only_futures
|
||||
- camel_case_extensions
|
||||
- camel_case_types
|
||||
- cast_nullable_to_non_nullable
|
||||
- collection_methods_unrelated_type
|
||||
- constant_identifier_names
|
||||
- curly_braces_in_flow_control_structures
|
||||
- directives_ordering
|
||||
- empty_constructor_bodies
|
||||
- empty_statements
|
||||
- eol_at_end_of_file
|
||||
- flutter_style_todos
|
||||
- hash_and_equals
|
||||
- implicit_reopen
|
||||
- library_private_types_in_public_api
|
||||
- non_constant_identifier_names
|
||||
- only_throw_errors
|
||||
- package_names
|
||||
- prefer_asserts_with_message
|
||||
- prefer_conditional_assignment
|
||||
- prefer_const_constructors
|
||||
- prefer_const_constructors_in_immutables
|
||||
- prefer_const_declarations
|
||||
- prefer_constructors_over_static_methods
|
||||
- prefer_contains
|
||||
- prefer_double_quotes
|
||||
- prefer_expression_function_bodies
|
||||
- prefer_final_in_for_each
|
||||
- prefer_function_declarations_over_variables
|
||||
- prefer_if_null_operators
|
||||
- prefer_initializing_formals
|
||||
- prefer_inlined_adds
|
||||
- prefer_int_literals
|
||||
- prefer_is_empty
|
||||
- prefer_is_not_empty
|
||||
- prefer_is_not_operator
|
||||
- prefer_iterable_wheretype
|
||||
- prefer_null_aware_operators
|
||||
- prefer_spread_collections
|
||||
- prefer_typing_uninitialized_variables
|
||||
- recursive_getters
|
||||
- require_trailing_commas
|
||||
- sized_box_for_whitespace
|
||||
- sort_child_properties_last
|
||||
- sort_constructors_first
|
||||
- sort_unnamed_constructors_first
|
||||
- test_types_in_equals
|
||||
- unawaited_futures
|
||||
- unnecessary_async
|
||||
- unnecessary_await_in_return
|
||||
- unnecessary_const
|
||||
- unnecessary_constructor_name
|
||||
- unnecessary_lambdas
|
||||
- unnecessary_late
|
||||
- unnecessary_new
|
||||
- unnecessary_null_aware_assignments
|
||||
- unnecessary_null_checks
|
||||
- unnecessary_null_in_if_null_operators
|
||||
- unnecessary_nullable_for_final_variable_declarations
|
||||
- unnecessary_overrides
|
||||
- unnecessary_parenthesis
|
||||
- unnecessary_string_escapes
|
||||
- unnecessary_string_interpolations
|
||||
- unnecessary_this
|
||||
- unnecessary_to_list_in_spreads
|
||||
- unnecessary_underscores
|
||||
- unreachable_from_main
|
||||
- unrelated_type_equality_checks
|
||||
- use_build_context_synchronously
|
||||
- use_colored_box
|
||||
- use_full_hex_values_for_flutter_colors
|
||||
- use_function_type_syntax_for_parameters
|
||||
- use_is_even_rather_than_modulo
|
||||
- use_named_constants
|
||||
- use_null_aware_elements
|
||||
- use_raw_strings
|
||||
- use_rethrow_when_possible
|
||||
- use_string_buffers
|
||||
- use_super_parameters
|
||||
- use_truncating_division
|
||||
- valid_regexps
|
||||
- void_checks
|
||||
- prefer_final_locals
|
||||
- prefer_interpolation_to_compose_strings
|
||||
- null_closures
|
||||
- no_wildcard_variable_uses
|
||||
|
||||
|
||||
|
||||
|
||||
formatter:
|
||||
page_width: 100
|
||||
|
||||
|
||||
# analyzer:
|
||||
# strong-mode:
|
||||
# implicit-casts: false
|
||||
# implicit-dynamic: false
|
||||
# exclude: [build/**, lib/generated/*.dart, lib/**.g.dart, cw_monero/ios/External/**, cw_shared_external/**, shared_external/**]
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - always_declare_return_types
|
||||
# - annotate_overrides
|
||||
# - avoid_empty_else
|
||||
# - avoid_init_to_null
|
||||
# - avoid_return_types_on_setters
|
||||
# - await_only_futures
|
||||
# - camel_case_types
|
||||
# - cancel_subscriptions
|
||||
# - close_sinks
|
||||
# - comment_references
|
||||
# - constant_identifier_names
|
||||
# - control_flow_in_finally
|
||||
# - empty_catches
|
||||
# - empty_constructor_bodies
|
||||
# - empty_statements
|
||||
# - hash_and_equals
|
||||
# - invariant_booleans
|
||||
# - iterable_contains_unrelated_type
|
||||
# - library_names
|
||||
# - library_prefixes
|
||||
# - list_remove_unrelated_type
|
||||
# - literal_only_boolean_expressions
|
||||
# - non_constant_identifier_names
|
||||
# - one_member_abstracts
|
||||
# - only_throw_errors
|
||||
# - overridden_fields
|
||||
# - package_api_docs
|
||||
# - package_names
|
||||
# - package_prefixed_library_names
|
||||
# - parameter_assignments
|
||||
# - prefer_final_fields
|
||||
# - prefer_final_locals
|
||||
# - prefer_is_not_empty
|
||||
# - slash_for_doc_comments
|
||||
# - sort_constructors_first
|
||||
# - sort_unnamed_constructors_first
|
||||
# - test_types_in_equals
|
||||
# - throw_in_finally
|
||||
# - type_init_formals
|
||||
# - unawaited_futures
|
||||
# - unnecessary_getters_setters
|
||||
# - unrelated_type_equality_checks
|
||||
# - valid_regexps
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
org.gradle.jvmargs=-Xmx6144M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.ndk.suppressMinSdkVersionError=21
|
||||
|
||||
android.ndk.suppressMinSdkVersionError=21
|
||||
|
||||
@@ -17,21 +17,16 @@ BitcoinBaseAddress addressFromScript(Script script,
|
||||
|
||||
switch (addressType) {
|
||||
case P2pkhAddressType.p2pkh:
|
||||
return P2pkhAddress.fromScriptPubkey(
|
||||
script: script, network: BitcoinNetwork.mainnet);
|
||||
return P2pkhAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
|
||||
case P2shAddressType.p2pkhInP2sh:
|
||||
case P2shAddressType.p2pkInP2sh:
|
||||
return P2shAddress.fromScriptPubkey(
|
||||
script: script, network: BitcoinNetwork.mainnet);
|
||||
return P2shAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
|
||||
case SegwitAddresType.p2wpkh:
|
||||
return P2wpkhAddress.fromScriptPubkey(
|
||||
script: script, network: BitcoinNetwork.mainnet);
|
||||
return P2wpkhAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
|
||||
case SegwitAddresType.p2wsh:
|
||||
return P2wshAddress.fromScriptPubkey(
|
||||
script: script, network: BitcoinNetwork.mainnet);
|
||||
return P2wshAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
|
||||
case SegwitAddresType.p2tr:
|
||||
return P2trAddress.fromScriptPubkey(
|
||||
script: script, network: BitcoinNetwork.mainnet);
|
||||
return P2trAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
|
||||
}
|
||||
|
||||
throw ArgumentError("Invalid script");
|
||||
|
||||
@@ -72,17 +72,16 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||
required super.type,
|
||||
String? scriptHash,
|
||||
required super.network,
|
||||
}) {
|
||||
}) {
|
||||
try {
|
||||
this.scriptHash = scriptHash ??
|
||||
(network != null ? BitcoinAddressUtils.scriptHash(address, network: network!) : null);
|
||||
(network != null ? BitcoinAddressUtils.scriptHash(address, network: network!) : null);
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool _legacyDefaultForType(BitcoinAddressType type) {
|
||||
|
||||
// Some address types (p2wpkh, p2wsh) were historically derived from the same account/path as our new standard
|
||||
// but using legacy formats. For these, we default to legacy = false.
|
||||
if (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh) return false;
|
||||
@@ -154,6 +153,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||
}
|
||||
return scriptHash!;
|
||||
}
|
||||
|
||||
@override
|
||||
String get derivationPath {
|
||||
if (type == SegwitAddresType.mweb) {
|
||||
@@ -162,9 +162,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||
|
||||
final coinType = _coinTypeForNetwork();
|
||||
final purpose = _purposeForType(type);
|
||||
final accountPath = isLegacyDerivation
|
||||
? electrum_path
|
||||
: "m/$purpose'/$coinType'/0'";
|
||||
final accountPath = isLegacyDerivation ? electrum_path : "m/$purpose'/$coinType'/0'";
|
||||
|
||||
final chain = isHidden ? 1 : 0;
|
||||
return "$accountPath/$chain/$index";
|
||||
|
||||
@@ -7,8 +7,8 @@ final bitcoinAmountFormat = NumberFormat()
|
||||
..maximumFractionDigits = bitcoinAmountLength
|
||||
..minimumFractionDigits = 1;
|
||||
|
||||
String bitcoinAmountToString({required int amount}) => bitcoinAmountFormat.format(
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider));
|
||||
String bitcoinAmountToString({required int amount}) =>
|
||||
bitcoinAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider));
|
||||
|
||||
double bitcoinAmountToDouble({required int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
|
||||
|
||||
@@ -5,4 +5,3 @@ class BitcoinCommitTransactionException implements Exception {
|
||||
@override
|
||||
String toString() => errorMessage;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class BitcoinTransactionPriority extends TransactionPriority {
|
||||
const BitcoinTransactionPriority({required String title, required int raw, String? description, String? hint})
|
||||
const BitcoinTransactionPriority(
|
||||
{required String title, required int raw, String? description, String? hint})
|
||||
: super(title: title, raw: raw, description: description, hint: hint);
|
||||
|
||||
static const List<BitcoinTransactionPriority> all = [fast, medium, slow, custom];
|
||||
@@ -10,10 +11,10 @@ class BitcoinTransactionPriority extends TransactionPriority {
|
||||
BitcoinTransactionPriority(title: 'Slow', description: "2 sat/byte", hint: "~ 24 h", raw: 0);
|
||||
static const BitcoinTransactionPriority medium =
|
||||
BitcoinTransactionPriority(title: 'Medium', description: "3 sat/byte", hint: "~ 1 h", raw: 1);
|
||||
static const BitcoinTransactionPriority fast =
|
||||
BitcoinTransactionPriority(title: 'Fast', description: "4 sat/byte", hint: "~ 30 min", raw: 2);
|
||||
static const BitcoinTransactionPriority fast = BitcoinTransactionPriority(
|
||||
title: 'Fast', description: "4 sat/byte", hint: "~ 30 min", raw: 2);
|
||||
static const BitcoinTransactionPriority custom =
|
||||
BitcoinTransactionPriority(title: 'Custom', raw: 3);
|
||||
BitcoinTransactionPriority(title: 'Custom', raw: 3);
|
||||
|
||||
static BitcoinTransactionPriority deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
@@ -116,19 +117,19 @@ class LitecoinTransactionPriority extends BitcoinTransactionPriority {
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class BitcoinCashTransactionPriority extends BitcoinTransactionPriority {
|
||||
const BitcoinCashTransactionPriority({required String title, required int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const List<BitcoinCashTransactionPriority> all = [fast, medium, slow];
|
||||
static const BitcoinCashTransactionPriority slow =
|
||||
BitcoinCashTransactionPriority(title: 'Slow', raw: 0);
|
||||
BitcoinCashTransactionPriority(title: 'Slow', raw: 0);
|
||||
static const BitcoinCashTransactionPriority medium =
|
||||
BitcoinCashTransactionPriority(title: 'Medium', raw: 1);
|
||||
BitcoinCashTransactionPriority(title: 'Medium', raw: 1);
|
||||
static const BitcoinCashTransactionPriority fast =
|
||||
BitcoinCashTransactionPriority(title: 'Fast', raw: 2);
|
||||
BitcoinCashTransactionPriority(title: 'Fast', raw: 2);
|
||||
|
||||
static BitcoinCashTransactionPriority deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
@@ -170,4 +171,3 @@ class BitcoinCashTransactionPriority extends BitcoinTransactionPriority {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
autorun((_) {
|
||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||
});
|
||||
|
||||
|
||||
reaction((_) => this.useLightning, (bool useLightning) {
|
||||
if (useLightning && LightningWallet.isAvailable) {
|
||||
if (mnemonic != null) {
|
||||
@@ -306,28 +306,28 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
}
|
||||
|
||||
return BitcoinWallet(
|
||||
mnemonic: mnemonic,
|
||||
xpub: keysData.xPub != null ? convertZpubToXpub(keysData.xPub!) : null,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
walletInfo: walletInfo,
|
||||
derivationInfo: derivationInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: snp?.addresses,
|
||||
initialSilentAddresses: snp?.silentAddresses,
|
||||
initialSilentAddressIndex: snp?.silentAddressIndex ?? 0,
|
||||
initialBalance: snp?.balance,
|
||||
initialLightningBalance: snp?.lightningBalance,
|
||||
encryptionFileUtils: encryptionFileUtils,
|
||||
seedBytes: seedBytes,
|
||||
initialRegularAddressIndex: snp?.regularAddressIndex,
|
||||
initialChangeAddressIndex: snp?.changeAddressIndex,
|
||||
addressPageType: snp?.addressPageType,
|
||||
networkParam: network,
|
||||
alwaysScan: snp?.alwaysScan,
|
||||
useLightning: snp?.useLightning,
|
||||
cachedLightningAddress: snp?.cachedLightningAddress,
|
||||
payjoinBox: payjoinBox,
|
||||
mnemonic: mnemonic,
|
||||
xpub: keysData.xPub != null ? convertZpubToXpub(keysData.xPub!) : null,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
walletInfo: walletInfo,
|
||||
derivationInfo: derivationInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: snp?.addresses,
|
||||
initialSilentAddresses: snp?.silentAddresses,
|
||||
initialSilentAddressIndex: snp?.silentAddressIndex ?? 0,
|
||||
initialBalance: snp?.balance,
|
||||
initialLightningBalance: snp?.lightningBalance,
|
||||
encryptionFileUtils: encryptionFileUtils,
|
||||
seedBytes: seedBytes,
|
||||
initialRegularAddressIndex: snp?.regularAddressIndex,
|
||||
initialChangeAddressIndex: snp?.changeAddressIndex,
|
||||
addressPageType: snp?.addressPageType,
|
||||
networkParam: network,
|
||||
alwaysScan: snp?.alwaysScan,
|
||||
useLightning: snp?.useLightning,
|
||||
cachedLightningAddress: snp?.cachedLightningAddress,
|
||||
payjoinBox: payjoinBox,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -346,12 +346,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
}
|
||||
|
||||
try {
|
||||
final lBalance = await lightningWallet!.getBalance();
|
||||
final lBalance = await lightningWallet!.getBalance();
|
||||
|
||||
this.balance[CryptoCurrency.btcln] = ElectrumBalance(
|
||||
confirmed: lBalance,
|
||||
unconfirmed: Money.zero(CryptoCurrency.btcln),
|
||||
frozen: Money.zero(CryptoCurrency.btcln));
|
||||
this.balance[CryptoCurrency.btcln] = ElectrumBalance(
|
||||
confirmed: lBalance,
|
||||
unconfirmed: Money.zero(CryptoCurrency.btcln),
|
||||
frozen: Money.zero(CryptoCurrency.btcln));
|
||||
} catch (e) {
|
||||
printV("Error fetching lightning balance: $e");
|
||||
}
|
||||
@@ -494,7 +494,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
credentials = credentials as BitcoinTransactionCredentials;
|
||||
final lnAddr = credentials.outputs.first.isParsedAddress ? credentials.outputs.first.extractedAddress! : credentials.outputs.first.address;
|
||||
final lnAddr = credentials.outputs.first.isParsedAddress
|
||||
? credentials.outputs.first.extractedAddress!
|
||||
: credentials.outputs.first.address;
|
||||
|
||||
final isLNCompatible = await lightningWallet?.isCompatible(lnAddr);
|
||||
if ((credentials.coinTypeToSpendFrom == UnspentCoinType.lightning && lightningWallet != null) ||
|
||||
@@ -506,12 +508,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
amount = credentials.outputs.first.cryptoAmount;
|
||||
}
|
||||
|
||||
|
||||
return lightningWallet!.createTransaction(
|
||||
lnAddr,
|
||||
amount.amount > BigInt.zero ? amount.amount : null,
|
||||
credentials.priority,
|
||||
credentials.outputs.first.sendAll,);
|
||||
lnAddr,
|
||||
amount.amount > BigInt.zero ? amount.amount : null,
|
||||
credentials.priority,
|
||||
credentials.outputs.first.sendAll,
|
||||
);
|
||||
}
|
||||
|
||||
final tx = (await super.createTransaction(credentials)) as PendingBitcoinTransaction;
|
||||
|
||||
@@ -137,7 +137,8 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||
}
|
||||
|
||||
@override
|
||||
bool containsAddress(String address) => super.containsAddress(address) || address == lightningAddress;
|
||||
bool containsAddress(String address) =>
|
||||
super.containsAddress(address) || address == lightningAddress;
|
||||
|
||||
@override
|
||||
String get addressForBuy {
|
||||
@@ -153,12 +154,9 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||
|
||||
@override
|
||||
String get addressForExchange {
|
||||
|
||||
final current = getFreshAddress();
|
||||
final availableReceiveAddresses = receiveAddresses.where((element) =>
|
||||
!element.isUsed &&
|
||||
!element.isHidden &&
|
||||
!hiddenAddresses.contains(element.address));
|
||||
!element.isUsed && !element.isHidden && !hiddenAddresses.contains(element.address));
|
||||
|
||||
final bool isSilentPaymentsPage = addressPageType == SilentPaymentsAddresType.p2sp;
|
||||
final bool isLightningPage = addressPageType == LightningAddressType.p2l;
|
||||
|
||||
@@ -59,27 +59,27 @@ class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
|
||||
}
|
||||
|
||||
class BitcoinWalletFromKeysCredentials extends WalletCredentials {
|
||||
BitcoinWalletFromKeysCredentials({
|
||||
required String name,
|
||||
required String password,
|
||||
required this.xpub,
|
||||
WalletInfo? walletInfo,
|
||||
super.hardwareWalletType
|
||||
}) : super(name: name, password: password, walletInfo: walletInfo);
|
||||
BitcoinWalletFromKeysCredentials(
|
||||
{required String name,
|
||||
required String password,
|
||||
required this.xpub,
|
||||
WalletInfo? walletInfo,
|
||||
super.hardwareWalletType})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String xpub;
|
||||
}
|
||||
|
||||
class LitecoinWalletFromKeysCredentials extends WalletCredentials {
|
||||
LitecoinWalletFromKeysCredentials({
|
||||
required String name,
|
||||
required String password,
|
||||
required this.xpub,
|
||||
required this.scanSecret,
|
||||
required this.spendPubkey,
|
||||
WalletInfo? walletInfo,
|
||||
super.hardwareWalletType
|
||||
}) : super(name: name, password: password, walletInfo: walletInfo);
|
||||
LitecoinWalletFromKeysCredentials(
|
||||
{required String name,
|
||||
required String password,
|
||||
required this.xpub,
|
||||
required this.scanSecret,
|
||||
required this.spendPubkey,
|
||||
WalletInfo? walletInfo,
|
||||
super.hardwareWalletType})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String xpub;
|
||||
final String scanSecret;
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
class BitcoinWalletKeys {
|
||||
const BitcoinWalletKeys({required this.wif, required this.privateKey, required this.publicKey, required this.xpub});
|
||||
const BitcoinWalletKeys(
|
||||
{required this.wif, required this.privateKey, required this.publicKey, required this.xpub});
|
||||
|
||||
final String wif;
|
||||
final String privateKey;
|
||||
final String publicKey;
|
||||
final String xpub;
|
||||
|
||||
Map<String, String> toJson() => {
|
||||
'wif': wif,
|
||||
'privateKey': privateKey,
|
||||
'publicKey': publicKey,
|
||||
'xpub': xpub
|
||||
};
|
||||
}
|
||||
Map<String, String> toJson() =>
|
||||
{'wif': wif, 'privateKey': privateKey, 'publicKey': publicKey, 'xpub': xpub};
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@ class BitcoinWalletService extends WalletService<
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinWalletFromKeysCredentials,
|
||||
BitcoinRestoreWalletFromHardware> {
|
||||
BitcoinWalletService(this.unspentCoinsInfoSource,
|
||||
this.payjoinSessionSource, this.isDirect);
|
||||
BitcoinWalletService(this.unspentCoinsInfoSource, this.payjoinSessionSource, this.isDirect);
|
||||
|
||||
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
|
||||
final Box<PayjoinSession> payjoinSessionSource;
|
||||
@@ -38,9 +37,12 @@ class BitcoinWalletService extends WalletService<
|
||||
|
||||
final String mnemonic;
|
||||
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
|
||||
derivationInfo.derivationType = credentials.derivationInfo?.derivationType ?? derivationInfo.derivationType;
|
||||
derivationInfo.derivationPath = credentials.derivationInfo?.derivationPath ?? derivationInfo.derivationPath;
|
||||
derivationInfo.description = credentials.derivationInfo?.description ?? derivationInfo.description;
|
||||
derivationInfo.derivationType =
|
||||
credentials.derivationInfo?.derivationType ?? derivationInfo.derivationType;
|
||||
derivationInfo.derivationPath =
|
||||
credentials.derivationInfo?.derivationPath ?? derivationInfo.derivationPath;
|
||||
derivationInfo.description =
|
||||
credentials.derivationInfo?.description ?? derivationInfo.description;
|
||||
derivationInfo.scriptType = credentials.derivationInfo?.scriptType ?? derivationInfo.scriptType;
|
||||
await derivationInfo.save();
|
||||
switch (derivationInfo.derivationType) {
|
||||
@@ -119,8 +121,9 @@ class BitcoinWalletService extends WalletService<
|
||||
}
|
||||
await WalletInfo.delete(walletInfo);
|
||||
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
|
||||
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values
|
||||
.where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
|
||||
.toList();
|
||||
|
||||
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
|
||||
|
||||
@@ -135,11 +138,10 @@ class BitcoinWalletService extends WalletService<
|
||||
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
|
||||
credentials.walletInfo?.network = network.value;
|
||||
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
|
||||
derivationInfo.derivationPath =
|
||||
credentials.hwAccountData.derivationPath;
|
||||
|
||||
derivationInfo.derivationPath = credentials.hwAccountData.derivationPath;
|
||||
|
||||
final xpub = convertAnyToXpub(credentials.hwAccountData.xpub!);
|
||||
|
||||
|
||||
await credentials.walletInfo!.save();
|
||||
final wallet = await BitcoinWallet(
|
||||
password: credentials.password!,
|
||||
|
||||
@@ -304,9 +304,9 @@ class ElectrumClient {
|
||||
});
|
||||
|
||||
Future<Map<String, List<Map<String, dynamic>>>> getBatchHistory(
|
||||
List<String> scriptHashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
List<String> scriptHashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
final paramsList = scriptHashes.map((h) => <Object>[h]).toList(growable: false);
|
||||
|
||||
final batchResults = await callBatchWithTimeout(
|
||||
@@ -342,9 +342,9 @@ class ElectrumClient {
|
||||
}
|
||||
|
||||
Future<Map<String, List<Map<String, dynamic>>>> getBatchUnspent(
|
||||
List<String> scriptHashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
List<String> scriptHashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
final paramsList = scriptHashes.map((h) => <Object>[h]).toList(growable: false);
|
||||
|
||||
final batchResults = await callBatchWithTimeout(
|
||||
@@ -380,9 +380,9 @@ class ElectrumClient {
|
||||
}
|
||||
|
||||
Future<Map<String, Map<String, dynamic>>> getBatchBalance(
|
||||
List<String> scriptHashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
List<String> scriptHashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
final paramsList = scriptHashes.map((h) => <Object>[h]).toList(growable: false);
|
||||
|
||||
final batchResults = await callBatchWithTimeout(
|
||||
@@ -416,9 +416,9 @@ class ElectrumClient {
|
||||
}
|
||||
|
||||
Future<Map<String, Map<String, dynamic>>> getBatchTransactionVerbose(
|
||||
List<String> hashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
List<String> hashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
final result = <String, Map<String, dynamic>>{};
|
||||
if (hashes.isEmpty) return result;
|
||||
|
||||
@@ -443,9 +443,9 @@ class ElectrumClient {
|
||||
}
|
||||
|
||||
Future<Map<String, String?>> getBatchTransactionHex(
|
||||
List<String> hashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
List<String> hashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
final result = <String, String?>{};
|
||||
if (hashes.isEmpty) return result;
|
||||
|
||||
@@ -483,12 +483,8 @@ class ElectrumClient {
|
||||
// Build the Batch Array
|
||||
final List<Map<String, dynamic>> batchPayload = [];
|
||||
for (int i = 0; i < paramsList.length; i++) {
|
||||
batchPayload.add({
|
||||
"jsonrpc": "2.0",
|
||||
"method": method,
|
||||
"params": paramsList[i],
|
||||
"id": "$batchBaseId-$i"
|
||||
});
|
||||
batchPayload.add(
|
||||
{"jsonrpc": "2.0", "method": method, "params": paramsList[i], "id": "$batchBaseId-$i"});
|
||||
}
|
||||
|
||||
// Register the task
|
||||
@@ -775,7 +771,6 @@ class ElectrumClient {
|
||||
}
|
||||
|
||||
void _handleResponse(dynamic response) {
|
||||
|
||||
// Handle batch response
|
||||
if (response is List) {
|
||||
if (response.isEmpty) return;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
|
||||
@@ -184,15 +184,13 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||
|
||||
// MWEB HogEx
|
||||
final isHogExTx = (BtcTransaction tx) {
|
||||
if (tx.inputs.isEmpty || tx.inputs.first.txIndex > 0 || tx.outputs.isEmpty)
|
||||
return false;
|
||||
if (tx.inputs.isEmpty || tx.inputs.first.txIndex > 0 || tx.outputs.isEmpty) return false;
|
||||
final b = tx.outputs.first.scriptPubKey.toBytes();
|
||||
return b.length == 34 && b[0] == 88 && b[1] == 32;
|
||||
};
|
||||
final firstInput = bundle.ins.isNotEmpty ? bundle.ins.first : null;
|
||||
final isHogEx = firstInput != null &&
|
||||
isHogExTx(bundle.originalTransaction) &&
|
||||
isHogExTx(firstInput);
|
||||
final isHogEx =
|
||||
firstInput != null && isHogExTx(bundle.originalTransaction) && isHogExTx(firstInput);
|
||||
|
||||
final fee = hasMissingInputTx ? null : inputAmount - totalOutAmount;
|
||||
final walletCurrency = walletTypeToCryptoCurrency(type);
|
||||
|
||||
@@ -156,7 +156,6 @@ abstract class ElectrumWalletBase
|
||||
sideHdByType[type] = sideHd;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int _purposeForType(BitcoinAddressType type) {
|
||||
@@ -195,7 +194,6 @@ abstract class ElectrumWalletBase
|
||||
/// For LEGACY addresses, returns the wallet's legacy derivation base (derivationInfo.derivationPath)
|
||||
/// which is already the account path used historically (e.g. m/0' or m/84'/0'/0').
|
||||
String _accountDerivationPathForRecord(BaseBitcoinAddressRecord record) {
|
||||
|
||||
if (derivationInfo.derivationType == DerivationType.electrum) {
|
||||
return derivationInfo.derivationPath ?? electrum_path; // m/0'
|
||||
}
|
||||
@@ -807,7 +805,7 @@ abstract class ElectrumWalletBase
|
||||
node!.isElectrs = true;
|
||||
// TODO figure out why condition was needed
|
||||
// if (node!.isInBox) {
|
||||
node!.save();
|
||||
node!.save();
|
||||
// }
|
||||
return node!.isElectrs!;
|
||||
}
|
||||
@@ -873,7 +871,8 @@ abstract class ElectrumWalletBase
|
||||
|
||||
BigInt get networkDustAmount => BigInt.from(546);
|
||||
|
||||
bool _isBelowDust(BigInt amount) => amount <= networkDustAmount && network != BitcoinNetwork.testnet;
|
||||
bool _isBelowDust(BigInt amount) =>
|
||||
amount <= networkDustAmount && network != BitcoinNetwork.testnet;
|
||||
|
||||
UtxoDetails _createUTXOS({
|
||||
required bool sendAll,
|
||||
@@ -961,8 +960,7 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final baseDerivationPath = _accountDerivationPathForRecord(utx.bitcoinAddressRecord);
|
||||
|
||||
final derivationPath =
|
||||
"${_hardenedDerivationPath(baseDerivationPath)}"
|
||||
final derivationPath = "${_hardenedDerivationPath(baseDerivationPath)}"
|
||||
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
|
||||
"/${utx.bitcoinAddressRecord.index}";
|
||||
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||
@@ -1139,7 +1137,8 @@ abstract class ElectrumWalletBase
|
||||
utxoDetails.utxos.length ==
|
||||
utxoDetails.availableInputs.length - utxoDetails.unconfirmedCoins.length;
|
||||
|
||||
final amountLeftForChangeAndFee = utxoDetails.allInputsAmount - credentialsAmount.amount.toInt();
|
||||
final amountLeftForChangeAndFee =
|
||||
utxoDetails.allInputsAmount - credentialsAmount.amount.toInt();
|
||||
|
||||
if (amountLeftForChangeAndFee <= 0) {
|
||||
if (!spendingAllCoins) {
|
||||
@@ -1175,11 +1174,9 @@ abstract class ElectrumWalletBase
|
||||
isChange: true,
|
||||
));
|
||||
|
||||
|
||||
// Must match the address' account root (purpose/coinType) and legacy derivation when applicable.
|
||||
final changeBaseDerivationPath = _accountDerivationPathForRecord(changeAddress);
|
||||
final changeDerivationPath =
|
||||
"${_hardenedDerivationPath(changeBaseDerivationPath)}"
|
||||
final changeDerivationPath = "${_hardenedDerivationPath(changeBaseDerivationPath)}"
|
||||
"/${changeAddress.isHidden ? "1" : "0"}"
|
||||
"/${changeAddress.index}";
|
||||
utxoDetails.publicKeys[address.pubKeyHash()] =
|
||||
@@ -1242,7 +1239,8 @@ abstract class ElectrumWalletBase
|
||||
inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
|
||||
vinOutpoints: utxoDetails.vinOutpoints,
|
||||
);
|
||||
final leftover = utxoDetails.allInputsAmount - credentialsAmount.amount.toInt() - feeNoChange;
|
||||
final leftover =
|
||||
utxoDetails.allInputsAmount - credentialsAmount.amount.toInt() - feeNoChange;
|
||||
|
||||
if (leftover >= 0) {
|
||||
final finalFee = feeNoChange + leftover; // absorb tiny remainder
|
||||
@@ -1827,16 +1825,15 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
Future<List<List<BitcoinUnspent>?>> _fetchUnspentsRegular(
|
||||
List<BitcoinAddressRecord> addresses,
|
||||
) async {
|
||||
List<BitcoinAddressRecord> addresses,
|
||||
) async {
|
||||
final addressFutures = addresses.map((address) => fetchUnspent(address)).toList();
|
||||
return Future.wait(addressFutures);
|
||||
}
|
||||
|
||||
|
||||
Future<List<List<BitcoinUnspent>?>> _fetchUnspentsBatch(
|
||||
List<BitcoinAddressRecord> addresses,
|
||||
) async {
|
||||
List<BitcoinAddressRecord> addresses,
|
||||
) async {
|
||||
final byScriptHash = <String, BitcoinAddressRecord>{
|
||||
for (final address in addresses) address.getScriptHash(network): address,
|
||||
};
|
||||
@@ -1845,7 +1842,7 @@ abstract class ElectrumWalletBase
|
||||
|
||||
try {
|
||||
final unspentByScriptHash =
|
||||
await _processChunksToMap<String, String, List<Map<String, dynamic>>>(
|
||||
await _processChunksToMap<String, String, List<Map<String, dynamic>>>(
|
||||
items: scriptHashes,
|
||||
chunkSize: addressHistoryChunkSize,
|
||||
processChunk: _getListUnspentBatch,
|
||||
@@ -2131,10 +2128,7 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final hd = _hdFor(record: addressRecord);
|
||||
|
||||
final privkey = generateECPrivate(
|
||||
hd: hd,
|
||||
index: addressRecord.index,
|
||||
network: network);
|
||||
final privkey = generateECPrivate(hd: hd, index: addressRecord.index, network: network);
|
||||
|
||||
privateKeys.add(privkey);
|
||||
|
||||
@@ -2197,8 +2191,7 @@ abstract class ElectrumWalletBase
|
||||
final deduction = (outputAmount - networkDustAmount >= remainingFee)
|
||||
? remainingFee
|
||||
: outputAmount - networkDustAmount;
|
||||
outputs[i] = BitcoinOutput(
|
||||
address: output.address, value: outputAmount - deduction);
|
||||
outputs[i] = BitcoinOutput(address: output.address, value: outputAmount - deduction);
|
||||
remainingFee -= deduction;
|
||||
|
||||
if (remainingFee <= BigInt.zero) break;
|
||||
@@ -2464,7 +2457,8 @@ abstract class ElectrumWalletBase
|
||||
@override
|
||||
Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
|
||||
try {
|
||||
final Map<String, ElectrumTransactionInfo> historiesWithDetails = {};;
|
||||
final Map<String, ElectrumTransactionInfo> historiesWithDetails = {};
|
||||
;
|
||||
|
||||
printV('[BATCH_TEST] Fetching transactions with batch: $shouldUseBatchFetching');
|
||||
|
||||
@@ -2473,19 +2467,16 @@ abstract class ElectrumWalletBase
|
||||
? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type)
|
||||
: fetchTransactionsForAddressType(historiesWithDetails, type)));
|
||||
} else if (type == WalletType.bitcoinCash) {
|
||||
await Future.wait(BITCOIN_CASH_ADDRESS_TYPES
|
||||
.map((type) => shouldUseBatchFetching
|
||||
await Future.wait(BITCOIN_CASH_ADDRESS_TYPES.map((type) => shouldUseBatchFetching
|
||||
? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type)
|
||||
: fetchTransactionsForAddressType(historiesWithDetails, type)));
|
||||
} else if (type == WalletType.litecoin) {
|
||||
await Future.wait(LITECOIN_ADDRESS_TYPES
|
||||
.where((type) => type != SegwitAddresType.mweb)
|
||||
.map((type) => shouldUseBatchFetching
|
||||
? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type)
|
||||
: fetchTransactionsForAddressType(historiesWithDetails, type)));
|
||||
await Future.wait(LITECOIN_ADDRESS_TYPES.where((type) => type != SegwitAddresType.mweb).map(
|
||||
(type) => shouldUseBatchFetching
|
||||
? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type)
|
||||
: fetchTransactionsForAddressType(historiesWithDetails, type)));
|
||||
} else if (type == WalletType.dogecoin) {
|
||||
await Future.wait(DOGECOIN_ADDRESS_TYPES
|
||||
.map((type) => shouldUseBatchFetching
|
||||
await Future.wait(DOGECOIN_ADDRESS_TYPES.map((type) => shouldUseBatchFetching
|
||||
? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type)
|
||||
: fetchTransactionsForAddressType(historiesWithDetails, type)));
|
||||
}
|
||||
@@ -2633,8 +2624,7 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
Future<void> fetchTransactionsForAddressTypeBatch(
|
||||
Map<String, ElectrumTransactionInfo> historiesWithDetails,
|
||||
BitcoinAddressType type) async {
|
||||
Map<String, ElectrumTransactionInfo> historiesWithDetails, BitcoinAddressType type) async {
|
||||
final addressesByType =
|
||||
walletAddresses.allAddresses.where((addr) => addr.type == type).toList();
|
||||
final receiveAddresses = addressesByType.where((addr) => !addr.isHidden).toList();
|
||||
@@ -2676,14 +2666,13 @@ abstract class ElectrumWalletBase
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Future<void> fetchTransactionsForAddressesBranchBatch(
|
||||
Map<String, ElectrumTransactionInfo> historiesWithDetails,
|
||||
BitcoinAddressType type,
|
||||
List<BitcoinAddressRecord> branchAddresses, {
|
||||
required bool isHidden,
|
||||
required bool isLegacyDerivation,
|
||||
}) async {
|
||||
Map<String, ElectrumTransactionInfo> historiesWithDetails,
|
||||
BitcoinAddressType type,
|
||||
List<BitcoinAddressRecord> branchAddresses, {
|
||||
required bool isHidden,
|
||||
required bool isLegacyDerivation,
|
||||
}) async {
|
||||
if (branchAddresses.isEmpty) return;
|
||||
|
||||
final tip = await getCurrentChainTip();
|
||||
@@ -2714,7 +2703,6 @@ abstract class ElectrumWalletBase
|
||||
|
||||
if (!shouldDiscover) return;
|
||||
|
||||
|
||||
final newAddresses = await walletAddresses.discoverAddressesBatch(
|
||||
currentBranch,
|
||||
isHidden,
|
||||
@@ -2756,9 +2744,7 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
Future<Map<String, ElectrumTransactionInfo>> _fetchBatchAddressHistory(
|
||||
List<BitcoinAddressRecord> addressRecords,
|
||||
int? currentHeight,
|
||||
int historyChunkSize) async {
|
||||
List<BitcoinAddressRecord> addressRecords, int? currentHeight, int historyChunkSize) async {
|
||||
String lastTxId = '';
|
||||
bool didUpdateHistory = false;
|
||||
|
||||
@@ -2769,11 +2755,8 @@ abstract class ElectrumWalletBase
|
||||
final scriptHashes = addressRecords.map((a) => a.getScriptHash(network)).toList();
|
||||
|
||||
final historyByScriptHash =
|
||||
await _processChunksToMap<String, String, List<Map<String, dynamic>>>(
|
||||
items: scriptHashes,
|
||||
chunkSize: historyChunkSize,
|
||||
processChunk: _getHistoryBatch
|
||||
);
|
||||
await _processChunksToMap<String, String, List<Map<String, dynamic>>>(
|
||||
items: scriptHashes, chunkSize: historyChunkSize, processChunk: _getHistoryBatch);
|
||||
|
||||
// Map scriptHash -> addressRecord
|
||||
final byScriptHash = <String, BitcoinAddressRecord>{};
|
||||
@@ -2869,8 +2852,7 @@ abstract class ElectrumWalletBase
|
||||
.toList(growable: false);
|
||||
|
||||
final heightsByHash = <String, int?>{
|
||||
for (final e in chunkHistory)
|
||||
(e['tx_hash'] as String): (e['height'] as int?),
|
||||
for (final e in chunkHistory) (e['tx_hash'] as String): (e['height'] as int?),
|
||||
};
|
||||
|
||||
final infosByHash = await fetchTransactionInfoBatch(
|
||||
@@ -2909,40 +2891,35 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, Map<String, dynamic>>> _getTransactionVerboseBatch(
|
||||
List<String> hashes) {
|
||||
Future<Map<String, Map<String, dynamic>>> _getTransactionVerboseBatch(List<String> hashes) {
|
||||
return electrumClient.getBatchTransactionVerbose(
|
||||
hashes,
|
||||
timeout: transactionBatchTimeoutMs,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, String?>> _getTransactionHexBatch(
|
||||
List<String> hashes) {
|
||||
Future<Map<String, String?>> _getTransactionHexBatch(List<String> hashes) {
|
||||
return electrumClient.getBatchTransactionHex(
|
||||
hashes,
|
||||
timeout: transactionBatchTimeoutMs,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, List<Map<String, dynamic>>>> _getHistoryBatch(
|
||||
List<String> scriptHashes) {
|
||||
Future<Map<String, List<Map<String, dynamic>>>> _getHistoryBatch(List<String> scriptHashes) {
|
||||
return electrumClient.getBatchHistory(
|
||||
scriptHashes,
|
||||
timeout: transactionBatchTimeoutMs,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, List<Map<String, dynamic>>>> _getListUnspentBatch(
|
||||
List<String> scriptHashes) {
|
||||
Future<Map<String, List<Map<String, dynamic>>>> _getListUnspentBatch(List<String> scriptHashes) {
|
||||
return electrumClient.getBatchUnspent(
|
||||
scriptHashes,
|
||||
timeout: transactionBatchTimeoutMs,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, Map<String, dynamic>>> _getBalanceBatch(
|
||||
List<String> scriptHashes) {
|
||||
Future<Map<String, Map<String, dynamic>>> _getBalanceBatch(List<String> scriptHashes) {
|
||||
return electrumClient.getBatchBalance(
|
||||
scriptHashes,
|
||||
timeout: transactionBatchTimeoutMs,
|
||||
@@ -2956,8 +2933,7 @@ abstract class ElectrumWalletBase
|
||||
Duration retryDelay = const Duration(seconds: 2),
|
||||
}) async {
|
||||
final result = <String, ElectrumTransactionInfo?>{};
|
||||
final uniqueHashes =
|
||||
hashes.map((h) => h.trim()).where((h) => h.isNotEmpty).toSet().toList();
|
||||
final uniqueHashes = hashes.map((h) => h.trim()).where((h) => h.isNotEmpty).toSet().toList();
|
||||
|
||||
if (uniqueHashes.isEmpty) return result;
|
||||
|
||||
@@ -2990,9 +2966,8 @@ abstract class ElectrumWalletBase
|
||||
required Map<String, int?>? heightsByHash,
|
||||
}) async {
|
||||
for (var i = 0; i < txIds.length; i += transactionChunkSize) {
|
||||
final end = (i + transactionChunkSize < txIds.length)
|
||||
? i + transactionChunkSize
|
||||
: txIds.length;
|
||||
final end =
|
||||
(i + transactionChunkSize < txIds.length) ? i + transactionChunkSize : txIds.length;
|
||||
final chunk = txIds.sublist(i, end);
|
||||
|
||||
final bundlesByHash = await getTransactionExpandedBatch(
|
||||
@@ -3024,9 +2999,8 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, ElectrumTransactionBundle>> getTransactionExpandedBatch({
|
||||
required List<String> hashes,
|
||||
Map<String, int?>? heightsByHash}) async {
|
||||
Future<Map<String, ElectrumTransactionBundle>> getTransactionExpandedBatch(
|
||||
{required List<String> hashes, Map<String, int?>? heightsByHash}) async {
|
||||
final bundles = <String, ElectrumTransactionBundle>{};
|
||||
if (hashes.isEmpty) return bundles;
|
||||
|
||||
@@ -3062,8 +3036,8 @@ abstract class ElectrumWalletBase
|
||||
|
||||
Future<Map<String, Map<String, dynamic>>> _fetchTransactionVerboseBatch(
|
||||
List<String> txIds) async {
|
||||
|
||||
final verboseTransactionByHash = await _processChunksToMap<String, String, Map<String, dynamic>>(
|
||||
final verboseTransactionByHash =
|
||||
await _processChunksToMap<String, String, Map<String, dynamic>>(
|
||||
items: txIds,
|
||||
chunkSize: transactionChunkSize,
|
||||
processChunk: _getTransactionVerboseBatch,
|
||||
@@ -3100,8 +3074,8 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
Map<String, BtcTransaction> _parseTransactions(
|
||||
Map<String, Map<String, dynamic>> verboseByHash,
|
||||
) {
|
||||
Map<String, Map<String, dynamic>> verboseByHash,
|
||||
) {
|
||||
final result = <String, BtcTransaction>{};
|
||||
|
||||
for (final entry in verboseByHash.entries) {
|
||||
@@ -3116,10 +3090,9 @@ abstract class ElectrumWalletBase
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Map<String, List<String>> _collectInputTxIdsByHash(
|
||||
Map<String, BtcTransaction> originalByHash,
|
||||
) {
|
||||
Map<String, BtcTransaction> originalByHash,
|
||||
) {
|
||||
final inputTxIdsByHash = <String, List<String>>{};
|
||||
|
||||
for (final entry in originalByHash.entries) {
|
||||
@@ -3201,8 +3174,8 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
Future<Map<int, int>> _fetchBlockTimestampsFromMempoolByHeights(
|
||||
Set<int> heights,
|
||||
) async {
|
||||
Set<int> heights,
|
||||
) async {
|
||||
final out = <int, int>{};
|
||||
if (heights.isEmpty) return out;
|
||||
if (!(await checkIfMempoolAPIIsEnabled())) return out;
|
||||
@@ -3212,10 +3185,10 @@ abstract class ElectrumWalletBase
|
||||
try {
|
||||
final blockHashResp = await ProxyWrapper()
|
||||
.get(
|
||||
clearnetUri: Uri.parse(
|
||||
'https://mempool.cakewallet.com/api/v1/block-height/$h',
|
||||
),
|
||||
)
|
||||
clearnetUri: Uri.parse(
|
||||
'https://mempool.cakewallet.com/api/v1/block-height/$h',
|
||||
),
|
||||
)
|
||||
.timeout(const Duration(seconds: 15));
|
||||
|
||||
if (blockHashResp.statusCode != 200 || blockHashResp.body.isEmpty) return;
|
||||
@@ -3225,10 +3198,10 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final blockResp = await ProxyWrapper()
|
||||
.get(
|
||||
clearnetUri: Uri.parse(
|
||||
'https://mempool.cakewallet.com/api/v1/block/$blockHash',
|
||||
),
|
||||
)
|
||||
clearnetUri: Uri.parse(
|
||||
'https://mempool.cakewallet.com/api/v1/block/$blockHash',
|
||||
),
|
||||
)
|
||||
.timeout(const Duration(seconds: 15));
|
||||
|
||||
if (blockResp.statusCode != 200 || blockResp.body.isEmpty) return;
|
||||
@@ -3276,7 +3249,6 @@ abstract class ElectrumWalletBase
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Future<void> updateTransactions() async {
|
||||
printV("updateTransactions() called!");
|
||||
try {
|
||||
@@ -3368,8 +3340,7 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
try {
|
||||
final balancesByScriptHash =
|
||||
await _processChunksToMap<String, String, Map<String, dynamic>>(
|
||||
final balancesByScriptHash = await _processChunksToMap<String, String, Map<String, dynamic>>(
|
||||
items: scriptHashes,
|
||||
chunkSize: addressHistoryChunkSize,
|
||||
processChunk: _getBalanceBatch,
|
||||
@@ -3415,7 +3386,8 @@ abstract class ElectrumWalletBase
|
||||
? await fetchBalancesBatch(addresses)
|
||||
: await fetchBalancesRegular(addresses);
|
||||
|
||||
printV('Fetched balances for ${addresses.length} addresses. Batch fetching: $shouldUseBatchFetching');
|
||||
printV(
|
||||
'Fetched balances for ${addresses.length} addresses. Batch fetching: $shouldUseBatchFetching');
|
||||
|
||||
var totalFrozen = 0;
|
||||
var totalConfirmed = 0;
|
||||
@@ -3544,17 +3516,14 @@ abstract class ElectrumWalletBase
|
||||
// that matches this received transaction, mark it as being from a peg out:
|
||||
for (final tx2 in transactionHistory.transactions.values) {
|
||||
final heightDiff = ((tx2.height ?? 0) - (tx.height ?? 0)).abs();
|
||||
// this isn't a perfect matching algorithm since we don't have the right input/output information from these transaction models (the addresses are in different formats), but this should be more than good enough for now as it's extremely unlikely a user receives the EXACT same amount from 2 different sources and one of them is a peg out and the other isn't WITHIN 5 blocks of each other
|
||||
if (tx2.additionalInfo["isPegOut"] == true &&
|
||||
tx2.amount == tx.amount &&
|
||||
heightDiff <= 5) {
|
||||
// this isn't a perfect matching algorithm since we don't have the right input/output information from these transaction models (the addresses are in different formats), but this should be more than good enough for now as it's extremely unlikely a user receives the EXACT same amount from 2 different sources and one of them is a peg out and the other isn't WITHIN 5 blocks of each other
|
||||
if (tx2.additionalInfo["isPegOut"] == true && tx2.amount == tx.amount && heightDiff <= 5) {
|
||||
tx.additionalInfo["fromPegOut"] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkIfBatchSupported() async {
|
||||
|
||||
if (_isBatchSupported != null) {
|
||||
printV('[BATCH_TEST] Already checked: $_isBatchSupported');
|
||||
return;
|
||||
@@ -3580,9 +3549,7 @@ abstract class ElectrumWalletBase
|
||||
);
|
||||
|
||||
final hasError = result.any((item) =>
|
||||
item is Map<String, dynamic> &&
|
||||
item.containsKey('error') &&
|
||||
item['error'] != null);
|
||||
item is Map<String, dynamic> && item.containsKey('error') && item['error'] != null);
|
||||
|
||||
if (hasError) {
|
||||
_isBatchSupported = false;
|
||||
|
||||
@@ -195,13 +195,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
}
|
||||
|
||||
if (addressPageType == LightningAddressType.p2l) {
|
||||
return lightningAddress ??
|
||||
"Error: Unable to fetch your Lightning address, please check your network connection.";
|
||||
return lightningAddress ??
|
||||
"Error: Unable to fetch your Lightning address, please check your network connection.";
|
||||
}
|
||||
|
||||
final typeMatchingAddressesAll = _addresses
|
||||
.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr))
|
||||
.toList();
|
||||
final typeMatchingAddressesAll =
|
||||
_addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
|
||||
|
||||
// Prefer standard derivation addresses for the current/active address,
|
||||
// but keep legacy addresses present in the overall address lists.
|
||||
@@ -210,8 +209,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
...typeMatchingAddressesAll.where((a) => a.isLegacyDerivation),
|
||||
];
|
||||
|
||||
final typeMatchingReceiveAddressesAll =
|
||||
typeMatchingAddressesAll.where((addr) => !addr.isUsed && !hiddenAddresses.contains(addr.address)).toList();
|
||||
final typeMatchingReceiveAddressesAll = typeMatchingAddressesAll
|
||||
.where((addr) => !addr.isUsed && !hiddenAddresses.contains(addr.address))
|
||||
.toList();
|
||||
final typeMatchingReceiveAddresses = <BitcoinAddressRecord>[
|
||||
...typeMatchingReceiveAddressesAll.where((a) => !a.isLegacyDerivation),
|
||||
...typeMatchingReceiveAddressesAll.where((a) => a.isLegacyDerivation),
|
||||
@@ -354,34 +354,35 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
return acc;
|
||||
});
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
if (walletInfo.type == WalletType.bitcoinCash) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
} else if (walletInfo.type == WalletType.litecoin) {
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
|
||||
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
|
||||
await _generateInitialAddresses(type: SegwitAddresType.mweb);
|
||||
}
|
||||
} else if (walletInfo.type == WalletType.dogecoin) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
} else if (walletInfo.type == WalletType.bitcoin) {
|
||||
await _generateInitialAddresses(isLegacyDerivation: true);
|
||||
await _generateInitialAddresses();
|
||||
if (!isHardwareWallet) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
|
||||
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2tr, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wsh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||
}
|
||||
@override
|
||||
Future<void> init() async {
|
||||
if (walletInfo.type == WalletType.bitcoinCash) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
} else if (walletInfo.type == WalletType.litecoin) {
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
|
||||
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
|
||||
await _generateInitialAddresses(type: SegwitAddresType.mweb);
|
||||
}
|
||||
} else if (walletInfo.type == WalletType.dogecoin) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
} else if (walletInfo.type == WalletType.bitcoin) {
|
||||
await _generateInitialAddresses(isLegacyDerivation: true);
|
||||
await _generateInitialAddresses();
|
||||
if (!isHardwareWallet) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
|
||||
await _generateInitialAddresses(
|
||||
type: P2shAddressType.p2wpkhInP2sh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2tr, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wsh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||
}
|
||||
}
|
||||
|
||||
updateAddressesByMatch();
|
||||
updateReceiveAddresses();
|
||||
@@ -680,9 +681,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
@action
|
||||
void updateReceiveAddresses() {
|
||||
receiveAddresses.removeRange(0, receiveAddresses.length);
|
||||
final newAddresses = _addresses.where((addressRecord) =>
|
||||
!addressRecord.isHidden &&
|
||||
!addressRecord.isUsed);
|
||||
final newAddresses =
|
||||
_addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
|
||||
receiveAddresses.addAll(newAddresses);
|
||||
}
|
||||
|
||||
@@ -699,12 +699,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
|
||||
@action
|
||||
Future<void> discoverAddresses(
|
||||
List<BitcoinAddressRecord> addressList,
|
||||
bool isHidden,
|
||||
Future<String?> Function(BitcoinAddressRecord) getAddressHistory, {
|
||||
BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
required bool isLegacyDerivation,
|
||||
}) async {
|
||||
List<BitcoinAddressRecord> addressList,
|
||||
bool isHidden,
|
||||
Future<String?> Function(BitcoinAddressRecord) getAddressHistory, {
|
||||
BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
required bool isLegacyDerivation,
|
||||
}) async {
|
||||
final newAddresses = await _createNewAddresses(
|
||||
gap,
|
||||
startIndex: addressList.length,
|
||||
@@ -716,8 +716,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
addAddresses(newAddresses);
|
||||
addressList.addAll(newAddresses);
|
||||
|
||||
final addressesWithHistory =
|
||||
await Future.wait(newAddresses.map(getAddressHistory));
|
||||
final addressesWithHistory = await Future.wait(newAddresses.map(getAddressHistory));
|
||||
final isLastAddressUsed = addressesWithHistory.last != null;
|
||||
|
||||
if (isLastAddressUsed) {
|
||||
@@ -733,12 +732,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
|
||||
@action
|
||||
Future<List<BitcoinAddressRecord>> discoverAddressesBatch(
|
||||
List<BitcoinAddressRecord> addressList,
|
||||
bool isHidden,
|
||||
Future<Set<String>> Function(List<BitcoinAddressRecord>) getUsedAddresses, {
|
||||
BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
required bool isLegacyDerivation,
|
||||
}) async {
|
||||
List<BitcoinAddressRecord> addressList,
|
||||
bool isHidden,
|
||||
Future<Set<String>> Function(List<BitcoinAddressRecord>) getUsedAddresses, {
|
||||
BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
required bool isLegacyDerivation,
|
||||
}) async {
|
||||
final newAddresses = await _createNewAddresses(
|
||||
gap,
|
||||
startIndex: addressList.length,
|
||||
@@ -750,8 +749,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
|
||||
final usedAddresses = await getUsedAddresses(newAddresses);
|
||||
|
||||
final hasUsedAddressInGap = newAddresses.any(
|
||||
(addressRecord) => usedAddresses.contains(addressRecord.address));
|
||||
final hasUsedAddressInGap =
|
||||
newAddresses.any((addressRecord) => usedAddresses.contains(addressRecord.address));
|
||||
|
||||
if (!hasUsedAddressInGap) {
|
||||
return newAddresses;
|
||||
@@ -770,64 +769,69 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
return [...newAddresses, ...moreNewAddresses];
|
||||
}
|
||||
|
||||
|
||||
Future<void> _generateInitialAddresses(
|
||||
{BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
bool isLegacyDerivation = false }) async {
|
||||
|
||||
// Legacy derivation produces the same addresses as standard for these types.
|
||||
// Don't generate a legacy set to avoid duplicates.
|
||||
if (isLegacyDerivation && (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var countOfReceiveAddresses = 0;
|
||||
var countOfHiddenAddresses = 0;
|
||||
|
||||
_addresses.forEach((addr) {
|
||||
if (addr.type == type && addr.isLegacyDerivation == isLegacyDerivation) {
|
||||
if (addr.isHidden) {
|
||||
countOfHiddenAddresses += 1;
|
||||
} else {
|
||||
countOfReceiveAddresses += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
||||
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfReceiveAddresses, isHidden: false, type: type, isLegacyDerivation: isLegacyDerivation);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
|
||||
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
||||
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfHiddenAddresses, isHidden: true, type: type, isLegacyDerivation: isLegacyDerivation);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
{BitcoinAddressType type = SegwitAddresType.p2wpkh, bool isLegacyDerivation = false}) async {
|
||||
// Legacy derivation produces the same addresses as standard for these types.
|
||||
// Don't generate a legacy set to avoid duplicates.
|
||||
if (isLegacyDerivation && (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
|
||||
{int startIndex = 0, bool isHidden = false, BitcoinAddressType? type, bool isLegacyDerivation = false}) async {
|
||||
final list = <BitcoinAddressRecord>[];
|
||||
var countOfReceiveAddresses = 0;
|
||||
var countOfHiddenAddresses = 0;
|
||||
|
||||
for (var i = startIndex; i < count + startIndex; i++) {
|
||||
|
||||
final addrType = type ?? addressPageType;
|
||||
final hd = _hdFor(isHidden: isHidden, type: addrType, isLegacyDerivation: isLegacyDerivation);
|
||||
|
||||
final address = BitcoinAddressRecord(
|
||||
await getAddressAsync(index: i, hd: hd, addressType: addrType),
|
||||
index: i,
|
||||
isHidden: isHidden,
|
||||
isLegacyDerivation: isLegacyDerivation,
|
||||
type: addrType,
|
||||
network: network,
|
||||
);
|
||||
list.add(address);
|
||||
_addresses.forEach((addr) {
|
||||
if (addr.type == type && addr.isLegacyDerivation == isLegacyDerivation) {
|
||||
if (addr.isHidden) {
|
||||
countOfHiddenAddresses += 1;
|
||||
} else {
|
||||
countOfReceiveAddresses += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
||||
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfReceiveAddresses,
|
||||
isHidden: false,
|
||||
type: type,
|
||||
isLegacyDerivation: isLegacyDerivation);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
|
||||
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
||||
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfHiddenAddresses,
|
||||
isHidden: true,
|
||||
type: type,
|
||||
isLegacyDerivation: isLegacyDerivation);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
|
||||
{int startIndex = 0,
|
||||
bool isHidden = false,
|
||||
BitcoinAddressType? type,
|
||||
bool isLegacyDerivation = false}) async {
|
||||
final list = <BitcoinAddressRecord>[];
|
||||
|
||||
for (var i = startIndex; i < count + startIndex; i++) {
|
||||
final addrType = type ?? addressPageType;
|
||||
final hd = _hdFor(isHidden: isHidden, type: addrType, isLegacyDerivation: isLegacyDerivation);
|
||||
|
||||
final address = BitcoinAddressRecord(
|
||||
await getAddressAsync(index: i, hd: hd, addressType: addrType),
|
||||
index: i,
|
||||
isHidden: isHidden,
|
||||
isLegacyDerivation: isLegacyDerivation,
|
||||
type: addrType,
|
||||
network: network,
|
||||
);
|
||||
list.add(address);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
@@ -868,8 +872,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
return;
|
||||
}
|
||||
|
||||
final mainHd = _hdFor(isHidden: false, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
|
||||
final sideHd = _hdFor(isHidden: true, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
|
||||
final mainHd = _hdFor(
|
||||
isHidden: false, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
|
||||
final sideHd = _hdFor(
|
||||
isHidden: true, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
|
||||
if (!element.isHidden &&
|
||||
element.address !=
|
||||
await getAddressAsync(index: element.index, hd: mainHd, addressType: element.type)) {
|
||||
@@ -897,7 +903,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
return _isAddressByType(addressRecord, addressPageType);
|
||||
}
|
||||
|
||||
bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
|
||||
bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
|
||||
|
||||
bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) =>
|
||||
!addr.isHidden && !addr.isUsed && addr.type == type;
|
||||
@@ -907,9 +913,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
final addressRecord = silentAddresses.firstWhere((addressRecord) =>
|
||||
addressRecord.type == SilentPaymentsAddresType.p2sp && addressRecord.address == address);
|
||||
|
||||
silentAddresses.remove(addressRecord);
|
||||
updateAddressesByMatch();
|
||||
}
|
||||
silentAddresses.remove(addressRecord);
|
||||
updateAddressesByMatch();
|
||||
}
|
||||
|
||||
Bip32Slip10Secp256k1 _hdFor({
|
||||
required bool isHidden,
|
||||
@@ -923,7 +929,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
if (hd == null) throw Exception("HD not found for type $type");
|
||||
return hd;
|
||||
}
|
||||
|
||||
|
||||
@action
|
||||
Future<void> setLightningAddress(String walletName, {String newAddress = ""}) async {
|
||||
if (lightningWallet == null) return;
|
||||
|
||||
@@ -30,7 +30,7 @@ class BitcoinTransactionCommitFailed extends TransactionCommitFailed {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return errorMessage??"unknown error";
|
||||
return errorMessage ?? "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,8 @@ class BitcoinBitboxService extends HardwareWalletService with BitcoinHardwareWal
|
||||
Future<Uint8List> getMasterFingerprint() => manager.getMasterFingerprint();
|
||||
}
|
||||
|
||||
class LitecoinBitboxService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
|
||||
class LitecoinBitboxService extends HardwareWalletService
|
||||
with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
|
||||
LitecoinBitboxService(this.manager);
|
||||
|
||||
final BitboxManager manager;
|
||||
|
||||
@@ -12,7 +12,8 @@ import 'package:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
|
||||
import 'package:ledger_litecoin/ledger_litecoin.dart';
|
||||
|
||||
class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
|
||||
class LitecoinLedgerService extends HardwareWalletService
|
||||
with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
|
||||
LitecoinLedgerService(this.ledgerConnection)
|
||||
: litecoinLedgerApp = LitecoinLedgerApp(ledgerConnection);
|
||||
|
||||
@@ -53,7 +54,6 @@ class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWa
|
||||
required List<PSBTReadyUtxoWithAddress> inputs,
|
||||
required Map<String, PublicKeyWithDerivationPath> publicKeys,
|
||||
}) {
|
||||
|
||||
final readyInputs = <LedgerTransaction>[];
|
||||
for (final utxo in inputs) {
|
||||
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
|
||||
@@ -77,7 +77,7 @@ class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWa
|
||||
inputs: readyInputs,
|
||||
outputs: outputs
|
||||
.map((e) => TransactionOutput.fromBigInt((e as BitcoinOutput).value,
|
||||
Uint8List.fromList(e.address.toScriptPubKey().toBytes())))
|
||||
Uint8List.fromList(e.address.toScriptPubKey().toBytes())))
|
||||
.toList(),
|
||||
changePath: changePath,
|
||||
sigHashType: 0x01,
|
||||
|
||||
@@ -90,8 +90,7 @@ class LightningWallet {
|
||||
lnurlDomain: lnurlDomain,
|
||||
apiKey: apiKey,
|
||||
privateEnabledDefault: true,
|
||||
maxDepositClaimFee: MaxFee.rate(satPerVbyte: BigInt.from(5))
|
||||
);
|
||||
maxDepositClaimFee: MaxFee.rate(satPerVbyte: BigInt.from(5)));
|
||||
|
||||
final connectRequest = ConnectRequest(
|
||||
config: config,
|
||||
@@ -105,8 +104,7 @@ class LightningWallet {
|
||||
_logStream ??= initLogging().asBroadcastStream();
|
||||
|
||||
try {
|
||||
final logFile = File("$appPath/lightning.log")
|
||||
..createSync();
|
||||
final logFile = File("$appPath/lightning.log")..createSync();
|
||||
_subscribeToLogStream(logFile);
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
@@ -198,15 +196,17 @@ class LightningWallet {
|
||||
}
|
||||
}
|
||||
|
||||
Future<PendingLightningTransaction> createTransaction(
|
||||
String address, BigInt? amountSats, BitcoinTransactionPriority? priority, bool feesIncluded) async {
|
||||
Future<PendingLightningTransaction> createTransaction(String address, BigInt? amountSats,
|
||||
BitcoinTransactionPriority? priority, bool feesIncluded) async {
|
||||
final inputType = await sdk.parse(input: address);
|
||||
|
||||
final feePolicy = feesIncluded ? FeePolicy.feesIncluded : FeePolicy.feesExcluded;
|
||||
|
||||
if (inputType is InputType_Bolt11Invoice) {
|
||||
final request = PrepareSendPaymentRequest(
|
||||
paymentRequest: inputType.field0.invoice.bolt11, amount: amountSats, feePolicy: feePolicy);
|
||||
paymentRequest: inputType.field0.invoice.bolt11,
|
||||
amount: amountSats,
|
||||
feePolicy: feePolicy);
|
||||
final prepareResponse = await sdk.prepareSendPayment(request: request);
|
||||
|
||||
final paymentMethod = prepareResponse.paymentMethod;
|
||||
@@ -392,14 +392,16 @@ class LightningWallet {
|
||||
return _getElectrumTransactionInfoFromPayment(response.payment);
|
||||
}
|
||||
|
||||
Future<String> refundDeposit(String txId, int vout, String destinationAddress,
|
||||
BigInt feeRate) async {
|
||||
final response = await sdk.refundDeposit(request: RefundDepositRequest(
|
||||
txid: txId,
|
||||
vout: vout,
|
||||
destinationAddress: destinationAddress,
|
||||
fee: Fee.rate(satPerVbyte: feeRate),
|
||||
),);
|
||||
Future<String> refundDeposit(
|
||||
String txId, int vout, String destinationAddress, BigInt feeRate) async {
|
||||
final response = await sdk.refundDeposit(
|
||||
request: RefundDepositRequest(
|
||||
txid: txId,
|
||||
vout: vout,
|
||||
destinationAddress: destinationAddress,
|
||||
fee: Fee.rate(satPerVbyte: feeRate),
|
||||
),
|
||||
);
|
||||
|
||||
return response.txHex;
|
||||
}
|
||||
|
||||
@@ -10,10 +10,9 @@ class PendingLightningTransaction with PendingTransaction {
|
||||
required this.commitOverride,
|
||||
});
|
||||
|
||||
|
||||
final bool isSendAll;
|
||||
Future<String> Function() commitOverride;
|
||||
final List<void Function()> _listeners =[];
|
||||
final List<void Function()> _listeners = [];
|
||||
|
||||
@override
|
||||
String id;
|
||||
|
||||
@@ -175,7 +175,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
@override
|
||||
bool get hasRescan => true;
|
||||
|
||||
|
||||
final String? scanSecretOverride;
|
||||
final String? spendPubkeyOverride;
|
||||
List<int> get scanSecret => (scanSecretOverride != null && scanSecretOverride?.isNotEmpty == true)
|
||||
@@ -232,8 +231,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
}
|
||||
|
||||
@override
|
||||
WalletKeysData get walletKeysData =>
|
||||
WalletKeysData(mnemonic: seed, xPub: xpub, passphrase: passphrase, scanSecret: scanSecretOverride, spendPubkey: spendPubkeyOverride);
|
||||
WalletKeysData get walletKeysData => WalletKeysData(
|
||||
mnemonic: seed,
|
||||
xPub: xpub,
|
||||
passphrase: passphrase,
|
||||
scanSecret: scanSecretOverride,
|
||||
spendPubkey: spendPubkeyOverride);
|
||||
|
||||
static Future<LitecoinWallet> open({
|
||||
required String name,
|
||||
@@ -1106,15 +1109,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
for (final utxo in transaction.utxos) {
|
||||
if (utxo.utxo.scriptType != SegwitAddresType.mweb) {
|
||||
inputs.add(utxo.utxo.toInput());
|
||||
txouts.add(TxOut(value: Int64(utxo.utxo.value.toInt()),
|
||||
pkScript: utxo.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
txouts.add(TxOut(
|
||||
value: Int64(utxo.utxo.value.toInt()),
|
||||
pkScript: utxo.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
}
|
||||
}
|
||||
var resp = await CwMweb.psbtCreate(PsbtCreateRequest(
|
||||
rawTx: inputs.isEmpty ? null : BtcTransaction(
|
||||
inputs: inputs,
|
||||
outputs: isMweb ? [] : transaction.outputs,
|
||||
).toBytes(),
|
||||
rawTx: inputs.isEmpty
|
||||
? null
|
||||
: BtcTransaction(
|
||||
inputs: inputs,
|
||||
outputs: isMweb ? [] : transaction.outputs,
|
||||
).toBytes(),
|
||||
witnessUtxo: txouts,
|
||||
));
|
||||
for (final utxo in transaction.utxos) {
|
||||
@@ -1127,17 +1133,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
));
|
||||
}
|
||||
}
|
||||
if (isMweb) for (final output in transaction.outputs) {
|
||||
var address = addressFromOutputScript(output.scriptPubKey, LitecoinNetwork.mainnet);
|
||||
if (output.scriptPubKey.getAddressType() == SegwitAddresType.mweb) {
|
||||
address = SegwitBech32Encoder.encode("ltcmweb", 0, output.scriptPubKey.toBytes());
|
||||
if (isMweb)
|
||||
for (final output in transaction.outputs) {
|
||||
var address = addressFromOutputScript(output.scriptPubKey, LitecoinNetwork.mainnet);
|
||||
if (output.scriptPubKey.getAddressType() == SegwitAddresType.mweb) {
|
||||
address = SegwitBech32Encoder.encode("ltcmweb", 0, output.scriptPubKey.toBytes());
|
||||
}
|
||||
resp = await CwMweb.psbtAddRecipient(PsbtAddRecipientRequest(
|
||||
psbtB64: resp.psbtB64,
|
||||
recipient: PsbtRecipient(address: address, value: Int64(output.amount.toInt())),
|
||||
feeRatePerKb: Int64.parseInt(transaction.feeRate) * 1000,
|
||||
));
|
||||
}
|
||||
resp = await CwMweb.psbtAddRecipient(PsbtAddRecipientRequest(
|
||||
psbtB64: resp.psbtB64,
|
||||
recipient: PsbtRecipient(address: address, value: Int64(output.amount.toInt())),
|
||||
feeRatePerKb: Int64.parseInt(transaction.feeRate) * 1000,
|
||||
));
|
||||
}
|
||||
return base64.decode(resp.psbtB64);
|
||||
}
|
||||
|
||||
@@ -1203,7 +1210,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
for (final utxo in tx.utxos) {
|
||||
if (utxo.utxo.scriptType == SegwitAddresType.mweb) {
|
||||
hasMwebInput = true;
|
||||
|
||||
} else {
|
||||
// check if any of the inputs of this transaction are hog-ex:
|
||||
// this list is only non-mweb inputs:
|
||||
@@ -1256,9 +1262,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
final utxo = unspentCoins
|
||||
.firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex);
|
||||
final key = generateECPrivate(
|
||||
hd: utxo.bitcoinAddressRecord.isHidden
|
||||
? sideHd
|
||||
: mainHd,
|
||||
hd: utxo.bitcoinAddressRecord.isHidden ? sideHd : mainHd,
|
||||
index: utxo.bitcoinAddressRecord.index,
|
||||
network: network);
|
||||
final digest = tx2.getTransactionSegwitDigit(
|
||||
@@ -1284,8 +1288,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
}
|
||||
}
|
||||
|
||||
void addTransactionListener(PendingBitcoinTransaction tx,
|
||||
List<String> inputAddresses, bool isPegIn, bool isPegOut) {
|
||||
void addTransactionListener(
|
||||
PendingBitcoinTransaction tx, List<String> inputAddresses, bool isPegIn, bool isPegOut) {
|
||||
tx.addListener((transaction) async {
|
||||
final addresses = <String>{};
|
||||
transaction.inputAddresses?.addAll(inputAddresses);
|
||||
@@ -1575,7 +1579,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
rawTx: rawTx,
|
||||
ownerDetails: utxo.ownerDetails,
|
||||
ownerDerivationPath: publicKeyAndDerivationPath.derivationPath,
|
||||
ownerMasterFingerprint:masterFingerprint,
|
||||
ownerMasterFingerprint: masterFingerprint,
|
||||
ownerPublicKey: publicKeyAndDerivationPath.publicKey,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -59,9 +59,11 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||
? hex.decode(scanSecretOverride!)
|
||||
: mwebHd?.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw ?? List.filled(32, 0);
|
||||
|
||||
List<int> get spendPubkey => (spendPubkeyOverride != null && spendPubkeyOverride?.isNotEmpty == true)
|
||||
? hex.decode(spendPubkeyOverride!)
|
||||
: mwebHd?.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed ?? List.filled(32, 0);
|
||||
List<int> get spendPubkey =>
|
||||
(spendPubkeyOverride != null && spendPubkeyOverride?.isNotEmpty == true)
|
||||
? hex.decode(spendPubkeyOverride!)
|
||||
: mwebHd?.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed ??
|
||||
List.filled(32, 0);
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
@@ -81,7 +83,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||
return null;
|
||||
}
|
||||
if ((scanSecret.length < 1 || scanSecret.reduce((a, b) => a + b) == 0) &&
|
||||
(spendPubkey.length < 1 || spendPubkey.reduce((a, b) => a + b) == 0)) {
|
||||
(spendPubkey.length < 1 || spendPubkey.reduce((a, b) => a + b) == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -230,12 +232,16 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||
// don't use mweb addresses for exchange refund address:
|
||||
|
||||
final current = getFreshAddress();
|
||||
final bool isMweb = receiveAddresses
|
||||
.any((e) => e.address == current && e.type == SegwitAddresType.mweb);
|
||||
final bool isMweb =
|
||||
receiveAddresses.any((e) => e.address == current && e.type == SegwitAddresType.mweb);
|
||||
|
||||
if (isMweb) {
|
||||
final segwit = receiveAddresses
|
||||
.where((e) => e.type == SegwitAddresType.p2wpkh && !e.isUsed && !e.isHidden && !hiddenAddresses.contains(e.address))
|
||||
.where((e) =>
|
||||
e.type == SegwitAddresType.p2wpkh &&
|
||||
!e.isUsed &&
|
||||
!e.isHidden &&
|
||||
!hiddenAddresses.contains(e.address))
|
||||
.map((e) => e.address)
|
||||
.toList();
|
||||
|
||||
|
||||
@@ -48,7 +48,8 @@ class LitecoinWalletService extends WalletService<
|
||||
password: credentials.password!,
|
||||
passphrase: credentials.passphrase,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
|
||||
derivationInfo:
|
||||
credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
@@ -64,7 +65,6 @@ class LitecoinWalletService extends WalletService<
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> openWallet(String name, String password) async {
|
||||
|
||||
final walletInfo = await WalletInfo.get(name, getType());
|
||||
if (walletInfo == null) {
|
||||
throw Exception('Wallet not found');
|
||||
@@ -125,8 +125,9 @@ class LitecoinWalletService extends WalletService<
|
||||
}
|
||||
}
|
||||
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
|
||||
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values
|
||||
.where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
|
||||
.toList();
|
||||
|
||||
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
|
||||
|
||||
@@ -150,8 +151,7 @@ class LitecoinWalletService extends WalletService<
|
||||
final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
|
||||
credentials.walletInfo?.network = network.value;
|
||||
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
|
||||
derivationInfo.derivationPath =
|
||||
credentials.hwAccountData.derivationPath;
|
||||
derivationInfo.derivationPath = credentials.hwAccountData.derivationPath;
|
||||
await derivationInfo.save();
|
||||
credentials.walletInfo!.save();
|
||||
|
||||
@@ -170,7 +170,7 @@ class LitecoinWalletService extends WalletService<
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromKeys(LitecoinWalletFromKeysCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
{bool? isTestnet}) async {
|
||||
final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
|
||||
credentials.walletInfo?.network = network.value;
|
||||
|
||||
@@ -202,7 +202,8 @@ class LitecoinWalletService extends WalletService<
|
||||
passphrase: credentials.passphrase,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
|
||||
derivationInfo:
|
||||
credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
@@ -108,16 +108,14 @@ class PayjoinManager {
|
||||
Future<Sender> initSender(
|
||||
String pjUriString, String originalPsbt, int networkFeesSatPerVb) async {
|
||||
try {
|
||||
final pjUri =
|
||||
(await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
|
||||
final pjUri = (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
|
||||
final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250);
|
||||
final senderBuilder = await SenderBuilder.fromPsbtAndUri(
|
||||
psbtBase64: originalPsbt,
|
||||
pjUri: pjUri,
|
||||
);
|
||||
final persister = PayjoinSenderPersister.impl();
|
||||
final newSender =
|
||||
await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
|
||||
final newSender = await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
|
||||
final senderToken = await newSender.persist(persister: persister);
|
||||
|
||||
return Sender.load(token: senderToken, persister: persister);
|
||||
@@ -133,8 +131,7 @@ class PayjoinManager {
|
||||
bool isTestnet = false,
|
||||
}) async {
|
||||
final pjUri = Uri.parse(pjUrl).queryParameters['pj']!;
|
||||
await _payjoinStorage.insertSenderSession(
|
||||
sender, pjUri, _wallet.id, amount);
|
||||
await _payjoinStorage.insertSenderSession(sender, pjUri, _wallet.id, amount);
|
||||
|
||||
return _spawnSender(isTestnet: isTestnet, sender: sender, pjUri: pjUri);
|
||||
}
|
||||
@@ -207,8 +204,7 @@ class PayjoinManager {
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<Receiver> getUnusedReceiver(String address,
|
||||
[bool isTestnet = false]) async {
|
||||
Future<Receiver> getUnusedReceiver(String address, [bool isTestnet = false]) async {
|
||||
final session = _payjoinStorage.getUnusedActiveReceiverSession(_wallet.id);
|
||||
|
||||
if (session != null) {
|
||||
@@ -220,7 +216,8 @@ class PayjoinManager {
|
||||
return initReceiver(address);
|
||||
}
|
||||
|
||||
Future<Receiver> initReceiver(String address, [bool isTestnet = false, int retryCount = 0]) async {
|
||||
Future<Receiver> initReceiver(String address,
|
||||
[bool isTestnet = false, int retryCount = 0]) async {
|
||||
if (retryCount > 0) writePayjoinLog("Retrying initReceiver ${retryCount + 1} attempt");
|
||||
|
||||
try {
|
||||
@@ -245,7 +242,6 @@ class PayjoinManager {
|
||||
} catch (e) {
|
||||
writePayjoinLog(e.toString());
|
||||
if (e.toString().contains("error sending request for url") && retryCount < 5) {
|
||||
|
||||
return initReceiver(address, isTestnet, ++retryCount);
|
||||
} else {
|
||||
rethrow;
|
||||
@@ -273,13 +269,11 @@ class PayjoinManager {
|
||||
rawAmount = getOutputAmountFromTx(tx, _wallet);
|
||||
break;
|
||||
case PayjoinReceiverRequestTypes.checkIsOwned:
|
||||
(_wallet.walletAddresses as BitcoinWalletAddresses)
|
||||
.newPayjoinReceiver();
|
||||
(_wallet.walletAddresses as BitcoinWalletAddresses).newPayjoinReceiver();
|
||||
_payjoinStorage.markReceiverSessionInProgress(receiver.id());
|
||||
|
||||
final inputScript = message['input_script'] as Uint8List;
|
||||
final isOwned =
|
||||
_wallet.isMine(Script.fromRaw(byteData: inputScript));
|
||||
final isOwned = _wallet.isMine(Script.fromRaw(byteData: inputScript));
|
||||
mainToIsolateSendPort?.send({
|
||||
'requestId': message['requestId'],
|
||||
'result': isOwned,
|
||||
@@ -288,8 +282,7 @@ class PayjoinManager {
|
||||
|
||||
case PayjoinReceiverRequestTypes.checkIsReceiverOutput:
|
||||
final outputScript = message['output_script'] as Uint8List;
|
||||
final isReceiverOutput =
|
||||
_wallet.isMine(Script.fromRaw(byteData: outputScript));
|
||||
final isReceiverOutput = _wallet.isMine(Script.fromRaw(byteData: outputScript));
|
||||
mainToIsolateSendPort?.send({
|
||||
'requestId': message['requestId'],
|
||||
'result': isReceiverOutput,
|
||||
@@ -310,7 +303,8 @@ class PayjoinManager {
|
||||
|
||||
case PayjoinReceiverRequestTypes.processPsbt:
|
||||
final psbt = message['psbt'] as String;
|
||||
writePayjoinLog("Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.processPsbt: $psbt");
|
||||
writePayjoinLog(
|
||||
"Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.processPsbt: $psbt");
|
||||
|
||||
final signedPsbt = await _wallet.signPsbt(psbt, utxos);
|
||||
mainToIsolateSendPort?.send({
|
||||
@@ -322,7 +316,8 @@ class PayjoinManager {
|
||||
case PayjoinReceiverRequestTypes.proposalSent:
|
||||
_cleanupSession(receiver.id());
|
||||
final psbt = message['psbt'] as String;
|
||||
writePayjoinLog("Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.proposalSent: $psbt");
|
||||
writePayjoinLog(
|
||||
"Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.proposalSent: $psbt");
|
||||
|
||||
await _payjoinStorage.markReceiverSessionComplete(
|
||||
receiver.id(), getTxIdFromPsbtV0(psbt), rawAmount);
|
||||
|
||||
@@ -47,8 +47,7 @@ class PayjoinReceiverWorker {
|
||||
try {
|
||||
final receiver = Receiver.fromJson(json: receiverJson);
|
||||
|
||||
final uncheckedProposal =
|
||||
await worker.receiveUncheckedProposal(receiver);
|
||||
final uncheckedProposal = await worker.receiveUncheckedProposal(receiver);
|
||||
|
||||
final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast();
|
||||
sendPort.send({
|
||||
@@ -112,8 +111,7 @@ class PayjoinReceiverWorker {
|
||||
final httpRequest = await client.post(url,
|
||||
headers: {'Content-Type': request.contentType}, body: request.body);
|
||||
|
||||
final proposal = await session.processRes(
|
||||
body: httpRequest.bodyBytes, ctx: extractReq.$2);
|
||||
final proposal = await session.processRes(body: httpRequest.bodyBytes, ctx: extractReq.$2);
|
||||
if (proposal != null) return proposal;
|
||||
sleep(Duration(seconds: 2));
|
||||
}
|
||||
@@ -140,8 +138,7 @@ class PayjoinReceiverWorker {
|
||||
return await finalProposal.psbt();
|
||||
}
|
||||
|
||||
Future<PayjoinProposal> processPayjoinProposal(
|
||||
UncheckedProposal proposal) async {
|
||||
Future<PayjoinProposal> processPayjoinProposal(UncheckedProposal proposal) async {
|
||||
await proposal.extractTxToScheduleBroadcast();
|
||||
// TODO Handle this. send to the main port on a timer?
|
||||
|
||||
@@ -174,20 +171,17 @@ class PayjoinReceiverWorker {
|
||||
);
|
||||
final pj5 = await pj4.commitOutputs();
|
||||
|
||||
final listUnspent =
|
||||
await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
|
||||
final listUnspent = await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
|
||||
final unspent = listUnspent as List<UtxoWithPrivateKey>;
|
||||
if (unspent.isEmpty) throw RecoverableError('No unspent outputs available');
|
||||
|
||||
final candidateInputs =
|
||||
await Future.wait(unspent.map(_inputPairFromUtxo));
|
||||
final candidateInputs = await Future.wait(unspent.map(_inputPairFromUtxo));
|
||||
|
||||
// Prefer a UTXO that avoids the Unnecessary Input Heuristic (UIH2);
|
||||
// fall back to the first candidate if none preserves privacy.
|
||||
InputPair selectedUtxo = candidateInputs.first;
|
||||
try {
|
||||
selectedUtxo =
|
||||
await pj5.tryPreservingPrivacy(candidateInputs: candidateInputs);
|
||||
selectedUtxo = await pj5.tryPreservingPrivacy(candidateInputs: candidateInputs);
|
||||
} catch (_) {}
|
||||
|
||||
final pj6 = await pj5.contributeInputs(replacementInputs: [selectedUtxo]);
|
||||
@@ -196,8 +190,8 @@ class PayjoinReceiverWorker {
|
||||
// Finalize proposal
|
||||
final payjoinProposal = await pj7.finalizeProposal(
|
||||
processPsbt: (String psbt) async {
|
||||
final result = await _sendRequest(
|
||||
PayjoinReceiverRequestTypes.processPsbt, {'psbt': psbt});
|
||||
final result =
|
||||
await _sendRequest(PayjoinReceiverRequestTypes.processPsbt, {'psbt': psbt});
|
||||
return result as String;
|
||||
},
|
||||
// TODO set maxFeeRateSatPerVb
|
||||
@@ -213,15 +207,12 @@ class PayjoinReceiverWorker {
|
||||
Future<InputPair> _inputPairFromUtxo(UtxoWithPrivateKey utxo) async {
|
||||
final txout = TxOut(
|
||||
value: utxo.utxo.value,
|
||||
scriptPubkey: Uint8List.fromList(
|
||||
utxo.ownerDetails.address.toScriptPubKey().toBytes()),
|
||||
scriptPubkey: Uint8List.fromList(utxo.ownerDetails.address.toScriptPubKey().toBytes()),
|
||||
);
|
||||
|
||||
final psbtin =
|
||||
PsbtInput(witnessUtxo: txout, redeemScript: null, witnessScript: null);
|
||||
final psbtin = PsbtInput(witnessUtxo: txout, redeemScript: null, witnessScript: null);
|
||||
|
||||
final previousOutput =
|
||||
OutPoint(txid: utxo.utxo.txHash, vout: utxo.utxo.vout);
|
||||
final previousOutput = OutPoint(txid: utxo.utxo.txHash, vout: utxo.utxo.vout);
|
||||
|
||||
final txin = TxIn(
|
||||
previousOutput: previousOutput,
|
||||
|
||||
@@ -46,11 +46,11 @@ class PayjoinSenderWorker {
|
||||
sendPort.send(e);
|
||||
}
|
||||
}
|
||||
|
||||
final client = ProxyWrapper().getHttpIOClient();
|
||||
|
||||
/// Run a payjoin sender (V2 protocol first, fallback to V1).
|
||||
Future<String> runSender(Sender sender) async {
|
||||
|
||||
try {
|
||||
return await _runSenderV2(sender);
|
||||
} catch (e) {
|
||||
@@ -70,13 +70,11 @@ class PayjoinSenderWorker {
|
||||
Future<String> _runSenderV2(Sender sender) async {
|
||||
try {
|
||||
final postRequest = await sender.extractV2(
|
||||
ohttpProxyUrl:
|
||||
await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
|
||||
ohttpProxyUrl: await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
|
||||
);
|
||||
|
||||
final postResult = await _postRequest(postRequest.$1);
|
||||
final getContext =
|
||||
await postRequest.$2.processResponse(response: postResult);
|
||||
final getContext = await postRequest.$2.processResponse(response: postResult);
|
||||
|
||||
sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted, "pj": pjUrl});
|
||||
|
||||
|
||||
@@ -23,16 +23,14 @@ class PayjoinStorage {
|
||||
),
|
||||
);
|
||||
|
||||
PayjoinSession? getUnusedActiveReceiverSession(String walletId) =>
|
||||
_payjoinSessionSources.values
|
||||
.where((session) =>
|
||||
session.walletId == walletId &&
|
||||
session.status == PayjoinSessionStatus.created.name &&
|
||||
!session.isSenderSession)
|
||||
.firstOrNull;
|
||||
PayjoinSession? getUnusedActiveReceiverSession(String walletId) => _payjoinSessionSources.values
|
||||
.where((session) =>
|
||||
session.walletId == walletId &&
|
||||
session.status == PayjoinSessionStatus.created.name &&
|
||||
!session.isSenderSession)
|
||||
.firstOrNull;
|
||||
|
||||
Future<void> markReceiverSessionComplete(
|
||||
String sessionId, String txId, String amount) async {
|
||||
Future<void> markReceiverSessionComplete(String sessionId, String txId, String amount) async {
|
||||
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
|
||||
|
||||
session.status = PayjoinSessionStatus.success.name;
|
||||
@@ -41,8 +39,7 @@ class PayjoinStorage {
|
||||
await session.save();
|
||||
}
|
||||
|
||||
Future<void> markReceiverSessionUnrecoverable(
|
||||
String sessionId, String reason) async {
|
||||
Future<void> markReceiverSessionUnrecoverable(String sessionId, String reason) async {
|
||||
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
|
||||
|
||||
session.status = PayjoinSessionStatus.unrecoverable.name;
|
||||
@@ -92,13 +89,10 @@ class PayjoinStorage {
|
||||
await session.save();
|
||||
}
|
||||
|
||||
List<PayjoinSession> readAllOpenSessions(String walletId) =>
|
||||
_payjoinSessionSources.values
|
||||
.where((session) =>
|
||||
session.walletId == walletId &&
|
||||
![
|
||||
PayjoinSessionStatus.success.name,
|
||||
PayjoinSessionStatus.unrecoverable.name
|
||||
].contains(session.status))
|
||||
.toList();
|
||||
List<PayjoinSession> readAllOpenSessions(String walletId) => _payjoinSessionSources.values
|
||||
.where((session) =>
|
||||
session.walletId == walletId &&
|
||||
![PayjoinSessionStatus.success.name, PayjoinSessionStatus.unrecoverable.name]
|
||||
.contains(session.status))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@ extension PsbtSigner on PsbtV2 {
|
||||
return tx.buffer();
|
||||
}
|
||||
|
||||
Future<void> signWithUTXO(
|
||||
List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
|
||||
Future<void> signWithUTXO(List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
|
||||
[UTXOGetterCallBack? getTaprootPair]) async {
|
||||
final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false));
|
||||
final tx = BtcTransaction.fromRaw(raw);
|
||||
@@ -53,8 +52,8 @@ extension PsbtSigner on PsbtV2 {
|
||||
|
||||
if (utxos.any((e) => e.utxo.isP2tr())) {
|
||||
for (final input in tx.inputs) {
|
||||
final utxo = utxos.firstWhereOrNull(
|
||||
(u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
|
||||
final utxo = utxos
|
||||
.firstWhereOrNull((u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
|
||||
|
||||
if (utxo == null) {
|
||||
final trPair = await getTaprootPair!.call(input.txId, input.txIndex);
|
||||
@@ -81,8 +80,8 @@ extension PsbtSigner on PsbtV2 {
|
||||
: BitcoinOpCodeConst.SIGHASH_ALL;
|
||||
|
||||
/// We generate transaction digest for current input
|
||||
final digest = _generateTransactionDigest(
|
||||
script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
|
||||
final digest =
|
||||
_generateTransactionDigest(script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
|
||||
|
||||
/// now we need sign the transaction digest
|
||||
final sig = signer(digest, utxo, utxo.privateKey, sighash);
|
||||
@@ -90,21 +89,14 @@ extension PsbtSigner on PsbtV2 {
|
||||
if (utxo.utxo.isP2tr()) {
|
||||
setInputTapKeySig(i, Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
||||
} else {
|
||||
setInputPartialSig(
|
||||
i,
|
||||
Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
|
||||
setInputPartialSig(i, Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
|
||||
Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<int> _generateTransactionDigest(
|
||||
Script scriptPubKeys,
|
||||
int input,
|
||||
BitcoinUtxo utxo,
|
||||
BtcTransaction transaction,
|
||||
List<BigInt> taprootAmounts,
|
||||
List<Script> tapRootPubKeys) {
|
||||
List<int> _generateTransactionDigest(Script scriptPubKeys, int input, BitcoinUtxo utxo,
|
||||
BtcTransaction transaction, List<BigInt> taprootAmounts, List<Script> tapRootPubKeys) {
|
||||
if (utxo.isSegwit()) {
|
||||
if (utxo.isP2tr()) {
|
||||
return transaction.getTransactionTaprootDigset(
|
||||
@@ -116,8 +108,7 @@ extension PsbtSigner on PsbtV2 {
|
||||
return transaction.getTransactionSegwitDigit(
|
||||
txInIndex: input, script: scriptPubKeys, amount: utxo.value);
|
||||
}
|
||||
return transaction.getTransactionDigest(
|
||||
txInIndex: input, script: scriptPubKeys);
|
||||
return transaction.getTransactionDigest(txInIndex: input, script: scriptPubKeys);
|
||||
}
|
||||
|
||||
Script _findLockingScript(UtxoWithAddress utxo, bool isTaproot) {
|
||||
@@ -172,11 +163,10 @@ extension PsbtSigner on PsbtV2 {
|
||||
}
|
||||
}
|
||||
|
||||
typedef UTXOSignerCallBack = String Function(List<int> trDigest,
|
||||
UtxoWithAddress utxo, ECPrivate privateKey, int sighash);
|
||||
typedef UTXOSignerCallBack = String Function(
|
||||
List<int> trDigest, UtxoWithAddress utxo, ECPrivate privateKey, int sighash);
|
||||
|
||||
typedef UTXOGetterCallBack = Future<TaprootAmountScriptPair> Function(
|
||||
String txId, int vout);
|
||||
typedef UTXOGetterCallBack = Future<TaprootAmountScriptPair> Function(String txId, int vout);
|
||||
|
||||
class TaprootAmountScriptPair {
|
||||
final BigInt value;
|
||||
@@ -216,23 +206,17 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||
}
|
||||
|
||||
return UtxoWithPrivateKey(
|
||||
utxo: input.utxo,
|
||||
ownerDetails: input.ownerDetails,
|
||||
privateKey: key.privkey);
|
||||
utxo: input.utxo, ownerDetails: input.ownerDetails, privateKey: key.privkey);
|
||||
}
|
||||
|
||||
factory UtxoWithPrivateKey.fromUnspent(
|
||||
BitcoinUnspent input, BitcoinWalletBase wallet) {
|
||||
final address =
|
||||
RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
|
||||
factory UtxoWithPrivateKey.fromUnspent(BitcoinUnspent input, BitcoinWalletBase wallet) {
|
||||
final address = RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
|
||||
|
||||
final newHd =
|
||||
input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.mainHd;
|
||||
final newHd = input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.mainHd;
|
||||
|
||||
ECPrivate privkey;
|
||||
if (input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
|
||||
final unspentAddress =
|
||||
input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
|
||||
final unspentAddress = input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
|
||||
privkey = wallet.walletAddresses.silentAddress!.b_spend.tweakAdd(
|
||||
BigintUtils.fromBytes(
|
||||
BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!),
|
||||
@@ -240,9 +224,7 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||
);
|
||||
} else {
|
||||
privkey = generateECPrivate(
|
||||
hd: newHd,
|
||||
index: input.bitcoinAddressRecord.index,
|
||||
network: BitcoinNetwork.mainnet);
|
||||
hd: newHd, index: input.bitcoinAddressRecord.index, network: BitcoinNetwork.mainnet);
|
||||
}
|
||||
|
||||
return UtxoWithPrivateKey(
|
||||
@@ -251,8 +233,7 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||
value: BigInt.from(input.value),
|
||||
vout: input.vout,
|
||||
scriptType: input.bitcoinAddressRecord.type,
|
||||
isSilentPayment:
|
||||
input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
|
||||
isSilentPayment: input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
|
||||
),
|
||||
ownerDetails: UtxoAddressDetails(
|
||||
publicKey: privkey.getPublic().toHex(),
|
||||
|
||||
@@ -11,7 +11,10 @@ class PSBTTransactionBuild {
|
||||
final PsbtV2 psbt = PsbtV2();
|
||||
|
||||
PSBTTransactionBuild(
|
||||
{required List<PSBTReadyUtxoWithAddress> inputs, required List<BitcoinBaseOutput> outputs, required List<OutputInfo> cwOutputs, bool enableRBF = true}) {
|
||||
{required List<PSBTReadyUtxoWithAddress> inputs,
|
||||
required List<BitcoinBaseOutput> outputs,
|
||||
required List<OutputInfo> cwOutputs,
|
||||
bool enableRBF = true}) {
|
||||
psbt.setGlobalTxVersion(2);
|
||||
psbt.setGlobalInputCount(inputs.length);
|
||||
psbt.setGlobalOutputCount(outputs.length);
|
||||
@@ -23,10 +26,10 @@ class PSBTTransactionBuild {
|
||||
printV(input.utxo.isSegwit());
|
||||
printV(input.utxo.isP2shSegwit());
|
||||
|
||||
psbt.setInputPreviousTxId(i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
||||
psbt.setInputPreviousTxId(
|
||||
i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
||||
psbt.setInputOutputIndex(i, input.utxo.vout);
|
||||
psbt.setInputSequence(i, enableRBF ? 0xfffffffd : 0xffffffff);
|
||||
|
||||
|
||||
if (input.utxo.isSegwit()) {
|
||||
setInputSegwit(i, input);
|
||||
@@ -48,10 +51,10 @@ class PSBTTransactionBuild {
|
||||
if (cwOutputs.isNotEmpty) {
|
||||
try {
|
||||
final cwOutput = cwOutputs
|
||||
.where((e) => [e.address, e.extractedAddress]
|
||||
.map((e) => e?.toLowerCase())
|
||||
.contains(output.address.toAddress().toLowerCase()))
|
||||
.firstOrNull;
|
||||
.where((e) => [e.address, e.extractedAddress]
|
||||
.map((e) => e?.toLowerCase())
|
||||
.contains(output.address.toAddress().toLowerCase()))
|
||||
.firstOrNull;
|
||||
if (cwOutput != null &&
|
||||
cwOutput.extra.containsKey('bip353_name') &&
|
||||
cwOutput.extra.containsKey('bip353_proof')) {
|
||||
@@ -80,20 +83,14 @@ class PSBTTransactionBuild {
|
||||
|
||||
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
|
||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||
psbt.setInputBip32Derivation(
|
||||
i,
|
||||
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint,
|
||||
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
}
|
||||
|
||||
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||
psbt.setInputBip32Derivation(
|
||||
i,
|
||||
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint,
|
||||
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
|
||||
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
||||
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
|
||||
@@ -10,9 +10,7 @@ import 'package:ledger_bitcoin/psbt.dart';
|
||||
String getTxIdFromPsbtV0(String psbt) {
|
||||
final psbtV2 = PsbtV2()..deserializeV0(base64.decode(psbt));
|
||||
|
||||
return BtcTransaction.fromRaw(
|
||||
BytesUtils.toHexString(psbtV2.extractUnsignedTX(false)))
|
||||
.txId();
|
||||
return BtcTransaction.fromRaw(BytesUtils.toHexString(psbtV2.extractUnsignedTX(false))).txId();
|
||||
}
|
||||
|
||||
String getOutputAmountFromPsbt(String psbtV0, BitcoinWalletBase wallet) {
|
||||
|
||||
@@ -7,10 +7,10 @@ import 'package:ledger_bitcoin/src/utils/buffer_reader.dart';
|
||||
import 'package:ledger_bitcoin/src/utils/uint8list_extension.dart' as ext;
|
||||
|
||||
extension PsbtSigner on PsbtV2 {
|
||||
|
||||
void deserializeV0(Uint8List psbt) {
|
||||
final bufferReader = BufferReader(psbt);
|
||||
if (!listEquals(bufferReader.readSlice(5), Uint8List.fromList([0x70, 0x73, 0x62, 0x74, 0xff]))) {
|
||||
if (!listEquals(
|
||||
bufferReader.readSlice(5), Uint8List.fromList([0x70, 0x73, 0x62, 0x74, 0xff]))) {
|
||||
throw Exception("Invalid magic bytes");
|
||||
}
|
||||
while (_readKeyPair(globalMap, bufferReader)) {}
|
||||
@@ -26,7 +26,8 @@ extension PsbtSigner on PsbtV2 {
|
||||
while (_readKeyPair(inputMaps[i], bufferReader)) {}
|
||||
final input = tx.inputs[i];
|
||||
setInputOutputIndex(i, input.txIndex);
|
||||
setInputPreviousTxId(i, Uint8List.fromList(BytesUtils.fromHexString(input.txId).reversed.toList()));
|
||||
setInputPreviousTxId(
|
||||
i, Uint8List.fromList(BytesUtils.fromHexString(input.txId).reversed.toList()));
|
||||
setInputSequence(i, Uint8List.fromList(input.sequence).readUint32LE(0));
|
||||
}
|
||||
for (var i = 0; i < getGlobalOutputCount(); i++) {
|
||||
|
||||
@@ -18,7 +18,6 @@ import "package:ledger_bitcoin/src/utils/buffer_writer.dart";
|
||||
/// through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
|
||||
extension InputFinalizer on PsbtV2 {
|
||||
void finalizeV0() {
|
||||
|
||||
// First check that each input has a signature
|
||||
for (var i = 0; i < getGlobalInputCount(); i++) {
|
||||
if (_isFinalized(i)) continue;
|
||||
@@ -31,8 +30,7 @@ extension InputFinalizer on PsbtV2 {
|
||||
}
|
||||
if (legacyPubkeys.isNotEmpty) {
|
||||
if (legacyPubkeys.length > 1) {
|
||||
throw Exception(
|
||||
'Expected exactly one signature, got ${legacyPubkeys.length}');
|
||||
throw Exception('Expected exactly one signature, got ${legacyPubkeys.length}');
|
||||
}
|
||||
if (taprootSig != null) {
|
||||
throw Exception('Both taproot and non-taproot signatures present.');
|
||||
@@ -55,8 +53,7 @@ extension InputFinalizer on PsbtV2 {
|
||||
setInputFinalScriptwitness(i, witnessBuf.buffer());
|
||||
if (isWrappedSegwit) {
|
||||
if (redeemScript.isEmpty) {
|
||||
throw Exception(
|
||||
"Expected non-empty redeemscript. Can't finalize input $i");
|
||||
throw Exception("Expected non-empty redeemscript. Can't finalize input $i");
|
||||
}
|
||||
final scriptSigBuf = BufferWriter()
|
||||
..writeUInt8(redeemScript.length) // Push redeemScript length
|
||||
|
||||
@@ -52,4 +52,4 @@ String generateP2TRAddress({
|
||||
}) =>
|
||||
ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey)
|
||||
.toTaprootAddress()
|
||||
.toAddress(network);
|
||||
.toAddress(network);
|
||||
|
||||
@@ -6,7 +6,7 @@ author: Cake Wallet
|
||||
homepage: https://cakewallet.com
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.5 <3.0.0"
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
flutter: ">=1.20.0"
|
||||
|
||||
dependencies:
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@@ -87,8 +87,9 @@ class BitcoinCashWalletService extends WalletService<
|
||||
}
|
||||
await WalletInfo.delete(walletInfo);
|
||||
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
|
||||
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values
|
||||
.where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
|
||||
.toList();
|
||||
|
||||
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
|
||||
|
||||
@@ -122,8 +123,7 @@ class BitcoinCashWalletService extends WalletService<
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
passphrase: credentials.passphrase
|
||||
);
|
||||
passphrase: credentials.passphrase);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
|
||||
@@ -1 +1 @@
|
||||
export 'bitcoin_cash_mnemonic_is_incorrect_exception.dart';
|
||||
export 'bitcoin_cash_mnemonic_is_incorrect_exception.dart';
|
||||
|
||||
@@ -9,4 +9,4 @@ class Account {
|
||||
final int id;
|
||||
final String label;
|
||||
final String? balance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
abstract class AccountList<T> {
|
||||
|
||||
ObservableList<T> get accounts;
|
||||
|
||||
void update();
|
||||
|
||||
@@ -41,7 +41,5 @@ class AddressInfoAdapter extends TypeAdapter<AddressInfo> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is AddressInfoAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is AddressInfoAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ String _smartAmountSanitizer(String amount) {
|
||||
return amount.replaceAll(",", ".");
|
||||
}
|
||||
|
||||
|
||||
extension SanitizerUserAmounts on String {
|
||||
String sanitized() => _smartAmountSanitizer(this);
|
||||
}
|
||||
|
||||
@@ -9,19 +9,18 @@ class AmountConverter {
|
||||
amount = amount.replaceAll(',', '');
|
||||
final parts = amount.split('.');
|
||||
final whole = parts[0].isEmpty ? '0' : parts[0];
|
||||
final frac = parts.length > 1 ? parts[1] : '';
|
||||
final frac = parts.length > 1 ? parts[1] : '';
|
||||
|
||||
final fracPadded = (frac + '0' * decimals).substring(0, decimals);
|
||||
|
||||
final pow10 = BigInt.from(10).pow(decimals);
|
||||
final wholeBI = BigInt.parse(whole);
|
||||
final fracBI = fracPadded.isEmpty ? BigInt.zero : BigInt.parse(fracPadded);
|
||||
final fracBI = fracPadded.isEmpty ? BigInt.zero : BigInt.parse(fracPadded);
|
||||
|
||||
final res = wholeBI * pow10 + fracBI;
|
||||
return neg ? '-${res.toString()}' : res.toString();
|
||||
}
|
||||
|
||||
|
||||
static String fromBaseUnits(String units, int decimals) {
|
||||
units = units.trim();
|
||||
if (units.isEmpty) return '0';
|
||||
@@ -35,14 +34,14 @@ class AmountConverter {
|
||||
if (units.length <= decimals) {
|
||||
final s = units.padLeft(decimals + 1, '0'); // ensures at least "0xxxx"
|
||||
final intPart = s.substring(0, s.length - decimals);
|
||||
var fracPart = s.substring(s.length - decimals);
|
||||
var fracPart = s.substring(s.length - decimals);
|
||||
fracPart = fracPart.replaceFirst(RegExp(r'0+$'), '');
|
||||
final out = fracPart.isEmpty ? intPart : '$intPart.$fracPart';
|
||||
return neg ? '-$out' : out;
|
||||
}
|
||||
|
||||
final intPart = units.substring(0, units.length - decimals);
|
||||
var fracPart = units.substring(units.length - decimals);
|
||||
var fracPart = units.substring(units.length - decimals);
|
||||
fracPart = fracPart.replaceFirst(RegExp(r'0+$'), '');
|
||||
final out = fracPart.isEmpty ? intPart : '$intPart.$fracPart';
|
||||
return neg ? '-$out' : out;
|
||||
|
||||
@@ -14,13 +14,13 @@ class BalanceCardStyleSettings {
|
||||
|
||||
BalanceCardStyleSettings(
|
||||
{required this.walletInfoId,
|
||||
required this.accountIndex,
|
||||
required this.gradientIndex,
|
||||
required this.useSpecialDesign,
|
||||
required this.backgroundImagePath,
|
||||
this.iconStyleIndex = 0,
|
||||
this.isGradientOnly = false,
|
||||
required this.cardOrder});
|
||||
required this.accountIndex,
|
||||
required this.gradientIndex,
|
||||
required this.useSpecialDesign,
|
||||
required this.backgroundImagePath,
|
||||
this.iconStyleIndex = 0,
|
||||
this.isGradientOnly = false,
|
||||
required this.cardOrder});
|
||||
|
||||
static const tableName = "BalanceCardStyleSettings";
|
||||
|
||||
@@ -59,21 +59,17 @@ class BalanceCardStyleSettings {
|
||||
int iconStyleIndex = 0,
|
||||
int? gradientIndexOverride,
|
||||
}) {
|
||||
final int gradientIndex = gradientIndexOverride ??
|
||||
CardDesign.allGradients.indexOf(design.gradient);
|
||||
final int gradientIndex =
|
||||
gradientIndexOverride ?? CardDesign.allGradients.indexOf(design.gradient);
|
||||
return BalanceCardStyleSettings(
|
||||
walletInfoId: walletInfoId,
|
||||
accountIndex: accountIndex,
|
||||
gradientIndex: gradientIndex,
|
||||
useSpecialDesign:
|
||||
design.backgroundType == CardDesignBackgroundTypes.svgFull,
|
||||
useSpecialDesign: design.backgroundType == CardDesignBackgroundTypes.svgFull,
|
||||
backgroundImagePath:
|
||||
design.backgroundType == CardDesignBackgroundTypes.image
|
||||
? design.imagePath
|
||||
: "",
|
||||
design.backgroundType == CardDesignBackgroundTypes.image ? design.imagePath : "",
|
||||
iconStyleIndex: iconStyleIndex,
|
||||
isGradientOnly:
|
||||
design.backgroundType == CardDesignBackgroundTypes.gradientOnly,
|
||||
isGradientOnly: design.backgroundType == CardDesignBackgroundTypes.gradientOnly,
|
||||
cardOrder: cardOrder,
|
||||
);
|
||||
}
|
||||
@@ -104,4 +100,4 @@ class BalanceCardStyleSettings {
|
||||
Future<void> insert() async {
|
||||
db!.insert(tableName, toJson(), conflictAlgorithm: ConflictAlgorithm.replace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+123
-110
@@ -12,14 +12,13 @@ class CardColorCombination {
|
||||
|
||||
const CardColorCombination(
|
||||
{required this.textColor,
|
||||
required this.textColorSecondary,
|
||||
required this.backgroundImageColor});
|
||||
required this.textColorSecondary,
|
||||
required this.backgroundImageColor});
|
||||
|
||||
static const light = CardColorCombination(
|
||||
textColor: Color.fromARGB(200, 255, 255, 255),
|
||||
textColorSecondary: Colors.white54,
|
||||
backgroundImageColor: Color.fromARGB(200, 0, 0, 0)
|
||||
);
|
||||
backgroundImageColor: Color.fromARGB(200, 0, 0, 0));
|
||||
|
||||
static const dark = CardColorCombination(
|
||||
textColor: Color.fromARGB(200, 0, 0, 0),
|
||||
@@ -50,10 +49,11 @@ class CardDesign {
|
||||
|
||||
const CardDesign(
|
||||
{this.backgroundType = CardDesignBackgroundTypes.svgIcon,
|
||||
this.gradient = const LinearGradient(colors: [Colors.black], begin: Alignment.topCenter, end: Alignment.bottomCenter),
|
||||
this.imagePath = "assets/new-ui/blank.svg",
|
||||
this.colors = CardColorCombination.dark,
|
||||
this.preColoredIcon = false});
|
||||
this.gradient = const LinearGradient(
|
||||
colors: [Colors.black], begin: Alignment.topCenter, end: Alignment.bottomCenter),
|
||||
this.imagePath = "assets/new-ui/blank.svg",
|
||||
this.colors = CardColorCombination.dark,
|
||||
this.preColoredIcon = false});
|
||||
|
||||
static const LinearGradient gradientOrange = LinearGradient(
|
||||
colors: <Color>[Color(0xFFFF7C02), Color(0xFFFF5602)],
|
||||
@@ -129,71 +129,50 @@ class CardDesign {
|
||||
|
||||
static const genericDefault = CardDesign(gradient: gradientBlue);
|
||||
|
||||
static const gradientOnlyDesign = CardDesign(
|
||||
backgroundType: CardDesignBackgroundTypes.gradientOnly,
|
||||
gradient: gradientBlue);
|
||||
static const gradientOnlyDesign =
|
||||
CardDesign(backgroundType: CardDesignBackgroundTypes.gradientOnly, gradient: gradientBlue);
|
||||
|
||||
static const btc = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/bitcoin.svg");
|
||||
static const btc = CardDesign(imagePath: "assets/new-ui/balance_card_icons/bitcoin.svg");
|
||||
|
||||
static const eth = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/ethereum.svg");
|
||||
static const eth = CardDesign(imagePath: "assets/new-ui/balance_card_icons/ethereum.svg");
|
||||
|
||||
static const btcln = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/lightning.svg");
|
||||
static const btcln = CardDesign(imagePath: "assets/new-ui/balance_card_icons/lightning.svg");
|
||||
|
||||
static const xmr = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/monero.svg");
|
||||
static const xmr = CardDesign(imagePath: "assets/new-ui/balance_card_icons/monero.svg");
|
||||
|
||||
static const ltc = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/litecoin.svg");
|
||||
static const ltc = CardDesign(imagePath: "assets/new-ui/balance_card_icons/litecoin.svg");
|
||||
|
||||
static const bch = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/bitcoin_cash.svg");
|
||||
static const bch = CardDesign(imagePath: "assets/new-ui/balance_card_icons/bitcoin_cash.svg");
|
||||
|
||||
static const doge = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/dogecoin.svg");
|
||||
static const doge = CardDesign(imagePath: "assets/new-ui/balance_card_icons/dogecoin.svg");
|
||||
|
||||
static const base = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/base.svg");
|
||||
static const base = CardDesign(imagePath: "assets/new-ui/balance_card_icons/base.svg");
|
||||
|
||||
static const pol = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/polygon.svg");
|
||||
static const pol = CardDesign(imagePath: "assets/new-ui/balance_card_icons/polygon.svg");
|
||||
|
||||
static const sol = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/solana.svg");
|
||||
static const sol = CardDesign(imagePath: "assets/new-ui/balance_card_icons/solana.svg");
|
||||
|
||||
static const tron = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/tron.svg");
|
||||
static const tron = CardDesign(imagePath: "assets/new-ui/balance_card_icons/tron.svg");
|
||||
|
||||
static const nano = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/nano.svg");
|
||||
static const nano = CardDesign(imagePath: "assets/new-ui/balance_card_icons/nano.svg");
|
||||
|
||||
static const zano = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/zano.svg");
|
||||
static const zano = CardDesign(imagePath: "assets/new-ui/balance_card_icons/zano.svg");
|
||||
|
||||
static const wow = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/wownero.svg");
|
||||
static const wow = CardDesign(imagePath: "assets/new-ui/balance_card_icons/wownero.svg");
|
||||
|
||||
static const dcr = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/decred.svg");
|
||||
static const dcr = CardDesign(imagePath: "assets/new-ui/balance_card_icons/decred.svg");
|
||||
|
||||
static const arbitrum = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/arbitrum.svg");
|
||||
|
||||
static const zec = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/zcash.svg");
|
||||
|
||||
static const bnb = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/bnb.svg");
|
||||
static const arbitrum = CardDesign(imagePath: "assets/new-ui/balance_card_icons/arbitrum.svg");
|
||||
|
||||
static const zec = CardDesign(imagePath: "assets/new-ui/balance_card_icons/zcash.svg");
|
||||
|
||||
static const bnb = CardDesign(imagePath: "assets/new-ui/balance_card_icons/bnb.svg");
|
||||
|
||||
static const ethSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF6259FF), Color(0xFF3B20E6)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.light,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/ethereum.svg");
|
||||
@@ -201,24 +180,24 @@ class CardDesign {
|
||||
static const btcSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFFFBF00), Color(0xFFFF6A00)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/bitcoin.svg");
|
||||
|
||||
static const xmrSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFFF5900), Color(0xFFE62E00)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/monero.svg");
|
||||
|
||||
static const ltcSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF2145BF), Color(0xFF072071)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.light,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/litecoin.svg");
|
||||
@@ -226,8 +205,8 @@ class CardDesign {
|
||||
static const lnSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFFFBF00), Color(0xFFFF6A00)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.dark,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/lightning.svg");
|
||||
@@ -235,8 +214,8 @@ class CardDesign {
|
||||
static const tronSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFFF1313), Color(0xFFB40000)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.light,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/tron.svg");
|
||||
@@ -244,8 +223,8 @@ class CardDesign {
|
||||
static const solSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF4701AA), Color(0xFF19004B)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.light,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/solana.svg");
|
||||
@@ -253,40 +232,40 @@ class CardDesign {
|
||||
static const bchSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF36DA4C), Color(0xFF008D57)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/bitcoin-cash.svg");
|
||||
|
||||
static const wowSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFFF6CD3), Color(0xFFF200A9)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/wownero.svg");
|
||||
|
||||
static const dogeSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFCBA818), Color(0xFF885D00)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/dogecoin.svg");
|
||||
|
||||
static const nanoSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF209CE9), Color(0xFF0073CB)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/nano.svg");
|
||||
|
||||
static const polSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF863FE3), Color(0xFF59098E)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.light,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/polygon.svg");
|
||||
@@ -294,8 +273,8 @@ class CardDesign {
|
||||
static const dcrSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF2871FF), Color(0xFF0057FF)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
colors: CardColorCombination.light,
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/decred.svg");
|
||||
@@ -303,8 +282,8 @@ class CardDesign {
|
||||
static const zanoSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF0004B4), Color(0xFF170069)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
colors: CardColorCombination.black,
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/zano.svg");
|
||||
@@ -312,8 +291,8 @@ class CardDesign {
|
||||
static const baseSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF003CFF), Color(0xFF000087)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
colors: CardColorCombination.light,
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/base.svg");
|
||||
@@ -321,8 +300,8 @@ class CardDesign {
|
||||
static const arbitrumSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF173F76), Color(0xFF031A39)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
colors: CardColorCombination.light,
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/arbitrum.svg");
|
||||
@@ -330,16 +309,16 @@ class CardDesign {
|
||||
static const zecSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFF6C527), Color(0xFFCD8C00)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/zcash.svg");
|
||||
|
||||
static const bnbSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF603000), Color(0xFFF0B90B)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
colors: CardColorCombination.light,
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/bnb.svg");
|
||||
@@ -351,12 +330,14 @@ class CardDesign {
|
||||
backgroundType: backgroundType,
|
||||
preColoredIcon: preColoredIcon);
|
||||
|
||||
CardDesign withGradientAndColorCombination(Gradient gradient, CardColorCombination cardColorCombination) => CardDesign(
|
||||
gradient: gradient,
|
||||
colors: cardColorCombination,
|
||||
imagePath: imagePath,
|
||||
backgroundType: backgroundType,
|
||||
preColoredIcon: preColoredIcon);
|
||||
CardDesign withGradientAndColorCombination(
|
||||
Gradient gradient, CardColorCombination cardColorCombination) =>
|
||||
CardDesign(
|
||||
gradient: gradient,
|
||||
colors: cardColorCombination,
|
||||
imagePath: imagePath,
|
||||
backgroundType: backgroundType,
|
||||
preColoredIcon: preColoredIcon);
|
||||
|
||||
CardDesign withIcon(CardIconPath icon) => CardDesign(
|
||||
gradient: gradient,
|
||||
@@ -374,8 +355,10 @@ class CardDesign {
|
||||
|
||||
static const Map<CryptoCurrency, _CurrencyIconNames> _iconNames = {
|
||||
CryptoCurrency.arbEth: _CurrencyIconNames(ticker: 'arb', longName: 'arbitrum'),
|
||||
CryptoCurrency.baseEth: _CurrencyIconNames(ticker: 'base', longName: 'base', chainFile: 'base_icon'),
|
||||
CryptoCurrency.bch: _CurrencyIconNames(ticker: 'bch', longName: 'bitcoin_cash', chainFile: 'bitcoin-cash'),
|
||||
CryptoCurrency.baseEth:
|
||||
_CurrencyIconNames(ticker: 'base', longName: 'base', chainFile: 'base_icon'),
|
||||
CryptoCurrency.bch:
|
||||
_CurrencyIconNames(ticker: 'bch', longName: 'bitcoin_cash', chainFile: 'bitcoin-cash'),
|
||||
CryptoCurrency.btc: _CurrencyIconNames(ticker: 'btc', longName: 'bitcoin', outlineFile: 'BTC'),
|
||||
CryptoCurrency.bnb: _CurrencyIconNames(ticker: 'bnb', longName: 'bnb'),
|
||||
CryptoCurrency.dcr: _CurrencyIconNames(ticker: 'dcr', longName: 'decred'),
|
||||
@@ -383,13 +366,13 @@ class CardDesign {
|
||||
CryptoCurrency.eth: _CurrencyIconNames(ticker: 'eth', longName: 'ethereum'),
|
||||
CryptoCurrency.btcln: _CurrencyIconNames(ticker: 'ln', longName: 'lightning'),
|
||||
CryptoCurrency.ltc: _CurrencyIconNames(ticker: 'ltc', longName: 'litecoin'),
|
||||
CryptoCurrency.xmr: _CurrencyIconNames(
|
||||
ticker: 'xmr', longName: 'monero', ogPath: 'assets/images/xmr-og.webp'),
|
||||
CryptoCurrency.xmr:
|
||||
_CurrencyIconNames(ticker: 'xmr', longName: 'monero', ogPath: 'assets/images/xmr-og.webp'),
|
||||
CryptoCurrency.nano: _CurrencyIconNames(ticker: 'xno', longName: 'nano'),
|
||||
CryptoCurrency.maticpoly: _CurrencyIconNames(ticker: 'pol', longName: 'polygon'),
|
||||
CryptoCurrency.sol: _CurrencyIconNames(ticker: 'sol', longName: 'solana'),
|
||||
CryptoCurrency.trx: _CurrencyIconNames(
|
||||
ticker: 'trx', longName: 'tron', ogPath: '$_ogIconPrefix/tron-og.svg'),
|
||||
CryptoCurrency.trx:
|
||||
_CurrencyIconNames(ticker: 'trx', longName: 'tron', ogPath: '$_ogIconPrefix/tron-og.svg'),
|
||||
CryptoCurrency.zano: _CurrencyIconNames(ticker: 'zano', longName: 'zano'),
|
||||
CryptoCurrency.zec: _CurrencyIconNames(ticker: 'zec', longName: 'zcash'),
|
||||
};
|
||||
@@ -408,7 +391,40 @@ class CardDesign {
|
||||
];
|
||||
}
|
||||
|
||||
static const List<CardDesign> all = [genericDefault, btc, eth, xmr, ltc, eth, pol, doge, base, sol, btcln, tron, zano, dcr, arbitrum, zec, bnb, ethSpecial, btcSpecial, xmrSpecial, ltcSpecial, lnSpecial, tronSpecial, bchSpecial, wowSpecial, dogeSpecial, polSpecial, dcrSpecial, zanoSpecial, arbitrumSpecial, zecSpecial, bnbSpecial];
|
||||
static const List<CardDesign> all = [
|
||||
genericDefault,
|
||||
btc,
|
||||
eth,
|
||||
xmr,
|
||||
ltc,
|
||||
eth,
|
||||
pol,
|
||||
doge,
|
||||
base,
|
||||
sol,
|
||||
btcln,
|
||||
tron,
|
||||
zano,
|
||||
dcr,
|
||||
arbitrum,
|
||||
zec,
|
||||
bnb,
|
||||
ethSpecial,
|
||||
btcSpecial,
|
||||
xmrSpecial,
|
||||
ltcSpecial,
|
||||
lnSpecial,
|
||||
tronSpecial,
|
||||
bchSpecial,
|
||||
wowSpecial,
|
||||
dogeSpecial,
|
||||
polSpecial,
|
||||
dcrSpecial,
|
||||
zanoSpecial,
|
||||
arbitrumSpecial,
|
||||
zecSpecial,
|
||||
bnbSpecial
|
||||
];
|
||||
|
||||
static CardDesign forCurrencySpecial(CryptoCurrency currency) {
|
||||
return specialDesignsForCurrencies[currency] ?? genericDefault;
|
||||
@@ -437,7 +453,6 @@ class CardDesign {
|
||||
CryptoCurrency.zec: zec,
|
||||
CryptoCurrency.dcr: dcr,
|
||||
CryptoCurrency.bnb: bnb,
|
||||
|
||||
};
|
||||
|
||||
static const Map<CryptoCurrency, CardDesign> specialDesignsForCurrencies = {
|
||||
@@ -504,9 +519,9 @@ class CardDesign {
|
||||
|
||||
if (setting.isGradientOnly) {
|
||||
final gradient = gradientForStoredIndex(setting.gradientIndex, walletCurrency);
|
||||
final textColors = preferredColorCombinations[gradient]
|
||||
?? specialDesignsForCurrencies[walletCurrency]?.colors
|
||||
?? gradientOnlyDesign.colors;
|
||||
final textColors = preferredColorCombinations[gradient] ??
|
||||
specialDesignsForCurrencies[walletCurrency]?.colors ??
|
||||
gradientOnlyDesign.colors;
|
||||
return gradientOnlyDesign.withGradientAndColorCombination(gradient, textColors);
|
||||
}
|
||||
|
||||
@@ -521,11 +536,9 @@ class CardDesign {
|
||||
}
|
||||
|
||||
if (!setting.useSpecialDesign && setting.gradientIndex == -1) {
|
||||
final specialColors =
|
||||
specialDesignsForCurrencies[walletCurrency] ?? genericDefault;
|
||||
final specialColors = specialDesignsForCurrencies[walletCurrency] ?? genericDefault;
|
||||
final design = CardDesign.forCurrencyIcon(walletCurrency)
|
||||
.withGradientAndColorCombination(
|
||||
specialColors.gradient, specialColors.colors);
|
||||
.withGradientAndColorCombination(specialColors.gradient, specialColors.colors);
|
||||
return design.withIconStyleIndex(setting.iconStyleIndex, walletCurrency);
|
||||
}
|
||||
if (setting.useSpecialDesign) {
|
||||
@@ -534,9 +547,9 @@ class CardDesign {
|
||||
if (setting.gradientIndex != -1) {
|
||||
final baseIcon = CardDesign.forCurrencyIcon(walletCurrency);
|
||||
final gradient = gradientForStoredIndex(setting.gradientIndex, walletCurrency);
|
||||
final textColors = preferredColorCombinations[gradient]
|
||||
?? specialDesignsForCurrencies[walletCurrency]?.colors
|
||||
?? baseIcon.colors;
|
||||
final textColors = preferredColorCombinations[gradient] ??
|
||||
specialDesignsForCurrencies[walletCurrency]?.colors ??
|
||||
baseIcon.colors;
|
||||
return baseIcon
|
||||
.withGradientAndColorCombination(gradient, textColors)
|
||||
.withIconStyleIndex(setting.iconStyleIndex, walletCurrency);
|
||||
@@ -561,4 +574,4 @@ class _CurrencyIconNames {
|
||||
this.chainFile,
|
||||
this.ogPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ extension MaxDecimals on String {
|
||||
return parts.join(".");
|
||||
}
|
||||
|
||||
|
||||
/// Format a stringified number to a localized representation
|
||||
/// 1.000.000,00 in de_DE
|
||||
/// 1’000’000.00 in de_CH
|
||||
|
||||
+842
-143
File diff suppressed because it is too large
Load Diff
@@ -90,7 +90,8 @@ int? getChainIdByCryptoCurrency(CryptoCurrency currency) {
|
||||
}
|
||||
}
|
||||
|
||||
CryptoCurrency getCryptoCurrencyForWalletListItem(WalletType type, {bool isTestnet = false, int? chainId}) {
|
||||
CryptoCurrency getCryptoCurrencyForWalletListItem(WalletType type,
|
||||
{bool isTestnet = false, int? chainId}) {
|
||||
if (type == WalletType.arbitrum) {
|
||||
return CryptoCurrency.arb;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ class _SqliteErrorHandlerState extends State<SqliteErrorHandler> {
|
||||
final dbFile = File("${(await getAppDir()).path}/cake.db");
|
||||
final jsonText = JsonEncoder.withIndent(" ").convert(await dumpCustomDb(dbFile.path));
|
||||
setState(() {
|
||||
log = jsonText;
|
||||
log = jsonText;
|
||||
});
|
||||
} catch (e, s) {
|
||||
setState(() {
|
||||
|
||||
@@ -3,40 +3,39 @@ import 'dart:typed_data';
|
||||
import 'package:cw_core/utils/file.dart' as file;
|
||||
import 'package:cake_backup/backup.dart' as cwb;
|
||||
|
||||
EncryptionFileUtils encryptionFileUtilsFor(bool direct)
|
||||
=> direct
|
||||
? XChaCha20EncryptionFileUtils()
|
||||
: Salsa20EncryhptionFileUtils();
|
||||
EncryptionFileUtils encryptionFileUtilsFor(bool direct) =>
|
||||
direct ? XChaCha20EncryptionFileUtils() : Salsa20EncryhptionFileUtils();
|
||||
|
||||
abstract class EncryptionFileUtils {
|
||||
Future<void> write({required String path, required String password, required String data});
|
||||
Future<String> read({required String path, required String password});
|
||||
Future<void> write({required String path, required String password, required String data});
|
||||
Future<String> read({required String path, required String password});
|
||||
}
|
||||
|
||||
class Salsa20EncryhptionFileUtils extends EncryptionFileUtils {
|
||||
// Requires legacy complex key + iv as password
|
||||
@override
|
||||
Future<void> write({required String path, required String password, required String data}) async
|
||||
=> await file.write(path: path, password: password, data: data);
|
||||
// Requires legacy complex key + iv as password
|
||||
@override
|
||||
Future<void> write(
|
||||
{required String path, required String password, required String data}) async =>
|
||||
await file.write(path: path, password: password, data: data);
|
||||
|
||||
// Requires legacy complex key + iv as password
|
||||
@override
|
||||
Future<String> read({required String path, required String password}) async
|
||||
=> await file.read(path: path, password: password);
|
||||
// Requires legacy complex key + iv as password
|
||||
@override
|
||||
Future<String> read({required String path, required String password}) async =>
|
||||
await file.read(path: path, password: password);
|
||||
}
|
||||
|
||||
class XChaCha20EncryptionFileUtils extends EncryptionFileUtils {
|
||||
@override
|
||||
Future<void> write({required String path, required String password, required String data}) async {
|
||||
final encrypted = await cwb.encrypt(password, Uint8List.fromList(data.codeUnits));
|
||||
await File(path).writeAsBytes(encrypted);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> read({required String path, required String password}) async {
|
||||
final file = File(path);
|
||||
final encrypted = await file.readAsBytes();
|
||||
final bytes = await cwb.decrypt(password, encrypted);
|
||||
return String.fromCharCodes(bytes);
|
||||
}
|
||||
}
|
||||
@override
|
||||
Future<void> write({required String path, required String password, required String data}) async {
|
||||
final encrypted = await cwb.encrypt(password, Uint8List.fromList(data.codeUnits));
|
||||
await File(path).writeAsBytes(encrypted);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> read({required String path, required String password}) async {
|
||||
final file = File(path);
|
||||
final encrypted = await file.readAsBytes();
|
||||
final bytes = await cwb.decrypt(password, encrypted);
|
||||
return String.fromCharCodes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'erc20_token.dart';
|
||||
|
||||
@@ -55,7 +55,5 @@ class Erc20TokenAdapter extends TypeAdapter<Erc20Token> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is Erc20TokenAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is Erc20TokenAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
@@ -69,4 +69,4 @@ class WalletDeprecationException implements Exception {
|
||||
String toString() => "Wallet type no longer supported";
|
||||
|
||||
WalletDeprecationException({required this.seed, required this.curr});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
String formatAmount(String amount) {
|
||||
if ((!amount.contains('.'))&&(!amount.contains(','))) {
|
||||
if ((!amount.contains('.')) && (!amount.contains(','))) {
|
||||
return amount + '.00';
|
||||
} else if ((amount.endsWith('.'))||(amount.endsWith(','))) {
|
||||
} else if ((amount.endsWith('.')) || (amount.endsWith(','))) {
|
||||
return amount + '00';
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:cw_core/parse_fixed.dart';
|
||||
|
||||
String formatFixed(BigInt value, int? decimals,
|
||||
{int? fractionalDigits, bool trimZeros = true}) {
|
||||
String formatFixed(BigInt value, int? decimals, {int? fractionalDigits, bool trimZeros = true}) {
|
||||
decimals ??= 0;
|
||||
fractionalDigits ??= decimals;
|
||||
|
||||
@@ -10,10 +9,8 @@ String formatFixed(BigInt value, int? decimals,
|
||||
var negative = value.isNegative;
|
||||
if (negative) value = value * BigInt.from(-1);
|
||||
|
||||
var fraction = value
|
||||
.modPow(BigInt.one, BigInt.parse(multiplier))
|
||||
.toString()
|
||||
.padLeft(decimals, "0");
|
||||
var fraction =
|
||||
value.modPow(BigInt.one, BigInt.parse(multiplier)).toString().padLeft(decimals, "0");
|
||||
|
||||
if (fractionalDigits < 0) fractionalDigits = 0;
|
||||
if (fractionalDigits > decimals) fractionalDigits = decimals;
|
||||
|
||||
@@ -6,7 +6,8 @@ import 'dart:convert';
|
||||
|
||||
// FIXME: Hardcoded values; Works only for monero
|
||||
|
||||
int getMoneroHeigthByDate({required DateTime date}) => MoneroHeight.getBlockHeightByTime(date..subtract(Duration(days: 1)));
|
||||
int getMoneroHeigthByDate({required DateTime date}) =>
|
||||
MoneroHeight.getBlockHeightByTime(date..subtract(Duration(days: 1)));
|
||||
|
||||
const havenDates = {
|
||||
"2023-05": 1352995,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
class MoneroHeight {
|
||||
// for i in $(seq 1 364); do echo "_Checkpoint(_t($(curl https://xmrchain.net/block/${i}0000 | grep -E '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}' | xargs | sed 's/ /\n/g' | head -5 | tail -1 | tr '()' ' ' | sed 's/ /\n/g' | head -2 | xargs)), ${i}0000),"; done > points.txt
|
||||
static final List<_Checkpoint> _points = [
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
const CONTACT_TYPE_ID = 0;
|
||||
const NODE_TYPE_ID = 1;
|
||||
const TRANSACTION_TYPE_ID = 2;
|
||||
const TRADE_TYPE_ID = 3;
|
||||
const WALLET_INFO_TYPE_ID = 4;
|
||||
const WALLET_TYPE_TYPE_ID = 5;
|
||||
const TEMPLATE_TYPE_ID = 6;
|
||||
const EXCHANGE_TEMPLATE_TYPE_ID = 7;
|
||||
const ORDER_TYPE_ID = 8;
|
||||
const UNSPENT_COINS_INFO_TYPE_ID = 9;
|
||||
const ANONPAY_INVOICE_INFO_TYPE_ID = 10;
|
||||
const ADDRESS_INFO_TYPE_ID = 11;
|
||||
const ERC20_TOKEN_TYPE_ID = 12;
|
||||
const NANO_ACCOUNT_TYPE_ID = 13;
|
||||
const POW_NODE_TYPE_ID = 14;
|
||||
const DERIVATION_TYPE_TYPE_ID = 15;
|
||||
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;
|
||||
const HAVEN_SEED_STORE_TYPE_ID = 21;
|
||||
const ZANO_ASSET_TYPE_ID = 22;
|
||||
const PAYJOIN_SESSION_TYPE_ID = 23;
|
||||
const CONTACT_TYPE_ID = 0;
|
||||
const NODE_TYPE_ID = 1;
|
||||
const TRANSACTION_TYPE_ID = 2;
|
||||
const TRADE_TYPE_ID = 3;
|
||||
const WALLET_INFO_TYPE_ID = 4;
|
||||
const WALLET_TYPE_TYPE_ID = 5;
|
||||
const TEMPLATE_TYPE_ID = 6;
|
||||
const EXCHANGE_TEMPLATE_TYPE_ID = 7;
|
||||
const ORDER_TYPE_ID = 8;
|
||||
const UNSPENT_COINS_INFO_TYPE_ID = 9;
|
||||
const ANONPAY_INVOICE_INFO_TYPE_ID = 10;
|
||||
const ADDRESS_INFO_TYPE_ID = 11;
|
||||
const ERC20_TOKEN_TYPE_ID = 12;
|
||||
const NANO_ACCOUNT_TYPE_ID = 13;
|
||||
const POW_NODE_TYPE_ID = 14;
|
||||
const DERIVATION_TYPE_TYPE_ID = 15;
|
||||
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;
|
||||
const HAVEN_SEED_STORE_TYPE_ID = 21;
|
||||
const ZANO_ASSET_TYPE_ID = 22;
|
||||
const PAYJOIN_SESSION_TYPE_ID = 23;
|
||||
|
||||
@@ -16,7 +16,8 @@ List<String> extractKeys(String key) {
|
||||
return [_key, iv];
|
||||
}
|
||||
|
||||
Future<String> encode({required encrypt.Key key, required encrypt.IV iv, required String data}) async {
|
||||
Future<String> encode(
|
||||
{required encrypt.Key key, required encrypt.IV iv, required String data}) async {
|
||||
final encrypter = encrypt.Encrypter(encrypt.Salsa20(key));
|
||||
final encrypted = encrypter.encrypt(data, iv: iv);
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
mixin Keyable {
|
||||
dynamic keyIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,11 @@ bool isBolt11ZeroInvoice(String invoice) {
|
||||
try {
|
||||
final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
|
||||
|
||||
|
||||
final prefix = _BOLT_PREFIXES
|
||||
.firstWhere((prefix) => request.hrp.startsWith(prefix), orElse: () => "");
|
||||
final prefix =
|
||||
_BOLT_PREFIXES.firstWhere((prefix) => request.hrp.startsWith(prefix), orElse: () => "");
|
||||
|
||||
return request.hrp.length == prefix.length;
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +26,7 @@ int? _getAmountBolt11Msat(String invoice) {
|
||||
final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
|
||||
|
||||
final prefix = _BOLT_PREFIXES.firstWhere(
|
||||
(prefix) => request.hrp.startsWith(prefix),
|
||||
(prefix) => request.hrp.startsWith(prefix),
|
||||
orElse: () => throw FormatException('Invalid BOLT11 invoice: unknown HRP prefix.'),
|
||||
);
|
||||
|
||||
@@ -86,8 +85,7 @@ class LNURL {
|
||||
final maxSendable = body["maxSendable"] as int?;
|
||||
|
||||
// if minSendable and maxSendable are the same we assume a specific payment request
|
||||
if (msat != maxSendable)
|
||||
return null;
|
||||
if (msat != maxSendable) return null;
|
||||
|
||||
if (msat == null || msat % 1000 != 0) return null;
|
||||
return Money.fromInt(msat ~/ 1000, CryptoCurrency.btcln);
|
||||
@@ -136,8 +134,7 @@ class LNURL {
|
||||
String _findLnUrl(String input) {
|
||||
final res = RegExp(r',*?((lnurl)([0-9]+[a-z0-9]+))').allMatches(input.toLowerCase());
|
||||
|
||||
if (res.length != 1)
|
||||
throw ArgumentError('Not a valid lnurl string');
|
||||
if (res.length != 1) throw ArgumentError('Not a valid lnurl string');
|
||||
return res.first.group(0)!;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
class MoneroWalletKeys {
|
||||
const MoneroWalletKeys(
|
||||
{required this.primaryAddress,
|
||||
required this.privateSpendKey,
|
||||
required this.privateViewKey,
|
||||
required this.publicSpendKey,
|
||||
required this.publicViewKey,
|
||||
required this.passphrase});
|
||||
required this.privateSpendKey,
|
||||
required this.privateViewKey,
|
||||
required this.publicSpendKey,
|
||||
required this.publicViewKey,
|
||||
required this.passphrase});
|
||||
|
||||
final String primaryAddress;
|
||||
final String publicViewKey;
|
||||
@@ -13,4 +13,4 @@ class MoneroWalletKeys {
|
||||
final String publicSpendKey;
|
||||
final String privateSpendKey;
|
||||
final String passphrase;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,5 @@ class MwebUtxoAdapter extends TypeAdapter<MwebUtxo> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is MwebUtxoAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is MwebUtxoAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,5 @@ class NanoAccountAdapter extends TypeAdapter<NanoAccount> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is NanoAccountAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is NanoAccountAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
+30
-34
@@ -25,11 +25,9 @@ Future<void> validateBuiltinNodes() async {
|
||||
for (final listNode in builtinFromList) {
|
||||
// preserve proxy settings from user
|
||||
try {
|
||||
final matchedDbNode = builtinFromDb.firstWhere(
|
||||
(dbNode) => dbNode.uri == listNode.uri
|
||||
);
|
||||
final matchedDbNode = builtinFromDb.firstWhere((dbNode) => dbNode.uri == listNode.uri);
|
||||
listNode.socksProxyAddress = matchedDbNode.socksProxyAddress;
|
||||
} catch(e) {}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
final dbSet = builtinFromDb.toSet();
|
||||
@@ -121,7 +119,7 @@ class Node {
|
||||
"label": label,
|
||||
'password': password,
|
||||
"isPow": isPow ? 1 : 0,
|
||||
'useSSL': (useSSL??false) ? 1 : 0,
|
||||
'useSSL': (useSSL ?? false) ? 1 : 0,
|
||||
"typeRaw": typeRaw,
|
||||
'trusted': trusted ? 1 : 0,
|
||||
'socksProxyAddress': socksProxyAddress,
|
||||
@@ -165,7 +163,6 @@ class Node {
|
||||
}
|
||||
|
||||
Future<int> save() async {
|
||||
|
||||
final json = toMap();
|
||||
if (json[selfIdColumn] == 0) {
|
||||
json[selfIdColumn] = null;
|
||||
@@ -174,9 +171,9 @@ class Node {
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
static Future<List<Node>> selectList(String where, List<dynamic> whereArgs, {String? orderBy}) async {
|
||||
if(orderBy == null) {
|
||||
static Future<List<Node>> selectList(String where, List<dynamic> whereArgs,
|
||||
{String? orderBy}) async {
|
||||
if (orderBy == null) {
|
||||
orderBy = selfIdColumn;
|
||||
}
|
||||
final list = await db!.query(
|
||||
@@ -189,7 +186,7 @@ class Node {
|
||||
}
|
||||
|
||||
static Future<Node?> select(String where, List<dynamic> whereArgs, {String? orderBy}) async {
|
||||
if(orderBy == null) {
|
||||
if (orderBy == null) {
|
||||
orderBy = selfIdColumn;
|
||||
}
|
||||
final list = await db!.query(
|
||||
@@ -201,7 +198,6 @@ class Node {
|
||||
return list.isEmpty ? null : Node.fromMap(list.first);
|
||||
}
|
||||
|
||||
|
||||
static Future<List<Node>> getAll() async {
|
||||
return selectList("isPow = ?", [0]);
|
||||
}
|
||||
@@ -219,11 +215,15 @@ class Node {
|
||||
}
|
||||
|
||||
static Future<Node?> getDefaultForWalletType(WalletType type) async {
|
||||
return (await selectList("typeRaw = ? AND isPow = ? AND isDefault = ?", [serializeToInt(type), 0, 1])).firstOrNull;
|
||||
return (await selectList(
|
||||
"typeRaw = ? AND isPow = ? AND isDefault = ?", [serializeToInt(type), 0, 1]))
|
||||
.firstOrNull;
|
||||
}
|
||||
|
||||
static Future<Node?> getDefaultPowForWalletType(WalletType type) async {
|
||||
return (await selectList("typeRaw = ? AND isPow = ? AND isDefault = ?", [serializeToInt(type), 1, 1])).firstOrNull;
|
||||
return (await selectList(
|
||||
"typeRaw = ? AND isPow = ? AND isDefault = ?", [serializeToInt(type), 1, 1]))
|
||||
.firstOrNull;
|
||||
}
|
||||
|
||||
static Future<List<Node>> getAllForWalletTypePow(WalletType type) async {
|
||||
@@ -238,7 +238,6 @@ class Node {
|
||||
return select("${selfIdColumn} = ?", [id]);
|
||||
}
|
||||
|
||||
|
||||
int id;
|
||||
late String uriRaw;
|
||||
String? login;
|
||||
@@ -262,7 +261,6 @@ class Node {
|
||||
static String get tableName => "Node";
|
||||
static String get selfIdColumn => "${tableName}Id";
|
||||
|
||||
|
||||
bool get isSSL => useSSL ?? false;
|
||||
|
||||
bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty;
|
||||
@@ -333,7 +331,6 @@ class Node {
|
||||
socksProxyAddress.hashCode ^
|
||||
path.hashCode;
|
||||
|
||||
|
||||
WalletType get type => deserializeFromInt(typeRaw);
|
||||
|
||||
set type(WalletType type) => typeRaw = serializeToInt(type);
|
||||
@@ -387,7 +384,6 @@ class Node {
|
||||
body: jsonBody,
|
||||
);
|
||||
|
||||
|
||||
final resBody = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
return resBody['result']['height'] != null;
|
||||
@@ -407,15 +403,13 @@ class Node {
|
||||
final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName};
|
||||
|
||||
try {
|
||||
|
||||
final jsonBody = json.encode(body);
|
||||
|
||||
final response = await ProxyWrapper().post(
|
||||
clearnetUri: rpcUri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonBody,
|
||||
allowMitmMoneroBypassSSLCheck: true
|
||||
);
|
||||
clearnetUri: rpcUri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonBody,
|
||||
allowMitmMoneroBypassSSLCheck: true);
|
||||
// Check if we received a 401 Unauthorized response
|
||||
if (response.statusCode == 401) {
|
||||
final daemonRpc = DaemonRpc(
|
||||
@@ -494,7 +488,6 @@ class Node {
|
||||
final ProxySocket socket;
|
||||
socket = await ProxyWrapper().getSocksSocket(useSSL ?? false, uri.host, uri.port);
|
||||
|
||||
|
||||
socket.destroy();
|
||||
return true;
|
||||
} catch (_) {
|
||||
@@ -531,9 +524,12 @@ class Node {
|
||||
|
||||
Future<bool> requestEthereumServer() async {
|
||||
try {
|
||||
final req = await ProxyWrapper().getHttpClient()
|
||||
.getUrl(uri,)
|
||||
.timeout(Duration(seconds: 15));
|
||||
final req = await ProxyWrapper()
|
||||
.getHttpClient()
|
||||
.getUrl(
|
||||
uri,
|
||||
)
|
||||
.timeout(Duration(seconds: 15));
|
||||
final response = await req.close();
|
||||
|
||||
return response.statusCode >= 200 && response.statusCode < 300;
|
||||
@@ -544,13 +540,13 @@ class Node {
|
||||
}
|
||||
|
||||
Future<bool> requestDecredNode() async {
|
||||
if (uri.host == "default-spv-nodes") {
|
||||
// Just show default port as ok. The wallet will connect to a list of known
|
||||
// nodes automatically.
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
final socket = await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5));
|
||||
if (uri.host == "default-spv-nodes") {
|
||||
// Just show default port as ok. The wallet will connect to a list of known
|
||||
// nodes automatically.
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
final socket = await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5));
|
||||
socket.destroy();
|
||||
return true;
|
||||
} catch (_) {
|
||||
|
||||
@@ -19,17 +19,15 @@ import 'node.dart' as node_new;
|
||||
part 'node_legacy.part.dart';
|
||||
|
||||
Future<void> performNodeHiveMigration() async {
|
||||
if(!CakeHive.isAdapterRegistered(Node.typeId)) {
|
||||
if (!CakeHive.isAdapterRegistered(Node.typeId)) {
|
||||
CakeHive.registerAdapter(NodeAdapter());
|
||||
}
|
||||
|
||||
final nodeBox = await CakeHive.openBox<Node>(Node.boxName);
|
||||
final powNodeBox = await CakeHive.openBox<Node>(Node.boxName+"pow");
|
||||
final powNodeBox = await CakeHive.openBox<Node>(Node.boxName + "pow");
|
||||
final builtinNodes = await loadAllDefaultNodes();
|
||||
final builtinPowNodes = await loadDefaultNanoPowNodes();
|
||||
await Node.migrateAllToSqlite(nodeBox, powNodeBox, builtinNodes, builtinPowNodes);
|
||||
|
||||
|
||||
}
|
||||
|
||||
Uri createUriFromElectrumAddress(String address, String path) =>
|
||||
@@ -62,31 +60,26 @@ class Node extends HiveObject with Keyable {
|
||||
final list = nodeBox.values.toList();
|
||||
final powList = powNodeBox.values.toList();
|
||||
for (final node in list) {
|
||||
if(defaultNodes.any((item)=>item.uri == node.uri)) {
|
||||
if (defaultNodes.any((item) => item.uri == node.uri)) {
|
||||
// default nodes will be added by validateBuiltinNodes(), no need to migrate them.
|
||||
await node.delete();
|
||||
continue;
|
||||
}
|
||||
await node.migrateToSqlite(
|
||||
isPow: false,
|
||||
isBuiltin: false,
|
||||
isOfficial: false);
|
||||
await node.migrateToSqlite(isPow: false, isBuiltin: false, isOfficial: false);
|
||||
await node.delete();
|
||||
}
|
||||
for (final node in powList) {
|
||||
if(defaultPowNodes.any((item)=>item.uri == node.uri)) {
|
||||
if (defaultPowNodes.any((item) => item.uri == node.uri)) {
|
||||
await node.delete();
|
||||
continue;
|
||||
}
|
||||
await node.migrateToSqlite(
|
||||
isPow: true,
|
||||
isBuiltin: false,
|
||||
isOfficial: false);
|
||||
await node.migrateToSqlite(isPow: true, isBuiltin: false, isOfficial: false);
|
||||
await node.delete();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> migrateToSqlite({required bool isPow, required bool isBuiltin, required bool isOfficial}) async {
|
||||
Future<void> migrateToSqlite(
|
||||
{required bool isPow, required bool isBuiltin, required bool isOfficial}) async {
|
||||
final newNode = node_new.Node(
|
||||
id: key as int,
|
||||
login: login,
|
||||
@@ -106,7 +99,6 @@ class Node extends HiveObject with Keyable {
|
||||
await newNode.save();
|
||||
}
|
||||
|
||||
|
||||
Node.fromMap(Map<String, Object?> map)
|
||||
: uriRaw = map['uri'] as String? ?? '',
|
||||
path = map['path'] as String? ?? '',
|
||||
@@ -208,15 +200,15 @@ class Node extends HiveObject with Keyable {
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is Node &&
|
||||
(other.uriRaw == uriRaw &&
|
||||
other.login == login &&
|
||||
other.label == label &&
|
||||
other.password == password &&
|
||||
other.typeRaw == typeRaw &&
|
||||
other.useSSL == useSSL &&
|
||||
other.trusted == trusted &&
|
||||
other.socksProxyAddress == socksProxyAddress &&
|
||||
other.path == path);
|
||||
(other.uriRaw == uriRaw &&
|
||||
other.login == login &&
|
||||
other.label == label &&
|
||||
other.password == password &&
|
||||
other.typeRaw == typeRaw &&
|
||||
other.useSSL == useSSL &&
|
||||
other.trusted == trusted &&
|
||||
other.socksProxyAddress == socksProxyAddress &&
|
||||
other.path == path);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
@@ -241,6 +233,4 @@ class Node extends HiveObject with Keyable {
|
||||
set type(WalletType type) => typeRaw = serializeToInt(type);
|
||||
|
||||
dynamic _keyIndex;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@ class NodeAdapter extends TypeAdapter<Node> {
|
||||
trusted: fields[5] == null ? false : fields[5] as bool,
|
||||
socksProxyAddress: fields[6] as String?,
|
||||
path: fields[7] == null ? '' : fields[7] as String?,
|
||||
isEnabledForAutoSwitching:
|
||||
fields[11] == null ? false : fields[11] as bool,
|
||||
isEnabledForAutoSwitching: fields[11] == null ? false : fields[11] as bool,
|
||||
)
|
||||
..uriRaw = fields[0] == null ? '' : fields[0] as String
|
||||
..typeRaw = fields[3] == null ? 0 : fields[3] as int
|
||||
@@ -72,7 +71,5 @@ class NodeAdapter extends TypeAdapter<Node> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is NodeAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is NodeAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ Future<List<Node>> loadDefaultNodes(WalletType type) async {
|
||||
case WalletType.haven:
|
||||
path = 'assets/haven_node_list.yml';
|
||||
break;
|
||||
// TODO: (refactoring) each wallet would have its path, so `wallet.nodePath` would be decided based on chain id in Evm wallet
|
||||
// TODO: (refactoring) each wallet would have its path, so `wallet.nodePath` would be decided based on chain id in Evm wallet
|
||||
case WalletType.ethereum:
|
||||
path = 'assets/ethereum_server_list.yml';
|
||||
break;
|
||||
@@ -147,7 +147,6 @@ Future<List<Node>> loadAllDefaultNodes() async {
|
||||
}
|
||||
|
||||
Future<void> resetToDefault() async {
|
||||
|
||||
final nodes = await loadAllDefaultNodes();
|
||||
|
||||
final currentNodes = await Node.getAll();
|
||||
@@ -157,15 +156,14 @@ Future<void> resetToDefault() async {
|
||||
for (final node in nodes) {
|
||||
try {
|
||||
final curr = currentNodes.firstWhere(
|
||||
(item) => item.uri == node.uri && item.typeRaw == node.typeRaw,
|
||||
(item) => item.uri == node.uri && item.typeRaw == node.typeRaw,
|
||||
);
|
||||
node.id = curr.id;
|
||||
} catch(e) {}
|
||||
|
||||
node.id = curr.id;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
await Node.deleteAll();
|
||||
for(final node in nodes) {
|
||||
for (final node in nodes) {
|
||||
await node.save();
|
||||
}
|
||||
}
|
||||
@@ -173,7 +171,7 @@ Future<void> resetToDefault() async {
|
||||
Future<void> resetPowToDefault() async {
|
||||
final nanoPowNodes = await loadDefaultNanoPowNodes();
|
||||
await Node.deleteAllPow();
|
||||
for(final node in nanoPowNodes) {
|
||||
for (final node in nanoPowNodes) {
|
||||
node.save();
|
||||
}
|
||||
}
|
||||
@@ -181,12 +179,9 @@ Future<void> resetPowToDefault() async {
|
||||
Future<Node> getDefaultNodeFromFiles(WalletType type, {bool isPow = false}) async {
|
||||
final nodes = isPow ? await loadDefaultNanoPowNodes() : await loadDefaultNodes(type);
|
||||
try {
|
||||
return nodes.firstWhere((item)=>item.isDefault);
|
||||
} catch(e) {
|
||||
return nodes.firstWhere((item) => item.isDefault);
|
||||
} catch (e) {
|
||||
// ideally the yamls would always have a default, but this is better than nothing
|
||||
return nodes.first;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
bool parseBoolFromString(String string) {
|
||||
return string.toString() == 'true';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,8 @@ BigInt parseFixed(String value, int decimals) {
|
||||
var fraction = (comps.length == 2 ? comps[1] : "0").padRight(decimals, "0");
|
||||
|
||||
if (fraction.length > multiplier.length - 1) {
|
||||
throw FormatException("fractional component(${fraction.length}) exceeds decimals(${decimals}), underflow, parseFixed");
|
||||
throw FormatException(
|
||||
"fractional component(${fraction.length}) exceeds decimals(${decimals}), underflow, parseFixed");
|
||||
}
|
||||
|
||||
final wholeValue = BigInt.parse(whole);
|
||||
|
||||
@@ -28,8 +28,7 @@ Future<String> pathForWalletDir({required String name, required WalletType type}
|
||||
}
|
||||
|
||||
Future<String> pathForWallet({required String name, required WalletType type}) async =>
|
||||
await pathForWalletDir(name: name, type: type)
|
||||
.then((path) => path + '/$name');
|
||||
await pathForWalletDir(name: name, type: type).then((path) => path + '/$name');
|
||||
|
||||
Future<String> outdatedAndroidPathForWalletDir({required String name}) async {
|
||||
final directory = await getAppDir();
|
||||
|
||||
@@ -56,7 +56,6 @@ class PayjoinSession extends HiveObject {
|
||||
|
||||
BigInt get amount => BigInt.parse(rawAmount ?? "0");
|
||||
set amount(BigInt amount) => rawAmount = amount.toString();
|
||||
|
||||
}
|
||||
|
||||
enum PayjoinSessionStatus {
|
||||
|
||||
@@ -59,7 +59,5 @@ class PayjoinSessionAdapter extends TypeAdapter<PayjoinSession> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is PayjoinSessionAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is PayjoinSessionAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
@@ -408,14 +408,14 @@ class ERC681URI extends PaymentURI {
|
||||
|
||||
static int _getChainID(String path) {
|
||||
return int.parse(RegExp(
|
||||
r'@\d*',
|
||||
).firstMatch(path)?.group(0)?.replaceAll("@", "") ??
|
||||
r'@\d*',
|
||||
).firstMatch(path)?.group(0)?.replaceAll("@", "") ??
|
||||
"1");
|
||||
}
|
||||
|
||||
static (bool, String) _getTargetAddress(String path) {
|
||||
final targetAddress =
|
||||
RegExp(r'^(0x)?[0-9a-f]{40}', caseSensitive: false).firstMatch(path)!.group(0)!;
|
||||
RegExp(r'^(0x)?[0-9a-f]{40}', caseSensitive: false).firstMatch(path)!.group(0)!;
|
||||
return (path.contains("/"), targetAddress);
|
||||
}
|
||||
|
||||
@@ -508,4 +508,3 @@ class LightningPaymentRequest extends PaymentURI {
|
||||
@override
|
||||
String toString() => bolt11Invoice ?? "lightning:$lnURL";
|
||||
}
|
||||
|
||||
|
||||
+16
-12
@@ -23,12 +23,11 @@ bool get isNonAmnesticTails {
|
||||
|
||||
bool showNotice = true;
|
||||
|
||||
void setRootDirFromEnv() =>
|
||||
_rootDirPath = Platform.environment['CAKE_WALLET_DIR'];
|
||||
void setRootDirFromEnv() => _rootDirPath = Platform.environment['CAKE_WALLET_DIR'];
|
||||
|
||||
void copyDirectory(Directory source, Directory destination) {
|
||||
source.listSync(recursive: false).forEach((var entity) {
|
||||
if (entity is Directory) {
|
||||
if (entity is Directory) {
|
||||
var newDirectory = Directory(p.join(destination.absolute.path, p.basename(entity.path)));
|
||||
newDirectory.createSync(recursive: true);
|
||||
copyDirectory(entity.absolute, newDirectory);
|
||||
@@ -41,7 +40,8 @@ void copyDirectory(Directory source, Directory destination) {
|
||||
|
||||
Future<void> linuxSymlinkSharedPreferences() async {
|
||||
if (!Platform.isLinux) return; // nuh-uh
|
||||
final dataHome = Platform.environment["XDG_DATA_HOME"] ?? p.join(Platform.environment["HOME"] ?? "", ".local", "share");
|
||||
final dataHome = Platform.environment["XDG_DATA_HOME"] ??
|
||||
p.join(Platform.environment["HOME"] ?? "", ".local", "share");
|
||||
var cakeNames = ['com.example.cake_wallet', 'cake_wallet'];
|
||||
for (String name in cakeNames) {
|
||||
final oldPath = p.join(dataHome, name);
|
||||
@@ -53,8 +53,8 @@ Future<void> linuxSymlinkSharedPreferences() async {
|
||||
if (oldLink.existsSync()) {
|
||||
printV("not creating, link exists");
|
||||
} else {
|
||||
if (newDir.existsSync()) {
|
||||
newDir.renameSync("${newPath}_${DateTime.now().millisecondsSinceEpoch~/1000}");
|
||||
if (newDir.existsSync()) {
|
||||
newDir.renameSync("${newPath}_${DateTime.now().millisecondsSinceEpoch ~/ 1000}");
|
||||
}
|
||||
copyDirectory(oldDir, newDir);
|
||||
oldDir.deleteSync(recursive: true);
|
||||
@@ -94,14 +94,16 @@ Future<Directory> getAppDir() async {
|
||||
// for storing things that users in general back-up
|
||||
var linuxAppPath = [
|
||||
if (appDirPath != null) appDirPath, // old preferred
|
||||
p.join('/home', Platform.environment['USER']??"null", appName), // old fallback
|
||||
if (Platform.environment['HOME'] != null) p.join(Platform.environment['HOME']!, ".$appName"), // old fallback but using HOME
|
||||
if (Platform.environment['HOME'] != null) p.join(Platform.environment['HOME']!, '.config', appName), // old fallback but using HOME
|
||||
p.join('/home', Platform.environment['USER'] ?? "null", appName), // old fallback
|
||||
if (Platform.environment['HOME'] != null)
|
||||
p.join(Platform.environment['HOME']!, ".$appName"), // old fallback but using HOME
|
||||
if (Platform.environment['HOME'] != null)
|
||||
p.join(Platform.environment['HOME']!, '.config', appName), // old fallback but using HOME
|
||||
if (isNonAmnesticTails) p.join(_tailsData, ".$appName") // tails (if persistance is enabled)
|
||||
];
|
||||
|
||||
String preferredPath = linuxAppPath.last;
|
||||
|
||||
|
||||
preferredLoop:
|
||||
for (String notSoPreferredPath in linuxAppPath) {
|
||||
if (notSoPreferredPath == linuxAppPath.last) continue;
|
||||
@@ -109,8 +111,10 @@ Future<Directory> getAppDir() async {
|
||||
if (useThisOne) {
|
||||
if (showNotice) {
|
||||
showNotice = false;
|
||||
printV("Not using $preferredPath because $notSoPreferredPath exists, falling back for backwards compatibility");
|
||||
printV("Can't see your wallet? Check\n - ${linuxAppPath.join("\n - ")}\n and move directory that to $preferredPath");
|
||||
printV(
|
||||
"Not using $preferredPath because $notSoPreferredPath exists, falling back for backwards compatibility");
|
||||
printV(
|
||||
"Can't see your wallet? Check\n - ${linuxAppPath.join("\n - ")}\n and move directory that to $preferredPath");
|
||||
printV("Or use CAKE_WALLET_DIR=/path/to/app/ ${Platform.executable}");
|
||||
}
|
||||
preferredPath = notSoPreferredPath;
|
||||
|
||||
@@ -14,7 +14,8 @@ Future<Uint8List> secRandom(int count) async {
|
||||
final rng = Random.secure();
|
||||
return Uint8List.fromList(List<int>.generate(count, (_) => rng.nextInt(byteSize)));
|
||||
}
|
||||
return await utils.invokeMethod<Uint8List>('sec_random', {'count': count}) ?? Uint8List.fromList([]);
|
||||
return await utils.invokeMethod<Uint8List>('sec_random', {'count': count}) ??
|
||||
Uint8List.fromList([]);
|
||||
} on PlatformException catch (_) {
|
||||
return Uint8List.fromList([]);
|
||||
}
|
||||
|
||||
@@ -4,14 +4,12 @@ import 'package:on_chain/solana/solana.dart';
|
||||
|
||||
class SolanaRPCHTTPService implements SolanaJSONRPCService {
|
||||
SolanaRPCHTTPService(
|
||||
{required this.url,
|
||||
this.defaultRequestTimeout = const Duration(seconds: 30)});
|
||||
{required this.url, this.defaultRequestTimeout = const Duration(seconds: 30)});
|
||||
@override
|
||||
final String url;
|
||||
final Duration defaultRequestTimeout;
|
||||
|
||||
Future<Map<String, dynamic>> call(SolanaRequestDetails params,
|
||||
[Duration? timeout]) async {
|
||||
Future<Map<String, dynamic>> call(SolanaRequestDetails params, [Duration? timeout]) async {
|
||||
final response = await ProxyWrapper().post(
|
||||
clearnetUri: Uri.parse(url),
|
||||
body: params.toRequestBody(),
|
||||
|
||||
@@ -58,7 +58,5 @@ class SPLTokenAdapter extends TypeAdapter<SPLToken> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is SPLTokenAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is SPLTokenAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ TransactionDirection parseTransactionDirectionFromInt(int raw) {
|
||||
case 1:
|
||||
return TransactionDirection.outgoing;
|
||||
default:
|
||||
throw Exception('Unexpected token: raw for TransactionDirection parseTransactionDirectionFromInt');
|
||||
throw Exception(
|
||||
'Unexpected token: raw for TransactionDirection parseTransactionDirectionFromInt');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +19,7 @@ TransactionDirection parseTransactionDirectionFromNumber(String raw) {
|
||||
case "1":
|
||||
return TransactionDirection.outgoing;
|
||||
default:
|
||||
throw Exception('Unexpected token: raw for TransactionDirection parseTransactionDirectionFromNumber');
|
||||
throw Exception(
|
||||
'Unexpected token: raw for TransactionDirection parseTransactionDirectionFromNumber');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
|
||||
abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
|
||||
TransactionHistoryBase()
|
||||
: transactions = ObservableMap<String, TransactionType>();
|
||||
TransactionHistoryBase() : transactions = ObservableMap<String, TransactionType>();
|
||||
|
||||
@observable
|
||||
ObservableMap<String, TransactionType> transactions;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
|
||||
abstract class TransactionPriority extends EnumerableItem<int>
|
||||
with Serializable<int> {
|
||||
abstract class TransactionPriority extends EnumerableItem<int> with Serializable<int> {
|
||||
final String? description;
|
||||
final String? hint;
|
||||
|
||||
const TransactionPriority({required String title, required int raw, this.description, this.hint}) : super(title: title, raw: raw);
|
||||
const TransactionPriority({required String title, required int raw, this.description, this.hint})
|
||||
: super(title: title, raw: raw);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,5 @@ class TronTokenAdapter extends TypeAdapter<TronToken> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is TronTokenAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is TronTokenAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ Future<void> write({required String path, required String password, required Str
|
||||
writeData(path: path, password: password, data: data);
|
||||
|
||||
Future<void> writeData(
|
||||
{required String path,
|
||||
required String password,
|
||||
required String data}) async {
|
||||
{required String path, required String password, required String data}) async {
|
||||
final keys = extractKeys(password);
|
||||
final key = encrypt.Key.fromBase64(keys.first);
|
||||
final iv = encrypt.IV.fromBase64(keys.last);
|
||||
|
||||
@@ -11,7 +11,7 @@ enum RequestMethod {
|
||||
post,
|
||||
put,
|
||||
delete,
|
||||
|
||||
|
||||
newHttpClient,
|
||||
newHttpIOClient,
|
||||
newProxySocket,
|
||||
@@ -23,7 +23,7 @@ abstract class ProxyLogger {
|
||||
required RequestMethod method,
|
||||
required Uint8List body,
|
||||
required very_insecure_http_do_not_use.Response? response,
|
||||
required RequestNetwork network,
|
||||
required RequestNetwork network,
|
||||
required String? error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,18 +46,20 @@ class MemoryProxyLogger implements ProxyLogger {
|
||||
required RequestMethod method,
|
||||
required Uint8List body,
|
||||
required very_insecure_http_do_not_use.Response? response,
|
||||
required RequestNetwork network,
|
||||
required RequestNetwork network,
|
||||
required String? error,
|
||||
}) {
|
||||
final trace = StackTrace.current;
|
||||
logs.add(MemoryProxyLoggerEntry(
|
||||
method: method,
|
||||
trace: trace,
|
||||
uri: uri,
|
||||
body: body,
|
||||
network: network,
|
||||
response: response,
|
||||
error: error,),
|
||||
logs.add(
|
||||
MemoryProxyLoggerEntry(
|
||||
method: method,
|
||||
trace: trace,
|
||||
uri: uri,
|
||||
body: body,
|
||||
network: network,
|
||||
response: response,
|
||||
error: error,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class SilentProxyLogger implements ProxyLogger {
|
||||
required RequestMethod method,
|
||||
required Uint8List body,
|
||||
required very_insecure_http_do_not_use.Response? response,
|
||||
required RequestNetwork network,
|
||||
required RequestNetwork network,
|
||||
required String? error,
|
||||
}) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,21 @@ class ProxyAddress {
|
||||
}
|
||||
|
||||
abstract class ProxySocket {
|
||||
static Future<ProxySocket> connect(bool sslEnabled, ProxyAddress address, {Duration? connectionTimeout}) async {
|
||||
static Future<ProxySocket> connect(bool sslEnabled, ProxyAddress address,
|
||||
{Duration? connectionTimeout}) async {
|
||||
if (CakeTor.instance!.started) {
|
||||
var socksSocket = await SOCKSSocket.create(
|
||||
proxyHost: InternetAddress.loopbackIPv4.address,
|
||||
proxyPort: CakeTor.instance!.port,
|
||||
sslEnabled: sslEnabled,
|
||||
proxyHost: InternetAddress.loopbackIPv4.address,
|
||||
proxyPort: CakeTor.instance!.port,
|
||||
sslEnabled: sslEnabled,
|
||||
);
|
||||
await socksSocket.connect();
|
||||
await socksSocket.connectTo(address.host, address.port);
|
||||
return ProxySocketSocks(socksSocket);
|
||||
}
|
||||
if (sslEnabled == false) {
|
||||
return ProxySocketInsecure(await Socket.connect(address.host, address.port, timeout: connectionTimeout));
|
||||
return ProxySocketInsecure(
|
||||
await Socket.connect(address.host, address.port, timeout: connectionTimeout));
|
||||
} else {
|
||||
return ProxySocketSecure(await SecureSocket.connect(
|
||||
address.host,
|
||||
@@ -43,6 +45,7 @@ abstract class ProxySocket {
|
||||
void destroy();
|
||||
void write(String data);
|
||||
bool get isClosed;
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function (Object error)? onError, Function ()? onDone, bool cancelOnError = true});
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData,
|
||||
{Function(Object error)? onError, Function()? onDone, bool cancelOnError = true});
|
||||
ProxyAddress get address;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||
import 'dart:async';
|
||||
@@ -9,15 +8,16 @@ class ProxySocketInsecure implements ProxySocket {
|
||||
final Socket socket;
|
||||
|
||||
ProxySocketInsecure(this.socket);
|
||||
|
||||
|
||||
bool isClosed = false;
|
||||
|
||||
ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||
|
||||
ProxyAddress get address =>
|
||||
ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
try {
|
||||
if (isClosed) return;
|
||||
if (isClosed) return;
|
||||
isClosed = true;
|
||||
return socket.close();
|
||||
} catch (e) {
|
||||
@@ -25,7 +25,7 @@ class ProxySocketInsecure implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void destroy() async {
|
||||
try {
|
||||
@@ -37,7 +37,7 @@ class ProxySocketInsecure implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void write(String data) {
|
||||
try {
|
||||
@@ -51,9 +51,10 @@ class ProxySocketInsecure implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData,
|
||||
{Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
return socket.listen(
|
||||
(data) {
|
||||
onData(Uint8List.fromList(data));
|
||||
@@ -63,4 +64,4 @@ class ProxySocketInsecure implements ProxySocket {
|
||||
cancelOnError: cancelOnError,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ class ProxySocketSecure implements ProxySocket {
|
||||
bool isClosed = false;
|
||||
|
||||
ProxySocketSecure(this.socket);
|
||||
|
||||
ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||
|
||||
|
||||
ProxyAddress get address =>
|
||||
ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
try {
|
||||
@@ -25,11 +26,11 @@ class ProxySocketSecure implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void destroy() async {
|
||||
try {
|
||||
if (isClosed) return;
|
||||
if (isClosed) return;
|
||||
isClosed = true;
|
||||
socket.destroy();
|
||||
} catch (e) {
|
||||
@@ -37,7 +38,7 @@ class ProxySocketSecure implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void write(String data) {
|
||||
runZonedGuarded(() {
|
||||
@@ -57,9 +58,10 @@ class ProxySocketSecure implements ProxySocket {
|
||||
printV("ProxySocketSecure: write (async error): $e");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData,
|
||||
{Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
return socket.listen(
|
||||
(data) {
|
||||
onData(Uint8List.fromList(data));
|
||||
|
||||
@@ -9,10 +9,10 @@ class ProxySocketSocks implements ProxySocket {
|
||||
final SOCKSSocket socket;
|
||||
bool isClosed = false;
|
||||
ProxySocketSocks(this.socket);
|
||||
|
||||
|
||||
@override
|
||||
ProxyAddress get address => ProxyAddress(host: socket.proxyHost, port: socket.proxyPort);
|
||||
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
try {
|
||||
@@ -24,10 +24,10 @@ class ProxySocketSocks implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void destroy() => close();
|
||||
|
||||
|
||||
@override
|
||||
void write(String data) {
|
||||
try {
|
||||
@@ -43,7 +43,8 @@ class ProxySocketSocks implements ProxySocket {
|
||||
}
|
||||
|
||||
@override
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData,
|
||||
{Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
return socket.listen(
|
||||
(data) {
|
||||
onData(Uint8List.fromList(data));
|
||||
@@ -54,4 +55,3 @@ class ProxySocketSocks implements ProxySocket {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,22 +16,23 @@ class ProxyWrapper {
|
||||
factory ProxyWrapper() {
|
||||
return _proxyWrapper;
|
||||
}
|
||||
|
||||
|
||||
ProxyWrapper._internal();
|
||||
Future<ProxySocket> getSocksSocket(bool sslEnabled, String host, int port, {Duration? connectionTimeout}) async {
|
||||
Future<ProxySocket> getSocksSocket(bool sslEnabled, String host, int port,
|
||||
{Duration? connectionTimeout}) async {
|
||||
logger?.log(
|
||||
uri: Uri(
|
||||
scheme: sslEnabled ? "https" : "http",
|
||||
host: host,
|
||||
port: port,
|
||||
),
|
||||
method: RequestMethod.newProxySocket,
|
||||
body: Uint8List(0),
|
||||
response: null,
|
||||
network: requestNetwork(),
|
||||
error: null
|
||||
);
|
||||
return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port), connectionTimeout: connectionTimeout);
|
||||
uri: Uri(
|
||||
scheme: sslEnabled ? "https" : "http",
|
||||
host: host,
|
||||
port: port,
|
||||
),
|
||||
method: RequestMethod.newProxySocket,
|
||||
body: Uint8List(0),
|
||||
response: null,
|
||||
network: requestNetwork(),
|
||||
error: null);
|
||||
return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port),
|
||||
connectionTimeout: connectionTimeout);
|
||||
}
|
||||
|
||||
RequestNetwork requestNetwork() {
|
||||
@@ -56,17 +57,17 @@ class ProxyWrapper {
|
||||
|
||||
int getPort() => CakeTor.instance!.port;
|
||||
|
||||
@Deprecated('Use ProxyWrapper().get/post/put methods instead, and provide proper clearnet and onion uri.')
|
||||
@Deprecated(
|
||||
'Use ProxyWrapper().get/post/put methods instead, and provide proper clearnet and onion uri.')
|
||||
HttpClient getHttpClient({int? portOverride, bool internal = false}) {
|
||||
if (!internal) {
|
||||
logger?.log(
|
||||
uri: null,
|
||||
method: RequestMethod.newProxySocket,
|
||||
body: Uint8List(0),
|
||||
response: null,
|
||||
network: requestNetwork(),
|
||||
error: null
|
||||
);
|
||||
uri: null,
|
||||
method: RequestMethod.newProxySocket,
|
||||
body: Uint8List(0),
|
||||
response: null,
|
||||
network: requestNetwork(),
|
||||
error: null);
|
||||
}
|
||||
if (CakeTor.instance!.started) {
|
||||
// Assign connection factory.
|
||||
@@ -84,8 +85,6 @@ class ProxyWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<Response> _make({
|
||||
required RequestMethod method,
|
||||
required ioc.IOClient client,
|
||||
@@ -216,7 +215,6 @@ class ProxyWrapper {
|
||||
|
||||
throw Exception("Unable to connect to server");
|
||||
}
|
||||
|
||||
|
||||
Future<Response> post({
|
||||
Map<String, String>? headers,
|
||||
@@ -235,7 +233,6 @@ class ProxyWrapper {
|
||||
|
||||
ioc.IOClient clearnetClient = ioc.IOClient(cleatnetHttpClient);
|
||||
|
||||
|
||||
bool torEnabled = CakeTor.instance!.started;
|
||||
|
||||
if (torEnabled) {
|
||||
@@ -439,7 +436,6 @@ class ProxyWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CakeTor {
|
||||
static CakeTorInstance? instance;
|
||||
}
|
||||
|
||||
@@ -65,4 +65,4 @@ CakeTorInstance(
|
||||
)
|
||||
""";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +30,4 @@ CakeTorDisabled(
|
||||
)
|
||||
""";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,4 @@ CakeTorSocks(
|
||||
)
|
||||
""";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,4 +88,4 @@ CakeTorTorch(
|
||||
)
|
||||
""";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user