Merge branch 'cake-tech:dev' into dev

This commit is contained in:
BSN ∞/21M
2026-07-15 09:12:41 +02:00
committed by GitHub
860 changed files with 19145 additions and 17953 deletions
-3
View File
@@ -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!
+115
View File
@@ -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
+1 -2
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -1,4 +1,5 @@
org.gradle.jvmargs=-Xmx6144M
android.useAndroidX=true
android.enableJetifier=true
android.ndk.suppressMinSdkVersionError=21
android.ndk.suppressMinSdkVersionError=21
+5 -10
View File
@@ -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");
+5 -7
View File
@@ -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";
+2 -2
View File
@@ -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;
}
}
+36 -34
View File
@@ -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;
+3 -5
View File
@@ -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;
+5 -8
View File
@@ -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};
}
+13 -11
View File
@@ -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!,
+17 -22
View File
@@ -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);
+67 -100
View File
@@ -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;
+119 -113
View File
@@ -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;
+1 -1
View File
@@ -30,7 +30,7 @@ class BitcoinTransactionCommitFailed extends TransactionCommitFailed {
@override
String toString() {
return errorMessage??"unknown error";
return errorMessage ?? "unknown error";
}
}
+2 -1
View File
@@ -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,
+17 -15
View File
@@ -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;
+30 -26
View File
@@ -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,
));
}
+13 -7
View File
@@ -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();
+9 -8
View File
@@ -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),
);
+13 -18
View File
@@ -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});
+14 -20
View File
@@ -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();
}
+19 -38
View File
@@ -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(),
+14 -17
View File
@@ -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()));
+1 -3
View File
@@ -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) {
+4 -3
View File
@@ -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++) {
+2 -5
View File
@@ -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
+1 -1
View File
@@ -52,4 +52,4 @@ String generateP2TRAddress({
}) =>
ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey)
.toTaprootAddress()
.toAddress(network);
.toAddress(network);
+1 -1
View File
@@ -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:
-4
View File
@@ -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';
+1 -1
View File
@@ -9,4 +9,4 @@ class Account {
final int id;
final String label;
final String? balance;
}
}
-1
View File
@@ -1,7 +1,6 @@
import 'package:mobx/mobx.dart';
abstract class AccountList<T> {
ObservableList<T> get accounts;
void update();
+1 -3
View File
@@ -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;
}
-1
View File
@@ -10,7 +10,6 @@ String _smartAmountSanitizer(String amount) {
return amount.replaceAll(",", ".");
}
extension SanitizerUserAmounts on String {
String sanitized() => _smartAmountSanitizer(this);
}
+4 -5
View File
@@ -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;
+13 -17
View File
@@ -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
View File
@@ -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,
});
}
}
-1
View File
@@ -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
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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(() {
+27 -28
View File
@@ -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);
}
}
+2 -4
View File
@@ -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;
}
+1 -1
View File
@@ -69,4 +69,4 @@ class WalletDeprecationException implements Exception {
String toString() => "Wallet type no longer supported";
WalletDeprecationException({required this.seed, required this.curr});
}
}
+3 -3
View File
@@ -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;
}
}
+3 -6
View File
@@ -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;
+2 -1
View File
@@ -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
View File
@@ -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 = [
+24 -24
View File
@@ -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;
+2 -1
View File
@@ -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 -1
View File
@@ -1,3 +1,3 @@
mixin Keyable {
dynamic keyIndex;
}
}
+6 -9
View File
@@ -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)!;
}
+6 -6
View File
@@ -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;
}
}
+1 -3
View File
@@ -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;
}
+1 -3
View File
@@ -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
View File
@@ -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 (_) {
+18 -28
View File
@@ -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;
}
}
+2 -5
View File
@@ -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;
}
+8 -13
View File
@@ -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 -1
View File
@@ -1,3 +1,3 @@
bool parseBoolFromString(String string) {
return string.toString() == 'true';
}
}
+2 -1
View File
@@ -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);
+1 -2
View File
@@ -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();
-1
View File
@@ -56,7 +56,6 @@ class PayjoinSession extends HiveObject {
BigInt get amount => BigInt.parse(rawAmount ?? "0");
set amount(BigInt amount) => rawAmount = amount.toString();
}
enum PayjoinSessionStatus {
+1 -3
View File
@@ -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;
}
+3 -4
View File
@@ -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
View File
@@ -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;
+2 -1
View File
@@ -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([]);
}
+2 -4
View File
@@ -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(),
+1 -3
View File
@@ -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;
}
+5 -3
View File
@@ -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');
}
}
}
+1 -2
View File
@@ -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;
+3 -3
View File
@@ -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);
}
+1 -3
View File
@@ -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;
}
+1 -3
View File
@@ -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);
+3 -3
View File
@@ -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,
}) {}
}
}
+10 -7
View File
@@ -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;
}
}
+11 -10
View File
@@ -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,
);
}
}
}
+10 -8
View File
@@ -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));
+6 -6
View File
@@ -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 {
);
}
}
+23 -27
View File
@@ -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;
}
+1 -1
View File
@@ -65,4 +65,4 @@ CakeTorInstance(
)
""";
}
}
}
+1 -1
View File
@@ -30,4 +30,4 @@ CakeTorDisabled(
)
""";
}
}
}
+1 -1
View File
@@ -31,4 +31,4 @@ CakeTorSocks(
)
""";
}
}
}
+1 -1
View File
@@ -88,4 +88,4 @@ CakeTorTorch(
)
""";
}
}
}

Some files were not shown because too many files have changed in this diff Show More