Reformat the whole codebase with 100 line width (#3403)

This commit is contained in:
Omar Hatem
2026-07-15 01:20:33 +03:00
committed by GitHub
parent 1241b28bec
commit 16ddc0837a
801 changed files with 17517 additions and 17091 deletions
+107 -107
View File
@@ -6,110 +6,110 @@ 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
# 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
+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);
@@ -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(
)
""";
}
}
}
+11 -11
View File
@@ -1,4 +1,3 @@
import 'dart:typed_data';
import 'package:blockchain_utils/blockchain_utils.dart';
@@ -15,31 +14,32 @@ String convertAnyToXpub(String any) {
String convertZpubToXpub(String zpub) {
try {
final decoded = Base58Decoder.checkDecode(zpub);
if (decoded.length < 4) {
throw ArgumentError('Invalid extended public key length');
}
final versionBytes = decoded.sublist(0, 4);
final zpubVersionBytes = [0x04, 0xb2, 0x47, 0x46]; // zpub mainnet version
final zpubTestnetVersionBytes = [0x04, 0x5f, 0x1c, 0xf6]; // vpub testnet version
bool isZpub = listEquals(versionBytes, zpubVersionBytes);
bool isVpub = listEquals(versionBytes, zpubTestnetVersionBytes);
if (!isZpub && !isVpub) {
return zpub;
}
final xpubVersionBytes = isZpub ?
[0x04, 0x88, 0xb2, 0x1e] : // xpub mainnet
[0x04, 0x35, 0x87, 0xcf]; // tpub testnet
final xpubVersionBytes = isZpub
? [0x04, 0x88, 0xb2, 0x1e]
: // xpub mainnet
[0x04, 0x35, 0x87, 0xcf]; // tpub testnet
final newExtendedKey = Uint8List.fromList([
...xpubVersionBytes,
...decoded.sublist(4),
]);
return Base58Encoder.checkEncode(newExtendedKey);
} catch (e) {
throw ArgumentError('Failed to convert zpub to xpub: $e');
+119 -121
View File
@@ -327,79 +327,77 @@ class DerivationInfo {
class WalletInfo {
WalletInfo(
this.internalId,
this.id,
this.name,
this.type,
this.isRecovery,
this.restoreHeight,
this.timestamp,
this.dirPath,
this.path,
this.address,
this.yatEid,
this.yatLastUsedAddressRaw,
this.showIntroCakePayCard,
this.derivationInfoId,
this.hardwareWalletType,
this.parentAddress,
this.hashedWalletIdentifier,
this.isNonSeedWallet,
this.sortOrder,
this.addressPageType,
this.receiveInfoboxDismissed,
this.showCombinedBalance,
this.favoriteTokenAddress
) : _yatLastUsedAddressController = StreamController<String>.broadcast();
this.internalId,
this.id,
this.name,
this.type,
this.isRecovery,
this.restoreHeight,
this.timestamp,
this.dirPath,
this.path,
this.address,
this.yatEid,
this.yatLastUsedAddressRaw,
this.showIntroCakePayCard,
this.derivationInfoId,
this.hardwareWalletType,
this.parentAddress,
this.hashedWalletIdentifier,
this.isNonSeedWallet,
this.sortOrder,
this.addressPageType,
this.receiveInfoboxDismissed,
this.showCombinedBalance,
this.favoriteTokenAddress)
: _yatLastUsedAddressController = StreamController<String>.broadcast();
factory WalletInfo.external({
required String id,
required String name,
required WalletType type,
required bool isRecovery,
required int restoreHeight,
required DateTime date,
required String dirPath,
required String path,
required String address,
bool? showIntroCakePayCard,
String yatEid = '',
String yatLastUsedAddressRaw = '',
int? derivationInfoId,
HardwareWalletType? hardwareWalletType,
String? parentAddress,
String? hashedWalletIdentifier,
bool? isNonSeedWallet,
int? sortOrder,
bool? receiveInfoboxDismissed,
bool? showCombinedBalance,
String? favoriteTokenAddress
}) {
factory WalletInfo.external(
{required String id,
required String name,
required WalletType type,
required bool isRecovery,
required int restoreHeight,
required DateTime date,
required String dirPath,
required String path,
required String address,
bool? showIntroCakePayCard,
String yatEid = '',
String yatLastUsedAddressRaw = '',
int? derivationInfoId,
HardwareWalletType? hardwareWalletType,
String? parentAddress,
String? hashedWalletIdentifier,
bool? isNonSeedWallet,
int? sortOrder,
bool? receiveInfoboxDismissed,
bool? showCombinedBalance,
String? favoriteTokenAddress}) {
return WalletInfo(
0,
id,
name,
type,
isRecovery,
restoreHeight,
date.millisecondsSinceEpoch,
dirPath,
path,
address,
yatEid,
yatLastUsedAddressRaw,
showIntroCakePayCard,
derivationInfoId ?? -1,
hardwareWalletType,
parentAddress,
hashedWalletIdentifier,
isNonSeedWallet ?? false,
sortOrder ?? 0,
null,
receiveInfoboxDismissed ?? false,
showCombinedBalance ?? true,
favoriteTokenAddress
);
0,
id,
name,
type,
isRecovery,
restoreHeight,
date.millisecondsSinceEpoch,
dirPath,
path,
address,
yatEid,
yatLastUsedAddressRaw,
showIntroCakePayCard,
derivationInfoId ?? -1,
hardwareWalletType,
parentAddress,
hashedWalletIdentifier,
isNonSeedWallet ?? false,
sortOrder ?? 0,
null,
receiveInfoboxDismissed ?? false,
showCombinedBalance ?? true,
favoriteTokenAddress);
}
static String get tableName => 'walletInfo';
@@ -565,59 +563,59 @@ class WalletInfo {
StreamController<String> _yatLastUsedAddressController;
Map<String, dynamic> toJson() => {
selfIdColumn: internalId,
"id": id,
"name": name,
"type": type.index,
"isRecovery": isRecovery ? 1 : 0,
"restoreHeight": restoreHeight,
"timestamp": timestamp,
"dirPath": dirPath,
"path": path,
"address": address,
"yatEid": yatEid,
"yatLastUsedAddressRaw": yatLastUsedAddressRaw,
"showIntroCakePayCard": showIntroCakePayCard == true ? 1 : 0, // SQL regression: null -> false
"walletInfoDerivationInfoId": derivationInfoId,
"hardwareWalletType": hardwareWalletType?.index,
"parentAddress": parentAddress,
"hashedWalletIdentifier": hashedWalletIdentifier,
"isNonSeedWallet": isNonSeedWallet ? 1 : 0,
"sortOrder": sortOrder,
"addressPageType": addressPageType,
"receiveInfoboxDismissed": receiveInfoboxDismissed ? 1 : 0,
"showCombinedBalance": showCombinedBalance ? 1 : 0,
"favoriteTokenAddress": favoriteTokenAddress
};
selfIdColumn: internalId,
"id": id,
"name": name,
"type": type.index,
"isRecovery": isRecovery ? 1 : 0,
"restoreHeight": restoreHeight,
"timestamp": timestamp,
"dirPath": dirPath,
"path": path,
"address": address,
"yatEid": yatEid,
"yatLastUsedAddressRaw": yatLastUsedAddressRaw,
"showIntroCakePayCard":
showIntroCakePayCard == true ? 1 : 0, // SQL regression: null -> false
"walletInfoDerivationInfoId": derivationInfoId,
"hardwareWalletType": hardwareWalletType?.index,
"parentAddress": parentAddress,
"hashedWalletIdentifier": hashedWalletIdentifier,
"isNonSeedWallet": isNonSeedWallet ? 1 : 0,
"sortOrder": sortOrder,
"addressPageType": addressPageType,
"receiveInfoboxDismissed": receiveInfoboxDismissed ? 1 : 0,
"showCombinedBalance": showCombinedBalance ? 1 : 0,
"favoriteTokenAddress": favoriteTokenAddress
};
factory WalletInfo.fromJson(Map<String, dynamic> json) {
return WalletInfo(
json[selfIdColumn] as int,
json['id'] as String,
json['name'] as String,
WalletType.values[json['type'] as int],
(json['isRecovery'] as int) == 1,
json['restoreHeight'] as int,
json['timestamp'] as int,
json['dirPath'] as String,
json['path'] as String,
json['address'] as String,
json['yatEid'] as String?,
json['yatLastUsedAddressRaw'] as String?,
(json['showIntroCakePayCard'] as int) == 1,
json['walletInfoDerivationInfoId'] as int,
json['hardwareWalletType'] == null
? null
: HardwareWalletType.values[json['hardwareWalletType'] as int],
json['parentAddress'] as String?,
json['hashedWalletIdentifier'] as String?,
(json['isNonSeedWallet'] as int) == 1,
json['sortOrder'] as int? ?? 0,
json['addressPageType'] as String? ?? null,
json['receiveInfoboxDismissed'] != 0,
json["showCombinedBalance"] != 0,
json["favoriteTokenAddress"] as String? ?? null
);
json[selfIdColumn] as int,
json['id'] as String,
json['name'] as String,
WalletType.values[json['type'] as int],
(json['isRecovery'] as int) == 1,
json['restoreHeight'] as int,
json['timestamp'] as int,
json['dirPath'] as String,
json['path'] as String,
json['address'] as String,
json['yatEid'] as String?,
json['yatLastUsedAddressRaw'] as String?,
(json['showIntroCakePayCard'] as int) == 1,
json['walletInfoDerivationInfoId'] as int,
json['hardwareWalletType'] == null
? null
: HardwareWalletType.values[json['hardwareWalletType'] as int],
json['parentAddress'] as String?,
json['hashedWalletIdentifier'] as String?,
(json['isNonSeedWallet'] as int) == 1,
json['sortOrder'] as int? ?? 0,
json['addressPageType'] as String? ?? null,
json['receiveInfoboxDismissed'] != 0,
json["showCombinedBalance"] != 0,
json["favoriteTokenAddress"] as String? ?? null);
}
Future<int> save() async {
+1 -1
View File
@@ -201,7 +201,7 @@ class WalletInfo extends HiveObject {
// @HiveField(22)
String? parentAddress;
// @HiveField(23)
List<String>? hiddenAddresses;
+5 -11
View File
@@ -53,9 +53,7 @@ class DerivationInfoAdapter extends TypeAdapter<DerivationInfo> {
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DerivationInfoAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
other is DerivationInfoAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
}
class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
@@ -88,8 +86,8 @@ class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
fields[26] == null ? false : fields[26] as bool,
)
..addresses = (fields[10] as Map?)?.cast<String, String>()
..addressInfos = (fields[14] as Map?)?.map((dynamic k, dynamic v) =>
MapEntry(k as int, (v as List).cast<AddressInfo>()))
..addressInfos = (fields[14] as Map?)
?.map((dynamic k, dynamic v) => MapEntry(k as int, (v as List).cast<AddressInfo>()))
..usedAddresses = (fields[15] as List?)?.cast<String>()
..derivationType = fields[16] as newWi.DerivationType?
..derivationPath = fields[17] as String?
@@ -163,9 +161,7 @@ class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is WalletInfoAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
other is WalletInfoAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
}
class DerivationTypeAdapter extends TypeAdapter<newWi.DerivationType> {
@@ -217,9 +213,7 @@ class DerivationTypeAdapter extends TypeAdapter<newWi.DerivationType> {
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DerivationTypeAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
other is DerivationTypeAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
}
class HardwareWalletTypeAdapter extends TypeAdapter<newWi.HardwareWalletType> {
+8 -1
View File
@@ -101,7 +101,14 @@ class WalletKeysData {
final String? scanSecret;
final String? spendPubkey;
WalletKeysData({this.privateKey, this.mnemonic, this.altMnemonic, this.passphrase, this.xPub, this.scanSecret, this.spendPubkey});
WalletKeysData(
{this.privateKey,
this.mnemonic,
this.altMnemonic,
this.passphrase,
this.xPub,
this.scanSecret,
this.spendPubkey});
String toJSON() => jsonEncode({
"privateKey": privateKey,
+13 -12
View File
@@ -334,17 +334,18 @@ WalletType? _cryptoCurrencyToWalletType(CryptoCurrency type) {
WalletType? cryptoCurrencyOrTokenToWalletType(CryptoCurrency type) {
try {
if(type.tag == CryptoCurrency.bnb.tag) {
return _cryptoCurrencyToWalletType(CryptoCurrency.bnb);
if (type.tag == CryptoCurrency.bnb.tag) {
return _cryptoCurrencyToWalletType(CryptoCurrency.bnb);
}
if (type.tag != null &&
![CryptoCurrency.btcln.tag, CryptoCurrency.bnb.tag].contains(type.tag)) {
return _cryptoCurrencyToWalletType(CryptoCurrency.fromString(type.tag!));
} else {
return _cryptoCurrencyToWalletType(type);
}
} catch (e) {
printV(e);
return null;
}
if(type.tag != null && ![CryptoCurrency.btcln.tag, CryptoCurrency.bnb.tag].contains(type.tag)) {
return _cryptoCurrencyToWalletType(CryptoCurrency.fromString(type.tag!));
} else {
return _cryptoCurrencyToWalletType(type);
}
} catch (e) {
printV(e);
return null;
}
}
+1 -3
View File
@@ -130,7 +130,5 @@ class WalletTypeAdapter extends TypeAdapter<WalletType> {
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is WalletTypeAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
other is WalletTypeAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
}
+1 -1
View File
@@ -15,4 +15,4 @@ double wowneroAmountToDouble({required int amount}) =>
cryptoAmountToDouble(amount: amount, divider: wowneroAmountDivider);
int wowneroParseAmount({required String amount}) =>
(double.parse(amount) * wowneroAmountDivider).round();
(double.parse(amount) * wowneroAmountDivider).round();

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