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: run:
shell: bash shell: bash
jobs: jobs:
lint: # lint:
runs-on: [Linux, amd64, android] # runs-on: [Linux, amd64, android]
container: # container:
image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.41.9-ndkr28-go1.24.1-ruststablenightly # image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.41.9-ndkr28-go1.24.1-ruststablenightly
env: # env:
STORE_PASS: test@cake_wallet # STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet # KEY_PASS: test@cake_wallet
MONEROC_CACHE_DIR_ROOT: /opt/generic_cache # MONEROC_CACHE_DIR_ROOT: /opt/generic_cache
BRANCH_NAME: ${{ github.head_ref || github.ref_name }} # BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
ANDROID_AVD_HOME: /root/.android/avd # ANDROID_AVD_HOME: /root/.android/avd
volumes: # volumes:
- /opt/cw_cache_android/root/.cache:/root/.cache # - /opt/cw_cache_android/root/.cache:/root/.cache
- /opt/cw_cache_android/root/.android/avd/:/root/.android/avd # - /opt/cw_cache_android/root/.android/avd/:/root/.android/avd
- /opt/cw_cache_android/root/.ccache:/root/.ccache # - /opt/cw_cache_android/root/.ccache:/root/.ccache
- /opt/cw_cache_android/root/.pub-cache/:/root/.pub-cache # - /opt/cw_cache_android/root/.pub-cache/:/root/.pub-cache
- /opt/cw_cache_android/root/.gradle/:/root/.gradle # - /opt/cw_cache_android/root/.gradle/:/root/.gradle
- /opt/cw_cache_android/root/.android/:/root/.android # - /opt/cw_cache_android/root/.android/:/root/.android
- /opt/cw_cache_android/root/go/pkg:/root/go/pkg # - /opt/cw_cache_android/root/go/pkg:/root/go/pkg
- /opt/cw_cache_android/opt/generic_cache:/opt/generic_cache # - /opt/cw_cache_android/opt/generic_cache:/opt/generic_cache
- /dev/kvm:/dev/kvm # - /dev/kvm:/dev/kvm
- /var/run/docker.sock:/var/run/docker.sock # - /var/run/docker.sock:/var/run/docker.sock
strategy: # strategy:
matrix: # matrix:
api-level: [ 29 ] # api-level: [ 29 ]
#
steps: # steps:
- name: Fix github actions messing up $HOME... # - name: Fix github actions messing up $HOME...
run: "echo HOME=/root | sudo tee -a $GITHUB_ENV" # run: "echo HOME=/root | sudo tee -a $GITHUB_ENV"
- uses: actions/checkout@v4 # - uses: actions/checkout@v4
with: # with:
ref: ${{ inputs.ref }} # ref: ${{ inputs.ref }}
#
- name: configure git # - name: configure git
run: | # run: |
git config --global --add safe.directory '*' # git config --global --add safe.directory '*'
git config --global user.email "ci@cakewallet.com" # git config --global user.email "ci@cakewallet.com"
git config --global user.name "CakeWallet CI" # git config --global user.name "CakeWallet CI"
#
- name: Fetch prebuilt Torch # - name: Fetch prebuilt Torch
run: | # run: |
set -x -e # set -x -e
pushd scripts # 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 # 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 # rm -rf torch_dart
mkdir torch_dart # mkdir torch_dart
pushd torch_dart # pushd torch_dart
tar -xzf ../torch_dart.tar.gz # tar -xzf ../torch_dart.tar.gz
popd # popd
rm ./torch_dart.tar.gz # rm ./torch_dart.tar.gz
popd # popd
#
- name: Fetch prebuilt Reown # - name: Fetch prebuilt Reown
run: | # run: |
set -x -e # set -x -e
pushd scripts # pushd scripts
# cleaning # # cleaning
rm -rf reown_flutter # rm -rf reown_flutter
rm -f reown_flutter.tar.gz # 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 # 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 # mkdir reown_flutter
pushd reown_flutter # pushd reown_flutter
tar -xzf ../reown_flutter.tar.gz # tar -xzf ../reown_flutter.tar.gz
popd # popd
rm ./reown_flutter.tar.gz # rm ./reown_flutter.tar.gz
popd # popd
#
- name: Build Bitbox Flutter # - name: Build Bitbox Flutter
run: | # run: |
set -x -e # set -x -e
pushd scripts # pushd scripts
./build_bitbox_flutter.sh # ./build_bitbox_flutter.sh
popd # popd
#
- name: Prepare Zcash # - name: Prepare Zcash
run: | # run: |
set -x -e # set -x -e
scripts/prepare_zcash.sh # scripts/prepare_zcash.sh
#
- name: Execute Build and Setup Commands # - name: Execute Build and Setup Commands
run: | # run: |
pushd scripts/android # pushd scripts/android
source ./app_env.sh cakewallet # source ./app_env.sh cakewallet
export GITHUB_HUH=yeah # export GITHUB_HUH=yeah
./app_config.sh # ./app_config.sh
popd # popd
#
- name: Install Flutter dependencies # - name: Install Flutter dependencies
run: | # run: |
flutter pub get # flutter pub get
#
- name: Build generated code # - name: Build generated code
run: | # run: |
flutter --version # flutter --version
flutter clean # flutter clean
rm -rf .dart_tool # rm -rf .dart_tool
rm pubspec.lock # rm pubspec.lock
flutter pub get # flutter pub get
./model_generator.sh async # ./model_generator.sh async
#
- name: Generate localization # - name: Generate localization
run: | # run: |
dart run tool/generate_localization.dart # dart run tool/generate_localization.dart
#
- name: Lint # - name: Lint
run: | # run: |
ABORT_ON_CHANGE=yeah scripts/lint.sh # ABORT_ON_CHANGE=yeah scripts/lint.sh
+5 -10
View File
@@ -17,21 +17,16 @@ BitcoinBaseAddress addressFromScript(Script script,
switch (addressType) { switch (addressType) {
case P2pkhAddressType.p2pkh: case P2pkhAddressType.p2pkh:
return P2pkhAddress.fromScriptPubkey( return P2pkhAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
script: script, network: BitcoinNetwork.mainnet);
case P2shAddressType.p2pkhInP2sh: case P2shAddressType.p2pkhInP2sh:
case P2shAddressType.p2pkInP2sh: case P2shAddressType.p2pkInP2sh:
return P2shAddress.fromScriptPubkey( return P2shAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
script: script, network: BitcoinNetwork.mainnet);
case SegwitAddresType.p2wpkh: case SegwitAddresType.p2wpkh:
return P2wpkhAddress.fromScriptPubkey( return P2wpkhAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
script: script, network: BitcoinNetwork.mainnet);
case SegwitAddresType.p2wsh: case SegwitAddresType.p2wsh:
return P2wshAddress.fromScriptPubkey( return P2wshAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
script: script, network: BitcoinNetwork.mainnet);
case SegwitAddresType.p2tr: case SegwitAddresType.p2tr:
return P2trAddress.fromScriptPubkey( return P2trAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
script: script, network: BitcoinNetwork.mainnet);
} }
throw ArgumentError("Invalid script"); throw ArgumentError("Invalid script");
+5 -7
View File
@@ -72,17 +72,16 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
required super.type, required super.type,
String? scriptHash, String? scriptHash,
required super.network, required super.network,
}) { }) {
try { try {
this.scriptHash = scriptHash ?? this.scriptHash = scriptHash ??
(network != null ? BitcoinAddressUtils.scriptHash(address, network: network!) : null); (network != null ? BitcoinAddressUtils.scriptHash(address, network: network!) : null);
} catch (e) { } catch (e) {
printV(e); printV(e);
} }
} }
static bool _legacyDefaultForType(BitcoinAddressType type) { static bool _legacyDefaultForType(BitcoinAddressType type) {
// Some address types (p2wpkh, p2wsh) were historically derived from the same account/path as our new standard // 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. // but using legacy formats. For these, we default to legacy = false.
if (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh) return false; if (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh) return false;
@@ -154,6 +153,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
} }
return scriptHash!; return scriptHash!;
} }
@override @override
String get derivationPath { String get derivationPath {
if (type == SegwitAddresType.mweb) { if (type == SegwitAddresType.mweb) {
@@ -162,9 +162,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
final coinType = _coinTypeForNetwork(); final coinType = _coinTypeForNetwork();
final purpose = _purposeForType(type); final purpose = _purposeForType(type);
final accountPath = isLegacyDerivation final accountPath = isLegacyDerivation ? electrum_path : "m/$purpose'/$coinType'/0'";
? electrum_path
: "m/$purpose'/$coinType'/0'";
final chain = isHidden ? 1 : 0; final chain = isHidden ? 1 : 0;
return "$accountPath/$chain/$index"; return "$accountPath/$chain/$index";
+2 -2
View File
@@ -7,8 +7,8 @@ final bitcoinAmountFormat = NumberFormat()
..maximumFractionDigits = bitcoinAmountLength ..maximumFractionDigits = bitcoinAmountLength
..minimumFractionDigits = 1; ..minimumFractionDigits = 1;
String bitcoinAmountToString({required int amount}) => bitcoinAmountFormat.format( String bitcoinAmountToString({required int amount}) =>
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider)); bitcoinAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider));
double bitcoinAmountToDouble({required int amount}) => double bitcoinAmountToDouble({required int amount}) =>
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider); cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
@@ -5,4 +5,3 @@ class BitcoinCommitTransactionException implements Exception {
@override @override
String toString() => errorMessage; String toString() => errorMessage;
} }
@@ -2,7 +2,8 @@ import 'package:cw_core/transaction_priority.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
class BitcoinTransactionPriority extends TransactionPriority { 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); : super(title: title, raw: raw, description: description, hint: hint);
static const List<BitcoinTransactionPriority> all = [fast, medium, slow, custom]; 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); BitcoinTransactionPriority(title: 'Slow', description: "2 sat/byte", hint: "~ 24 h", raw: 0);
static const BitcoinTransactionPriority medium = static const BitcoinTransactionPriority medium =
BitcoinTransactionPriority(title: 'Medium', description: "3 sat/byte", hint: "~ 1 h", raw: 1); BitcoinTransactionPriority(title: 'Medium', description: "3 sat/byte", hint: "~ 1 h", raw: 1);
static const BitcoinTransactionPriority fast = static const BitcoinTransactionPriority fast = BitcoinTransactionPriority(
BitcoinTransactionPriority(title: 'Fast', description: "4 sat/byte", hint: "~ 30 min", raw: 2); title: 'Fast', description: "4 sat/byte", hint: "~ 30 min", raw: 2);
static const BitcoinTransactionPriority custom = static const BitcoinTransactionPriority custom =
BitcoinTransactionPriority(title: 'Custom', raw: 3); BitcoinTransactionPriority(title: 'Custom', raw: 3);
static BitcoinTransactionPriority deserialize({required int raw}) { static BitcoinTransactionPriority deserialize({required int raw}) {
switch (raw) { switch (raw) {
@@ -116,19 +117,19 @@ class LitecoinTransactionPriority extends BitcoinTransactionPriority {
return label; return label;
} }
} }
class BitcoinCashTransactionPriority extends BitcoinTransactionPriority { class BitcoinCashTransactionPriority extends BitcoinTransactionPriority {
const BitcoinCashTransactionPriority({required String title, required int raw}) const BitcoinCashTransactionPriority({required String title, required int raw})
: super(title: title, raw: raw); : super(title: title, raw: raw);
static const List<BitcoinCashTransactionPriority> all = [fast, medium, slow]; static const List<BitcoinCashTransactionPriority> all = [fast, medium, slow];
static const BitcoinCashTransactionPriority slow = static const BitcoinCashTransactionPriority slow =
BitcoinCashTransactionPriority(title: 'Slow', raw: 0); BitcoinCashTransactionPriority(title: 'Slow', raw: 0);
static const BitcoinCashTransactionPriority medium = static const BitcoinCashTransactionPriority medium =
BitcoinCashTransactionPriority(title: 'Medium', raw: 1); BitcoinCashTransactionPriority(title: 'Medium', raw: 1);
static const BitcoinCashTransactionPriority fast = static const BitcoinCashTransactionPriority fast =
BitcoinCashTransactionPriority(title: 'Fast', raw: 2); BitcoinCashTransactionPriority(title: 'Fast', raw: 2);
static BitcoinCashTransactionPriority deserialize({required int raw}) { static BitcoinCashTransactionPriority deserialize({required int raw}) {
switch (raw) { switch (raw) {
@@ -170,4 +171,3 @@ class BitcoinCashTransactionPriority extends BitcoinTransactionPriority {
return label; return label;
} }
} }
+35 -33
View File
@@ -306,28 +306,28 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
} }
return BitcoinWallet( return BitcoinWallet(
mnemonic: mnemonic, mnemonic: mnemonic,
xpub: keysData.xPub != null ? convertZpubToXpub(keysData.xPub!) : null, xpub: keysData.xPub != null ? convertZpubToXpub(keysData.xPub!) : null,
password: password, password: password,
passphrase: passphrase, passphrase: passphrase,
walletInfo: walletInfo, walletInfo: walletInfo,
derivationInfo: derivationInfo, derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp?.addresses, initialAddresses: snp?.addresses,
initialSilentAddresses: snp?.silentAddresses, initialSilentAddresses: snp?.silentAddresses,
initialSilentAddressIndex: snp?.silentAddressIndex ?? 0, initialSilentAddressIndex: snp?.silentAddressIndex ?? 0,
initialBalance: snp?.balance, initialBalance: snp?.balance,
initialLightningBalance: snp?.lightningBalance, initialLightningBalance: snp?.lightningBalance,
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
seedBytes: seedBytes, seedBytes: seedBytes,
initialRegularAddressIndex: snp?.regularAddressIndex, initialRegularAddressIndex: snp?.regularAddressIndex,
initialChangeAddressIndex: snp?.changeAddressIndex, initialChangeAddressIndex: snp?.changeAddressIndex,
addressPageType: snp?.addressPageType, addressPageType: snp?.addressPageType,
networkParam: network, networkParam: network,
alwaysScan: snp?.alwaysScan, alwaysScan: snp?.alwaysScan,
useLightning: snp?.useLightning, useLightning: snp?.useLightning,
cachedLightningAddress: snp?.cachedLightningAddress, cachedLightningAddress: snp?.cachedLightningAddress,
payjoinBox: payjoinBox, payjoinBox: payjoinBox,
); );
} }
@@ -346,12 +346,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
} }
try { try {
final lBalance = await lightningWallet!.getBalance(); final lBalance = await lightningWallet!.getBalance();
this.balance[CryptoCurrency.btcln] = ElectrumBalance( this.balance[CryptoCurrency.btcln] = ElectrumBalance(
confirmed: lBalance, confirmed: lBalance,
unconfirmed: Money.zero(CryptoCurrency.btcln), unconfirmed: Money.zero(CryptoCurrency.btcln),
frozen: Money.zero(CryptoCurrency.btcln)); frozen: Money.zero(CryptoCurrency.btcln));
} catch (e) { } catch (e) {
printV("Error fetching lightning balance: $e"); printV("Error fetching lightning balance: $e");
} }
@@ -494,7 +494,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
@override @override
Future<PendingTransaction> createTransaction(Object credentials) async { Future<PendingTransaction> createTransaction(Object credentials) async {
credentials = credentials as BitcoinTransactionCredentials; 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); final isLNCompatible = await lightningWallet?.isCompatible(lnAddr);
if ((credentials.coinTypeToSpendFrom == UnspentCoinType.lightning && lightningWallet != null) || if ((credentials.coinTypeToSpendFrom == UnspentCoinType.lightning && lightningWallet != null) ||
@@ -506,12 +508,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
amount = credentials.outputs.first.cryptoAmount; amount = credentials.outputs.first.cryptoAmount;
} }
return lightningWallet!.createTransaction( return lightningWallet!.createTransaction(
lnAddr, lnAddr,
amount.amount > BigInt.zero ? amount.amount : null, amount.amount > BigInt.zero ? amount.amount : null,
credentials.priority, credentials.priority,
credentials.outputs.first.sendAll,); credentials.outputs.first.sendAll,
);
} }
final tx = (await super.createTransaction(credentials)) as PendingBitcoinTransaction; final tx = (await super.createTransaction(credentials)) as PendingBitcoinTransaction;
+3 -5
View File
@@ -137,7 +137,8 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
} }
@override @override
bool containsAddress(String address) => super.containsAddress(address) || address == lightningAddress; bool containsAddress(String address) =>
super.containsAddress(address) || address == lightningAddress;
@override @override
String get addressForBuy { String get addressForBuy {
@@ -153,12 +154,9 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
@override @override
String get addressForExchange { String get addressForExchange {
final current = getFreshAddress(); final current = getFreshAddress();
final availableReceiveAddresses = receiveAddresses.where((element) => final availableReceiveAddresses = receiveAddresses.where((element) =>
!element.isUsed && !element.isUsed && !element.isHidden && !hiddenAddresses.contains(element.address));
!element.isHidden &&
!hiddenAddresses.contains(element.address));
final bool isSilentPaymentsPage = addressPageType == SilentPaymentsAddresType.p2sp; final bool isSilentPaymentsPage = addressPageType == SilentPaymentsAddresType.p2sp;
final bool isLightningPage = addressPageType == LightningAddressType.p2l; final bool isLightningPage = addressPageType == LightningAddressType.p2l;
@@ -59,27 +59,27 @@ class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
} }
class BitcoinWalletFromKeysCredentials extends WalletCredentials { class BitcoinWalletFromKeysCredentials extends WalletCredentials {
BitcoinWalletFromKeysCredentials({ BitcoinWalletFromKeysCredentials(
required String name, {required String name,
required String password, required String password,
required this.xpub, required this.xpub,
WalletInfo? walletInfo, WalletInfo? walletInfo,
super.hardwareWalletType super.hardwareWalletType})
}) : super(name: name, password: password, walletInfo: walletInfo); : super(name: name, password: password, walletInfo: walletInfo);
final String xpub; final String xpub;
} }
class LitecoinWalletFromKeysCredentials extends WalletCredentials { class LitecoinWalletFromKeysCredentials extends WalletCredentials {
LitecoinWalletFromKeysCredentials({ LitecoinWalletFromKeysCredentials(
required String name, {required String name,
required String password, required String password,
required this.xpub, required this.xpub,
required this.scanSecret, required this.scanSecret,
required this.spendPubkey, required this.spendPubkey,
WalletInfo? walletInfo, WalletInfo? walletInfo,
super.hardwareWalletType super.hardwareWalletType})
}) : super(name: name, password: password, walletInfo: walletInfo); : super(name: name, password: password, walletInfo: walletInfo);
final String xpub; final String xpub;
final String scanSecret; final String scanSecret;
+4 -7
View File
@@ -1,15 +1,12 @@
class BitcoinWalletKeys { 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 wif;
final String privateKey; final String privateKey;
final String publicKey; final String publicKey;
final String xpub; final String xpub;
Map<String, String> toJson() => { Map<String, String> toJson() =>
'wif': wif, {'wif': wif, 'privateKey': privateKey, 'publicKey': publicKey, 'xpub': xpub};
'privateKey': privateKey,
'publicKey': publicKey,
'xpub': xpub
};
} }
+11 -9
View File
@@ -21,8 +21,7 @@ class BitcoinWalletService extends WalletService<
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromSeedCredentials,
BitcoinWalletFromKeysCredentials, BitcoinWalletFromKeysCredentials,
BitcoinRestoreWalletFromHardware> { BitcoinRestoreWalletFromHardware> {
BitcoinWalletService(this.unspentCoinsInfoSource, BitcoinWalletService(this.unspentCoinsInfoSource, this.payjoinSessionSource, this.isDirect);
this.payjoinSessionSource, this.isDirect);
final Box<UnspentCoinsInfo> unspentCoinsInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final Box<PayjoinSession> payjoinSessionSource; final Box<PayjoinSession> payjoinSessionSource;
@@ -38,9 +37,12 @@ class BitcoinWalletService extends WalletService<
final String mnemonic; final String mnemonic;
final derivationInfo = await credentials.walletInfo!.getDerivationInfo(); final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
derivationInfo.derivationType = credentials.derivationInfo?.derivationType ?? derivationInfo.derivationType; derivationInfo.derivationType =
derivationInfo.derivationPath = credentials.derivationInfo?.derivationPath ?? derivationInfo.derivationPath; credentials.derivationInfo?.derivationType ?? derivationInfo.derivationType;
derivationInfo.description = credentials.derivationInfo?.description ?? derivationInfo.description; derivationInfo.derivationPath =
credentials.derivationInfo?.derivationPath ?? derivationInfo.derivationPath;
derivationInfo.description =
credentials.derivationInfo?.description ?? derivationInfo.description;
derivationInfo.scriptType = credentials.derivationInfo?.scriptType ?? derivationInfo.scriptType; derivationInfo.scriptType = credentials.derivationInfo?.scriptType ?? derivationInfo.scriptType;
await derivationInfo.save(); await derivationInfo.save();
switch (derivationInfo.derivationType) { switch (derivationInfo.derivationType) {
@@ -119,8 +121,9 @@ class BitcoinWalletService extends WalletService<
} }
await WalletInfo.delete(walletInfo); await WalletInfo.delete(walletInfo);
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where( final unspentCoinsToDelete = unspentCoinsInfoSource.values
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList(); .where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
.toList();
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList(); final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
@@ -135,8 +138,7 @@ class BitcoinWalletService extends WalletService<
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet; final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
credentials.walletInfo?.network = network.value; credentials.walletInfo?.network = network.value;
final derivationInfo = await credentials.walletInfo!.getDerivationInfo(); final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
derivationInfo.derivationPath = derivationInfo.derivationPath = credentials.hwAccountData.derivationPath;
credentials.hwAccountData.derivationPath;
final xpub = convertAnyToXpub(credentials.hwAccountData.xpub!); final xpub = convertAnyToXpub(credentials.hwAccountData.xpub!);
+17 -22
View File
@@ -304,9 +304,9 @@ class ElectrumClient {
}); });
Future<Map<String, List<Map<String, dynamic>>>> getBatchHistory( Future<Map<String, List<Map<String, dynamic>>>> getBatchHistory(
List<String> scriptHashes, { List<String> scriptHashes, {
int timeout = 10000, int timeout = 10000,
}) async { }) async {
final paramsList = scriptHashes.map((h) => <Object>[h]).toList(growable: false); final paramsList = scriptHashes.map((h) => <Object>[h]).toList(growable: false);
final batchResults = await callBatchWithTimeout( final batchResults = await callBatchWithTimeout(
@@ -342,9 +342,9 @@ class ElectrumClient {
} }
Future<Map<String, List<Map<String, dynamic>>>> getBatchUnspent( Future<Map<String, List<Map<String, dynamic>>>> getBatchUnspent(
List<String> scriptHashes, { List<String> scriptHashes, {
int timeout = 10000, int timeout = 10000,
}) async { }) async {
final paramsList = scriptHashes.map((h) => <Object>[h]).toList(growable: false); final paramsList = scriptHashes.map((h) => <Object>[h]).toList(growable: false);
final batchResults = await callBatchWithTimeout( final batchResults = await callBatchWithTimeout(
@@ -380,9 +380,9 @@ class ElectrumClient {
} }
Future<Map<String, Map<String, dynamic>>> getBatchBalance( Future<Map<String, Map<String, dynamic>>> getBatchBalance(
List<String> scriptHashes, { List<String> scriptHashes, {
int timeout = 10000, int timeout = 10000,
}) async { }) async {
final paramsList = scriptHashes.map((h) => <Object>[h]).toList(growable: false); final paramsList = scriptHashes.map((h) => <Object>[h]).toList(growable: false);
final batchResults = await callBatchWithTimeout( final batchResults = await callBatchWithTimeout(
@@ -416,9 +416,9 @@ class ElectrumClient {
} }
Future<Map<String, Map<String, dynamic>>> getBatchTransactionVerbose( Future<Map<String, Map<String, dynamic>>> getBatchTransactionVerbose(
List<String> hashes, { List<String> hashes, {
int timeout = 10000, int timeout = 10000,
}) async { }) async {
final result = <String, Map<String, dynamic>>{}; final result = <String, Map<String, dynamic>>{};
if (hashes.isEmpty) return result; if (hashes.isEmpty) return result;
@@ -443,9 +443,9 @@ class ElectrumClient {
} }
Future<Map<String, String?>> getBatchTransactionHex( Future<Map<String, String?>> getBatchTransactionHex(
List<String> hashes, { List<String> hashes, {
int timeout = 10000, int timeout = 10000,
}) async { }) async {
final result = <String, String?>{}; final result = <String, String?>{};
if (hashes.isEmpty) return result; if (hashes.isEmpty) return result;
@@ -483,12 +483,8 @@ class ElectrumClient {
// Build the Batch Array // Build the Batch Array
final List<Map<String, dynamic>> batchPayload = []; final List<Map<String, dynamic>> batchPayload = [];
for (int i = 0; i < paramsList.length; i++) { for (int i = 0; i < paramsList.length; i++) {
batchPayload.add({ batchPayload.add(
"jsonrpc": "2.0", {"jsonrpc": "2.0", "method": method, "params": paramsList[i], "id": "$batchBaseId-$i"});
"method": method,
"params": paramsList[i],
"id": "$batchBaseId-$i"
});
} }
// Register the task // Register the task
@@ -775,7 +771,6 @@ class ElectrumClient {
} }
void _handleResponse(dynamic response) { void _handleResponse(dynamic response) {
// Handle batch response // Handle batch response
if (response is List) { if (response is List) {
if (response.isEmpty) return; if (response.isEmpty) return;
@@ -1,6 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart';
import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pathForWallet.dart';
@@ -184,15 +184,13 @@ class ElectrumTransactionInfo extends TransactionInfo {
// MWEB HogEx // MWEB HogEx
final isHogExTx = (BtcTransaction tx) { final isHogExTx = (BtcTransaction tx) {
if (tx.inputs.isEmpty || tx.inputs.first.txIndex > 0 || tx.outputs.isEmpty) if (tx.inputs.isEmpty || tx.inputs.first.txIndex > 0 || tx.outputs.isEmpty) return false;
return false;
final b = tx.outputs.first.scriptPubKey.toBytes(); final b = tx.outputs.first.scriptPubKey.toBytes();
return b.length == 34 && b[0] == 88 && b[1] == 32; return b.length == 34 && b[0] == 88 && b[1] == 32;
}; };
final firstInput = bundle.ins.isNotEmpty ? bundle.ins.first : null; final firstInput = bundle.ins.isNotEmpty ? bundle.ins.first : null;
final isHogEx = firstInput != null && final isHogEx =
isHogExTx(bundle.originalTransaction) && firstInput != null && isHogExTx(bundle.originalTransaction) && isHogExTx(firstInput);
isHogExTx(firstInput);
final fee = hasMissingInputTx ? null : inputAmount - totalOutAmount; final fee = hasMissingInputTx ? null : inputAmount - totalOutAmount;
final walletCurrency = walletTypeToCryptoCurrency(type); final walletCurrency = walletTypeToCryptoCurrency(type);
+67 -100
View File
@@ -156,7 +156,6 @@ abstract class ElectrumWalletBase
sideHdByType[type] = sideHd; sideHdByType[type] = sideHd;
} }
} }
} }
int _purposeForType(BitcoinAddressType type) { int _purposeForType(BitcoinAddressType type) {
@@ -195,7 +194,6 @@ abstract class ElectrumWalletBase
/// For LEGACY addresses, returns the wallet's legacy derivation base (derivationInfo.derivationPath) /// 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'). /// which is already the account path used historically (e.g. m/0' or m/84'/0'/0').
String _accountDerivationPathForRecord(BaseBitcoinAddressRecord record) { String _accountDerivationPathForRecord(BaseBitcoinAddressRecord record) {
if (derivationInfo.derivationType == DerivationType.electrum) { if (derivationInfo.derivationType == DerivationType.electrum) {
return derivationInfo.derivationPath ?? electrum_path; // m/0' return derivationInfo.derivationPath ?? electrum_path; // m/0'
} }
@@ -807,7 +805,7 @@ abstract class ElectrumWalletBase
node!.isElectrs = true; node!.isElectrs = true;
// TODO figure out why condition was needed // TODO figure out why condition was needed
// if (node!.isInBox) { // if (node!.isInBox) {
node!.save(); node!.save();
// } // }
return node!.isElectrs!; return node!.isElectrs!;
} }
@@ -873,7 +871,8 @@ abstract class ElectrumWalletBase
BigInt get networkDustAmount => BigInt.from(546); 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({ UtxoDetails _createUTXOS({
required bool sendAll, required bool sendAll,
@@ -961,8 +960,7 @@ abstract class ElectrumWalletBase
final baseDerivationPath = _accountDerivationPathForRecord(utx.bitcoinAddressRecord); final baseDerivationPath = _accountDerivationPathForRecord(utx.bitcoinAddressRecord);
final derivationPath = final derivationPath = "${_hardenedDerivationPath(baseDerivationPath)}"
"${_hardenedDerivationPath(baseDerivationPath)}"
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}" "/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
"/${utx.bitcoinAddressRecord.index}"; "/${utx.bitcoinAddressRecord.index}";
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath); publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
@@ -1139,7 +1137,8 @@ abstract class ElectrumWalletBase
utxoDetails.utxos.length == utxoDetails.utxos.length ==
utxoDetails.availableInputs.length - utxoDetails.unconfirmedCoins.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 (amountLeftForChangeAndFee <= 0) {
if (!spendingAllCoins) { if (!spendingAllCoins) {
@@ -1175,11 +1174,9 @@ abstract class ElectrumWalletBase
isChange: true, isChange: true,
)); ));
// Must match the address' account root (purpose/coinType) and legacy derivation when applicable. // Must match the address' account root (purpose/coinType) and legacy derivation when applicable.
final changeBaseDerivationPath = _accountDerivationPathForRecord(changeAddress); final changeBaseDerivationPath = _accountDerivationPathForRecord(changeAddress);
final changeDerivationPath = final changeDerivationPath = "${_hardenedDerivationPath(changeBaseDerivationPath)}"
"${_hardenedDerivationPath(changeBaseDerivationPath)}"
"/${changeAddress.isHidden ? "1" : "0"}" "/${changeAddress.isHidden ? "1" : "0"}"
"/${changeAddress.index}"; "/${changeAddress.index}";
utxoDetails.publicKeys[address.pubKeyHash()] = utxoDetails.publicKeys[address.pubKeyHash()] =
@@ -1242,7 +1239,8 @@ abstract class ElectrumWalletBase
inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos, inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
vinOutpoints: utxoDetails.vinOutpoints, vinOutpoints: utxoDetails.vinOutpoints,
); );
final leftover = utxoDetails.allInputsAmount - credentialsAmount.amount.toInt() - feeNoChange; final leftover =
utxoDetails.allInputsAmount - credentialsAmount.amount.toInt() - feeNoChange;
if (leftover >= 0) { if (leftover >= 0) {
final finalFee = feeNoChange + leftover; // absorb tiny remainder final finalFee = feeNoChange + leftover; // absorb tiny remainder
@@ -1827,16 +1825,15 @@ abstract class ElectrumWalletBase
} }
Future<List<List<BitcoinUnspent>?>> _fetchUnspentsRegular( Future<List<List<BitcoinUnspent>?>> _fetchUnspentsRegular(
List<BitcoinAddressRecord> addresses, List<BitcoinAddressRecord> addresses,
) async { ) async {
final addressFutures = addresses.map((address) => fetchUnspent(address)).toList(); final addressFutures = addresses.map((address) => fetchUnspent(address)).toList();
return Future.wait(addressFutures); return Future.wait(addressFutures);
} }
Future<List<List<BitcoinUnspent>?>> _fetchUnspentsBatch( Future<List<List<BitcoinUnspent>?>> _fetchUnspentsBatch(
List<BitcoinAddressRecord> addresses, List<BitcoinAddressRecord> addresses,
) async { ) async {
final byScriptHash = <String, BitcoinAddressRecord>{ final byScriptHash = <String, BitcoinAddressRecord>{
for (final address in addresses) address.getScriptHash(network): address, for (final address in addresses) address.getScriptHash(network): address,
}; };
@@ -1845,7 +1842,7 @@ abstract class ElectrumWalletBase
try { try {
final unspentByScriptHash = final unspentByScriptHash =
await _processChunksToMap<String, String, List<Map<String, dynamic>>>( await _processChunksToMap<String, String, List<Map<String, dynamic>>>(
items: scriptHashes, items: scriptHashes,
chunkSize: addressHistoryChunkSize, chunkSize: addressHistoryChunkSize,
processChunk: _getListUnspentBatch, processChunk: _getListUnspentBatch,
@@ -2131,10 +2128,7 @@ abstract class ElectrumWalletBase
final hd = _hdFor(record: addressRecord); final hd = _hdFor(record: addressRecord);
final privkey = generateECPrivate( final privkey = generateECPrivate(hd: hd, index: addressRecord.index, network: network);
hd: hd,
index: addressRecord.index,
network: network);
privateKeys.add(privkey); privateKeys.add(privkey);
@@ -2197,8 +2191,7 @@ abstract class ElectrumWalletBase
final deduction = (outputAmount - networkDustAmount >= remainingFee) final deduction = (outputAmount - networkDustAmount >= remainingFee)
? remainingFee ? remainingFee
: outputAmount - networkDustAmount; : outputAmount - networkDustAmount;
outputs[i] = BitcoinOutput( outputs[i] = BitcoinOutput(address: output.address, value: outputAmount - deduction);
address: output.address, value: outputAmount - deduction);
remainingFee -= deduction; remainingFee -= deduction;
if (remainingFee <= BigInt.zero) break; if (remainingFee <= BigInt.zero) break;
@@ -2464,7 +2457,8 @@ abstract class ElectrumWalletBase
@override @override
Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async { Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
try { try {
final Map<String, ElectrumTransactionInfo> historiesWithDetails = {};; final Map<String, ElectrumTransactionInfo> historiesWithDetails = {};
;
printV('[BATCH_TEST] Fetching transactions with batch: $shouldUseBatchFetching'); printV('[BATCH_TEST] Fetching transactions with batch: $shouldUseBatchFetching');
@@ -2473,19 +2467,16 @@ abstract class ElectrumWalletBase
? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type) ? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type)
: fetchTransactionsForAddressType(historiesWithDetails, type))); : fetchTransactionsForAddressType(historiesWithDetails, type)));
} else if (type == WalletType.bitcoinCash) { } else if (type == WalletType.bitcoinCash) {
await Future.wait(BITCOIN_CASH_ADDRESS_TYPES await Future.wait(BITCOIN_CASH_ADDRESS_TYPES.map((type) => shouldUseBatchFetching
.map((type) => shouldUseBatchFetching
? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type) ? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type)
: fetchTransactionsForAddressType(historiesWithDetails, type))); : fetchTransactionsForAddressType(historiesWithDetails, type)));
} else if (type == WalletType.litecoin) { } else if (type == WalletType.litecoin) {
await Future.wait(LITECOIN_ADDRESS_TYPES await Future.wait(LITECOIN_ADDRESS_TYPES.where((type) => type != SegwitAddresType.mweb).map(
.where((type) => type != SegwitAddresType.mweb) (type) => shouldUseBatchFetching
.map((type) => shouldUseBatchFetching ? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type)
? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type) : fetchTransactionsForAddressType(historiesWithDetails, type)));
: fetchTransactionsForAddressType(historiesWithDetails, type)));
} else if (type == WalletType.dogecoin) { } else if (type == WalletType.dogecoin) {
await Future.wait(DOGECOIN_ADDRESS_TYPES await Future.wait(DOGECOIN_ADDRESS_TYPES.map((type) => shouldUseBatchFetching
.map((type) => shouldUseBatchFetching
? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type) ? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type)
: fetchTransactionsForAddressType(historiesWithDetails, type))); : fetchTransactionsForAddressType(historiesWithDetails, type)));
} }
@@ -2633,8 +2624,7 @@ abstract class ElectrumWalletBase
} }
Future<void> fetchTransactionsForAddressTypeBatch( Future<void> fetchTransactionsForAddressTypeBatch(
Map<String, ElectrumTransactionInfo> historiesWithDetails, Map<String, ElectrumTransactionInfo> historiesWithDetails, BitcoinAddressType type) async {
BitcoinAddressType type) async {
final addressesByType = final addressesByType =
walletAddresses.allAddresses.where((addr) => addr.type == type).toList(); walletAddresses.allAddresses.where((addr) => addr.type == type).toList();
final receiveAddresses = addressesByType.where((addr) => !addr.isHidden).toList(); final receiveAddresses = addressesByType.where((addr) => !addr.isHidden).toList();
@@ -2676,14 +2666,13 @@ abstract class ElectrumWalletBase
); );
} }
Future<void> fetchTransactionsForAddressesBranchBatch( Future<void> fetchTransactionsForAddressesBranchBatch(
Map<String, ElectrumTransactionInfo> historiesWithDetails, Map<String, ElectrumTransactionInfo> historiesWithDetails,
BitcoinAddressType type, BitcoinAddressType type,
List<BitcoinAddressRecord> branchAddresses, { List<BitcoinAddressRecord> branchAddresses, {
required bool isHidden, required bool isHidden,
required bool isLegacyDerivation, required bool isLegacyDerivation,
}) async { }) async {
if (branchAddresses.isEmpty) return; if (branchAddresses.isEmpty) return;
final tip = await getCurrentChainTip(); final tip = await getCurrentChainTip();
@@ -2714,7 +2703,6 @@ abstract class ElectrumWalletBase
if (!shouldDiscover) return; if (!shouldDiscover) return;
final newAddresses = await walletAddresses.discoverAddressesBatch( final newAddresses = await walletAddresses.discoverAddressesBatch(
currentBranch, currentBranch,
isHidden, isHidden,
@@ -2756,9 +2744,7 @@ abstract class ElectrumWalletBase
} }
Future<Map<String, ElectrumTransactionInfo>> _fetchBatchAddressHistory( Future<Map<String, ElectrumTransactionInfo>> _fetchBatchAddressHistory(
List<BitcoinAddressRecord> addressRecords, List<BitcoinAddressRecord> addressRecords, int? currentHeight, int historyChunkSize) async {
int? currentHeight,
int historyChunkSize) async {
String lastTxId = ''; String lastTxId = '';
bool didUpdateHistory = false; bool didUpdateHistory = false;
@@ -2769,11 +2755,8 @@ abstract class ElectrumWalletBase
final scriptHashes = addressRecords.map((a) => a.getScriptHash(network)).toList(); final scriptHashes = addressRecords.map((a) => a.getScriptHash(network)).toList();
final historyByScriptHash = final historyByScriptHash =
await _processChunksToMap<String, String, List<Map<String, dynamic>>>( await _processChunksToMap<String, String, List<Map<String, dynamic>>>(
items: scriptHashes, items: scriptHashes, chunkSize: historyChunkSize, processChunk: _getHistoryBatch);
chunkSize: historyChunkSize,
processChunk: _getHistoryBatch
);
// Map scriptHash -> addressRecord // Map scriptHash -> addressRecord
final byScriptHash = <String, BitcoinAddressRecord>{}; final byScriptHash = <String, BitcoinAddressRecord>{};
@@ -2869,8 +2852,7 @@ abstract class ElectrumWalletBase
.toList(growable: false); .toList(growable: false);
final heightsByHash = <String, int?>{ final heightsByHash = <String, int?>{
for (final e in chunkHistory) for (final e in chunkHistory) (e['tx_hash'] as String): (e['height'] as int?),
(e['tx_hash'] as String): (e['height'] as int?),
}; };
final infosByHash = await fetchTransactionInfoBatch( final infosByHash = await fetchTransactionInfoBatch(
@@ -2909,40 +2891,35 @@ abstract class ElectrumWalletBase
} }
} }
Future<Map<String, Map<String, dynamic>>> _getTransactionVerboseBatch( Future<Map<String, Map<String, dynamic>>> _getTransactionVerboseBatch(List<String> hashes) {
List<String> hashes) {
return electrumClient.getBatchTransactionVerbose( return electrumClient.getBatchTransactionVerbose(
hashes, hashes,
timeout: transactionBatchTimeoutMs, timeout: transactionBatchTimeoutMs,
); );
} }
Future<Map<String, String?>> _getTransactionHexBatch( Future<Map<String, String?>> _getTransactionHexBatch(List<String> hashes) {
List<String> hashes) {
return electrumClient.getBatchTransactionHex( return electrumClient.getBatchTransactionHex(
hashes, hashes,
timeout: transactionBatchTimeoutMs, timeout: transactionBatchTimeoutMs,
); );
} }
Future<Map<String, List<Map<String, dynamic>>>> _getHistoryBatch( Future<Map<String, List<Map<String, dynamic>>>> _getHistoryBatch(List<String> scriptHashes) {
List<String> scriptHashes) {
return electrumClient.getBatchHistory( return electrumClient.getBatchHistory(
scriptHashes, scriptHashes,
timeout: transactionBatchTimeoutMs, timeout: transactionBatchTimeoutMs,
); );
} }
Future<Map<String, List<Map<String, dynamic>>>> _getListUnspentBatch( Future<Map<String, List<Map<String, dynamic>>>> _getListUnspentBatch(List<String> scriptHashes) {
List<String> scriptHashes) {
return electrumClient.getBatchUnspent( return electrumClient.getBatchUnspent(
scriptHashes, scriptHashes,
timeout: transactionBatchTimeoutMs, timeout: transactionBatchTimeoutMs,
); );
} }
Future<Map<String, Map<String, dynamic>>> _getBalanceBatch( Future<Map<String, Map<String, dynamic>>> _getBalanceBatch(List<String> scriptHashes) {
List<String> scriptHashes) {
return electrumClient.getBatchBalance( return electrumClient.getBatchBalance(
scriptHashes, scriptHashes,
timeout: transactionBatchTimeoutMs, timeout: transactionBatchTimeoutMs,
@@ -2956,8 +2933,7 @@ abstract class ElectrumWalletBase
Duration retryDelay = const Duration(seconds: 2), Duration retryDelay = const Duration(seconds: 2),
}) async { }) async {
final result = <String, ElectrumTransactionInfo?>{}; final result = <String, ElectrumTransactionInfo?>{};
final uniqueHashes = final uniqueHashes = hashes.map((h) => h.trim()).where((h) => h.isNotEmpty).toSet().toList();
hashes.map((h) => h.trim()).where((h) => h.isNotEmpty).toSet().toList();
if (uniqueHashes.isEmpty) return result; if (uniqueHashes.isEmpty) return result;
@@ -2990,9 +2966,8 @@ abstract class ElectrumWalletBase
required Map<String, int?>? heightsByHash, required Map<String, int?>? heightsByHash,
}) async { }) async {
for (var i = 0; i < txIds.length; i += transactionChunkSize) { for (var i = 0; i < txIds.length; i += transactionChunkSize) {
final end = (i + transactionChunkSize < txIds.length) final end =
? i + transactionChunkSize (i + transactionChunkSize < txIds.length) ? i + transactionChunkSize : txIds.length;
: txIds.length;
final chunk = txIds.sublist(i, end); final chunk = txIds.sublist(i, end);
final bundlesByHash = await getTransactionExpandedBatch( final bundlesByHash = await getTransactionExpandedBatch(
@@ -3024,9 +2999,8 @@ abstract class ElectrumWalletBase
} }
} }
Future<Map<String, ElectrumTransactionBundle>> getTransactionExpandedBatch({ Future<Map<String, ElectrumTransactionBundle>> getTransactionExpandedBatch(
required List<String> hashes, {required List<String> hashes, Map<String, int?>? heightsByHash}) async {
Map<String, int?>? heightsByHash}) async {
final bundles = <String, ElectrumTransactionBundle>{}; final bundles = <String, ElectrumTransactionBundle>{};
if (hashes.isEmpty) return bundles; if (hashes.isEmpty) return bundles;
@@ -3062,8 +3036,8 @@ abstract class ElectrumWalletBase
Future<Map<String, Map<String, dynamic>>> _fetchTransactionVerboseBatch( Future<Map<String, Map<String, dynamic>>> _fetchTransactionVerboseBatch(
List<String> txIds) async { List<String> txIds) async {
final verboseTransactionByHash =
final verboseTransactionByHash = await _processChunksToMap<String, String, Map<String, dynamic>>( await _processChunksToMap<String, String, Map<String, dynamic>>(
items: txIds, items: txIds,
chunkSize: transactionChunkSize, chunkSize: transactionChunkSize,
processChunk: _getTransactionVerboseBatch, processChunk: _getTransactionVerboseBatch,
@@ -3100,8 +3074,8 @@ abstract class ElectrumWalletBase
} }
Map<String, BtcTransaction> _parseTransactions( Map<String, BtcTransaction> _parseTransactions(
Map<String, Map<String, dynamic>> verboseByHash, Map<String, Map<String, dynamic>> verboseByHash,
) { ) {
final result = <String, BtcTransaction>{}; final result = <String, BtcTransaction>{};
for (final entry in verboseByHash.entries) { for (final entry in verboseByHash.entries) {
@@ -3116,10 +3090,9 @@ abstract class ElectrumWalletBase
return result; return result;
} }
Map<String, List<String>> _collectInputTxIdsByHash( Map<String, List<String>> _collectInputTxIdsByHash(
Map<String, BtcTransaction> originalByHash, Map<String, BtcTransaction> originalByHash,
) { ) {
final inputTxIdsByHash = <String, List<String>>{}; final inputTxIdsByHash = <String, List<String>>{};
for (final entry in originalByHash.entries) { for (final entry in originalByHash.entries) {
@@ -3201,8 +3174,8 @@ abstract class ElectrumWalletBase
} }
Future<Map<int, int>> _fetchBlockTimestampsFromMempoolByHeights( Future<Map<int, int>> _fetchBlockTimestampsFromMempoolByHeights(
Set<int> heights, Set<int> heights,
) async { ) async {
final out = <int, int>{}; final out = <int, int>{};
if (heights.isEmpty) return out; if (heights.isEmpty) return out;
if (!(await checkIfMempoolAPIIsEnabled())) return out; if (!(await checkIfMempoolAPIIsEnabled())) return out;
@@ -3212,10 +3185,10 @@ abstract class ElectrumWalletBase
try { try {
final blockHashResp = await ProxyWrapper() final blockHashResp = await ProxyWrapper()
.get( .get(
clearnetUri: Uri.parse( clearnetUri: Uri.parse(
'https://mempool.cakewallet.com/api/v1/block-height/$h', 'https://mempool.cakewallet.com/api/v1/block-height/$h',
), ),
) )
.timeout(const Duration(seconds: 15)); .timeout(const Duration(seconds: 15));
if (blockHashResp.statusCode != 200 || blockHashResp.body.isEmpty) return; if (blockHashResp.statusCode != 200 || blockHashResp.body.isEmpty) return;
@@ -3225,10 +3198,10 @@ abstract class ElectrumWalletBase
final blockResp = await ProxyWrapper() final blockResp = await ProxyWrapper()
.get( .get(
clearnetUri: Uri.parse( clearnetUri: Uri.parse(
'https://mempool.cakewallet.com/api/v1/block/$blockHash', 'https://mempool.cakewallet.com/api/v1/block/$blockHash',
), ),
) )
.timeout(const Duration(seconds: 15)); .timeout(const Duration(seconds: 15));
if (blockResp.statusCode != 200 || blockResp.body.isEmpty) return; if (blockResp.statusCode != 200 || blockResp.body.isEmpty) return;
@@ -3276,7 +3249,6 @@ abstract class ElectrumWalletBase
return result; return result;
} }
Future<void> updateTransactions() async { Future<void> updateTransactions() async {
printV("updateTransactions() called!"); printV("updateTransactions() called!");
try { try {
@@ -3368,8 +3340,7 @@ abstract class ElectrumWalletBase
} }
try { try {
final balancesByScriptHash = final balancesByScriptHash = await _processChunksToMap<String, String, Map<String, dynamic>>(
await _processChunksToMap<String, String, Map<String, dynamic>>(
items: scriptHashes, items: scriptHashes,
chunkSize: addressHistoryChunkSize, chunkSize: addressHistoryChunkSize,
processChunk: _getBalanceBatch, processChunk: _getBalanceBatch,
@@ -3415,7 +3386,8 @@ abstract class ElectrumWalletBase
? await fetchBalancesBatch(addresses) ? await fetchBalancesBatch(addresses)
: await fetchBalancesRegular(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 totalFrozen = 0;
var totalConfirmed = 0; var totalConfirmed = 0;
@@ -3544,17 +3516,14 @@ abstract class ElectrumWalletBase
// that matches this received transaction, mark it as being from a peg out: // that matches this received transaction, mark it as being from a peg out:
for (final tx2 in transactionHistory.transactions.values) { for (final tx2 in transactionHistory.transactions.values) {
final heightDiff = ((tx2.height ?? 0) - (tx.height ?? 0)).abs(); 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 // 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 && if (tx2.additionalInfo["isPegOut"] == true && tx2.amount == tx.amount && heightDiff <= 5) {
tx2.amount == tx.amount &&
heightDiff <= 5) {
tx.additionalInfo["fromPegOut"] = true; tx.additionalInfo["fromPegOut"] = true;
} }
} }
} }
Future<void> checkIfBatchSupported() async { Future<void> checkIfBatchSupported() async {
if (_isBatchSupported != null) { if (_isBatchSupported != null) {
printV('[BATCH_TEST] Already checked: $_isBatchSupported'); printV('[BATCH_TEST] Already checked: $_isBatchSupported');
return; return;
@@ -3580,9 +3549,7 @@ abstract class ElectrumWalletBase
); );
final hasError = result.any((item) => final hasError = result.any((item) =>
item is Map<String, dynamic> && item is Map<String, dynamic> && item.containsKey('error') && item['error'] != null);
item.containsKey('error') &&
item['error'] != null);
if (hasError) { if (hasError) {
_isBatchSupported = false; _isBatchSupported = false;
+118 -112
View File
@@ -195,13 +195,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
} }
if (addressPageType == LightningAddressType.p2l) { if (addressPageType == LightningAddressType.p2l) {
return lightningAddress ?? return lightningAddress ??
"Error: Unable to fetch your Lightning address, please check your network connection."; "Error: Unable to fetch your Lightning address, please check your network connection.";
} }
final typeMatchingAddressesAll = _addresses final typeMatchingAddressesAll =
.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)) _addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
.toList();
// Prefer standard derivation addresses for the current/active address, // Prefer standard derivation addresses for the current/active address,
// but keep legacy addresses present in the overall address lists. // 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), ...typeMatchingAddressesAll.where((a) => a.isLegacyDerivation),
]; ];
final typeMatchingReceiveAddressesAll = final typeMatchingReceiveAddressesAll = typeMatchingAddressesAll
typeMatchingAddressesAll.where((addr) => !addr.isUsed && !hiddenAddresses.contains(addr.address)).toList(); .where((addr) => !addr.isUsed && !hiddenAddresses.contains(addr.address))
.toList();
final typeMatchingReceiveAddresses = <BitcoinAddressRecord>[ final typeMatchingReceiveAddresses = <BitcoinAddressRecord>[
...typeMatchingReceiveAddressesAll.where((a) => !a.isLegacyDerivation), ...typeMatchingReceiveAddressesAll.where((a) => !a.isLegacyDerivation),
...typeMatchingReceiveAddressesAll.where((a) => a.isLegacyDerivation), ...typeMatchingReceiveAddressesAll.where((a) => a.isLegacyDerivation),
@@ -354,34 +354,35 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return acc; return acc;
}); });
@override @override
Future<void> init() async { Future<void> init() async {
if (walletInfo.type == WalletType.bitcoinCash) { if (walletInfo.type == WalletType.bitcoinCash) {
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
} else if (walletInfo.type == WalletType.litecoin) { } else if (walletInfo.type == WalletType.litecoin) {
await _generateInitialAddresses(type: SegwitAddresType.p2wpkh); await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) { if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
await _generateInitialAddresses(type: SegwitAddresType.mweb); 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);
}
} }
} 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(); updateAddressesByMatch();
updateReceiveAddresses(); updateReceiveAddresses();
@@ -680,9 +681,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@action @action
void updateReceiveAddresses() { void updateReceiveAddresses() {
receiveAddresses.removeRange(0, receiveAddresses.length); receiveAddresses.removeRange(0, receiveAddresses.length);
final newAddresses = _addresses.where((addressRecord) => final newAddresses =
!addressRecord.isHidden && _addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
!addressRecord.isUsed);
receiveAddresses.addAll(newAddresses); receiveAddresses.addAll(newAddresses);
} }
@@ -699,12 +699,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@action @action
Future<void> discoverAddresses( Future<void> discoverAddresses(
List<BitcoinAddressRecord> addressList, List<BitcoinAddressRecord> addressList,
bool isHidden, bool isHidden,
Future<String?> Function(BitcoinAddressRecord) getAddressHistory, { Future<String?> Function(BitcoinAddressRecord) getAddressHistory, {
BitcoinAddressType type = SegwitAddresType.p2wpkh, BitcoinAddressType type = SegwitAddresType.p2wpkh,
required bool isLegacyDerivation, required bool isLegacyDerivation,
}) async { }) async {
final newAddresses = await _createNewAddresses( final newAddresses = await _createNewAddresses(
gap, gap,
startIndex: addressList.length, startIndex: addressList.length,
@@ -716,8 +716,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
addAddresses(newAddresses); addAddresses(newAddresses);
addressList.addAll(newAddresses); addressList.addAll(newAddresses);
final addressesWithHistory = final addressesWithHistory = await Future.wait(newAddresses.map(getAddressHistory));
await Future.wait(newAddresses.map(getAddressHistory));
final isLastAddressUsed = addressesWithHistory.last != null; final isLastAddressUsed = addressesWithHistory.last != null;
if (isLastAddressUsed) { if (isLastAddressUsed) {
@@ -733,12 +732,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@action @action
Future<List<BitcoinAddressRecord>> discoverAddressesBatch( Future<List<BitcoinAddressRecord>> discoverAddressesBatch(
List<BitcoinAddressRecord> addressList, List<BitcoinAddressRecord> addressList,
bool isHidden, bool isHidden,
Future<Set<String>> Function(List<BitcoinAddressRecord>) getUsedAddresses, { Future<Set<String>> Function(List<BitcoinAddressRecord>) getUsedAddresses, {
BitcoinAddressType type = SegwitAddresType.p2wpkh, BitcoinAddressType type = SegwitAddresType.p2wpkh,
required bool isLegacyDerivation, required bool isLegacyDerivation,
}) async { }) async {
final newAddresses = await _createNewAddresses( final newAddresses = await _createNewAddresses(
gap, gap,
startIndex: addressList.length, startIndex: addressList.length,
@@ -750,8 +749,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final usedAddresses = await getUsedAddresses(newAddresses); final usedAddresses = await getUsedAddresses(newAddresses);
final hasUsedAddressInGap = newAddresses.any( final hasUsedAddressInGap =
(addressRecord) => usedAddresses.contains(addressRecord.address)); newAddresses.any((addressRecord) => usedAddresses.contains(addressRecord.address));
if (!hasUsedAddressInGap) { if (!hasUsedAddressInGap) {
return newAddresses; return newAddresses;
@@ -770,64 +769,69 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return [...newAddresses, ...moreNewAddresses]; return [...newAddresses, ...moreNewAddresses];
} }
Future<void> _generateInitialAddresses( Future<void> _generateInitialAddresses(
{BitcoinAddressType type = SegwitAddresType.p2wpkh, {BitcoinAddressType type = SegwitAddresType.p2wpkh, bool isLegacyDerivation = false}) async {
bool isLegacyDerivation = false }) async { // Legacy derivation produces the same addresses as standard for these types.
// Don't generate a legacy set to avoid duplicates.
// Legacy derivation produces the same addresses as standard for these types. if (isLegacyDerivation && (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh)) {
// Don't generate a legacy set to avoid duplicates. return;
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);
}
} }
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count, var countOfReceiveAddresses = 0;
{int startIndex = 0, bool isHidden = false, BitcoinAddressType? type, bool isLegacyDerivation = false}) async { var countOfHiddenAddresses = 0;
final list = <BitcoinAddressRecord>[];
for (var i = startIndex; i < count + startIndex; i++) { _addresses.forEach((addr) {
if (addr.type == type && addr.isLegacyDerivation == isLegacyDerivation) {
final addrType = type ?? addressPageType; if (addr.isHidden) {
final hd = _hdFor(isHidden: isHidden, type: addrType, isLegacyDerivation: isLegacyDerivation); countOfHiddenAddresses += 1;
} else {
final address = BitcoinAddressRecord( countOfReceiveAddresses += 1;
await getAddressAsync(index: i, hd: hd, addressType: addrType), }
index: i,
isHidden: isHidden,
isLegacyDerivation: isLegacyDerivation,
type: addrType,
network: network,
);
list.add(address);
} }
});
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; return list;
} }
@@ -868,8 +872,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return; return;
} }
final mainHd = _hdFor(isHidden: false, type: element.type, isLegacyDerivation: element.isLegacyDerivation); final mainHd = _hdFor(
final sideHd = _hdFor(isHidden: true, type: element.type, isLegacyDerivation: element.isLegacyDerivation); isHidden: false, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
final sideHd = _hdFor(
isHidden: true, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
if (!element.isHidden && if (!element.isHidden &&
element.address != element.address !=
await getAddressAsync(index: element.index, hd: mainHd, addressType: element.type)) { 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); 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) => bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) =>
!addr.isHidden && !addr.isUsed && addr.type == type; !addr.isHidden && !addr.isUsed && addr.type == type;
@@ -907,9 +913,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final addressRecord = silentAddresses.firstWhere((addressRecord) => final addressRecord = silentAddresses.firstWhere((addressRecord) =>
addressRecord.type == SilentPaymentsAddresType.p2sp && addressRecord.address == address); addressRecord.type == SilentPaymentsAddresType.p2sp && addressRecord.address == address);
silentAddresses.remove(addressRecord); silentAddresses.remove(addressRecord);
updateAddressesByMatch(); updateAddressesByMatch();
} }
Bip32Slip10Secp256k1 _hdFor({ Bip32Slip10Secp256k1 _hdFor({
required bool isHidden, required bool isHidden,
+1 -1
View File
@@ -30,7 +30,7 @@ class BitcoinTransactionCommitFailed extends TransactionCommitFailed {
@override @override
String toString() { 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(); Future<Uint8List> getMasterFingerprint() => manager.getMasterFingerprint();
} }
class LitecoinBitboxService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService { class LitecoinBitboxService extends HardwareWalletService
with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
LitecoinBitboxService(this.manager); LitecoinBitboxService(this.manager);
final BitboxManager 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_flutter_plus/ledger_flutter_plus.dart';
import 'package:ledger_litecoin/ledger_litecoin.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) LitecoinLedgerService(this.ledgerConnection)
: litecoinLedgerApp = LitecoinLedgerApp(ledgerConnection); : litecoinLedgerApp = LitecoinLedgerApp(ledgerConnection);
@@ -53,7 +54,6 @@ class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWa
required List<PSBTReadyUtxoWithAddress> inputs, required List<PSBTReadyUtxoWithAddress> inputs,
required Map<String, PublicKeyWithDerivationPath> publicKeys, required Map<String, PublicKeyWithDerivationPath> publicKeys,
}) { }) {
final readyInputs = <LedgerTransaction>[]; final readyInputs = <LedgerTransaction>[];
for (final utxo in inputs) { for (final utxo in inputs) {
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!; final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
@@ -77,7 +77,7 @@ class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWa
inputs: readyInputs, inputs: readyInputs,
outputs: outputs outputs: outputs
.map((e) => TransactionOutput.fromBigInt((e as BitcoinOutput).value, .map((e) => TransactionOutput.fromBigInt((e as BitcoinOutput).value,
Uint8List.fromList(e.address.toScriptPubKey().toBytes()))) Uint8List.fromList(e.address.toScriptPubKey().toBytes())))
.toList(), .toList(),
changePath: changePath, changePath: changePath,
sigHashType: 0x01, sigHashType: 0x01,
+17 -15
View File
@@ -90,8 +90,7 @@ class LightningWallet {
lnurlDomain: lnurlDomain, lnurlDomain: lnurlDomain,
apiKey: apiKey, apiKey: apiKey,
privateEnabledDefault: true, privateEnabledDefault: true,
maxDepositClaimFee: MaxFee.rate(satPerVbyte: BigInt.from(5)) maxDepositClaimFee: MaxFee.rate(satPerVbyte: BigInt.from(5)));
);
final connectRequest = ConnectRequest( final connectRequest = ConnectRequest(
config: config, config: config,
@@ -105,8 +104,7 @@ class LightningWallet {
_logStream ??= initLogging().asBroadcastStream(); _logStream ??= initLogging().asBroadcastStream();
try { try {
final logFile = File("$appPath/lightning.log") final logFile = File("$appPath/lightning.log")..createSync();
..createSync();
_subscribeToLogStream(logFile); _subscribeToLogStream(logFile);
} catch (e) { } catch (e) {
printV(e); printV(e);
@@ -198,15 +196,17 @@ class LightningWallet {
} }
} }
Future<PendingLightningTransaction> createTransaction( Future<PendingLightningTransaction> createTransaction(String address, BigInt? amountSats,
String address, BigInt? amountSats, BitcoinTransactionPriority? priority, bool feesIncluded) async { BitcoinTransactionPriority? priority, bool feesIncluded) async {
final inputType = await sdk.parse(input: address); final inputType = await sdk.parse(input: address);
final feePolicy = feesIncluded ? FeePolicy.feesIncluded : FeePolicy.feesExcluded; final feePolicy = feesIncluded ? FeePolicy.feesIncluded : FeePolicy.feesExcluded;
if (inputType is InputType_Bolt11Invoice) { if (inputType is InputType_Bolt11Invoice) {
final request = PrepareSendPaymentRequest( 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 prepareResponse = await sdk.prepareSendPayment(request: request);
final paymentMethod = prepareResponse.paymentMethod; final paymentMethod = prepareResponse.paymentMethod;
@@ -392,14 +392,16 @@ class LightningWallet {
return _getElectrumTransactionInfoFromPayment(response.payment); return _getElectrumTransactionInfoFromPayment(response.payment);
} }
Future<String> refundDeposit(String txId, int vout, String destinationAddress, Future<String> refundDeposit(
BigInt feeRate) async { String txId, int vout, String destinationAddress, BigInt feeRate) async {
final response = await sdk.refundDeposit(request: RefundDepositRequest( final response = await sdk.refundDeposit(
txid: txId, request: RefundDepositRequest(
vout: vout, txid: txId,
destinationAddress: destinationAddress, vout: vout,
fee: Fee.rate(satPerVbyte: feeRate), destinationAddress: destinationAddress,
),); fee: Fee.rate(satPerVbyte: feeRate),
),
);
return response.txHex; return response.txHex;
} }
@@ -10,10 +10,9 @@ class PendingLightningTransaction with PendingTransaction {
required this.commitOverride, required this.commitOverride,
}); });
final bool isSendAll; final bool isSendAll;
Future<String> Function() commitOverride; Future<String> Function() commitOverride;
final List<void Function()> _listeners =[]; final List<void Function()> _listeners = [];
@override @override
String id; String id;
+30 -26
View File
@@ -175,7 +175,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
@override @override
bool get hasRescan => true; bool get hasRescan => true;
final String? scanSecretOverride; final String? scanSecretOverride;
final String? spendPubkeyOverride; final String? spendPubkeyOverride;
List<int> get scanSecret => (scanSecretOverride != null && scanSecretOverride?.isNotEmpty == true) List<int> get scanSecret => (scanSecretOverride != null && scanSecretOverride?.isNotEmpty == true)
@@ -232,8 +231,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
} }
@override @override
WalletKeysData get walletKeysData => WalletKeysData get walletKeysData => WalletKeysData(
WalletKeysData(mnemonic: seed, xPub: xpub, passphrase: passphrase, scanSecret: scanSecretOverride, spendPubkey: spendPubkeyOverride); mnemonic: seed,
xPub: xpub,
passphrase: passphrase,
scanSecret: scanSecretOverride,
spendPubkey: spendPubkeyOverride);
static Future<LitecoinWallet> open({ static Future<LitecoinWallet> open({
required String name, required String name,
@@ -1106,15 +1109,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
for (final utxo in transaction.utxos) { for (final utxo in transaction.utxos) {
if (utxo.utxo.scriptType != SegwitAddresType.mweb) { if (utxo.utxo.scriptType != SegwitAddresType.mweb) {
inputs.add(utxo.utxo.toInput()); inputs.add(utxo.utxo.toInput());
txouts.add(TxOut(value: Int64(utxo.utxo.value.toInt()), txouts.add(TxOut(
pkScript: utxo.ownerDetails.address.toScriptPubKey().toBytes())); value: Int64(utxo.utxo.value.toInt()),
pkScript: utxo.ownerDetails.address.toScriptPubKey().toBytes()));
} }
} }
var resp = await CwMweb.psbtCreate(PsbtCreateRequest( var resp = await CwMweb.psbtCreate(PsbtCreateRequest(
rawTx: inputs.isEmpty ? null : BtcTransaction( rawTx: inputs.isEmpty
inputs: inputs, ? null
outputs: isMweb ? [] : transaction.outputs, : BtcTransaction(
).toBytes(), inputs: inputs,
outputs: isMweb ? [] : transaction.outputs,
).toBytes(),
witnessUtxo: txouts, witnessUtxo: txouts,
)); ));
for (final utxo in transaction.utxos) { 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) { if (isMweb)
var address = addressFromOutputScript(output.scriptPubKey, LitecoinNetwork.mainnet); for (final output in transaction.outputs) {
if (output.scriptPubKey.getAddressType() == SegwitAddresType.mweb) { var address = addressFromOutputScript(output.scriptPubKey, LitecoinNetwork.mainnet);
address = SegwitBech32Encoder.encode("ltcmweb", 0, output.scriptPubKey.toBytes()); 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); return base64.decode(resp.psbtB64);
} }
@@ -1203,7 +1210,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
for (final utxo in tx.utxos) { for (final utxo in tx.utxos) {
if (utxo.utxo.scriptType == SegwitAddresType.mweb) { if (utxo.utxo.scriptType == SegwitAddresType.mweb) {
hasMwebInput = true; hasMwebInput = true;
} else { } else {
// check if any of the inputs of this transaction are hog-ex: // check if any of the inputs of this transaction are hog-ex:
// this list is only non-mweb inputs: // this list is only non-mweb inputs:
@@ -1256,9 +1262,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
final utxo = unspentCoins final utxo = unspentCoins
.firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex); .firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex);
final key = generateECPrivate( final key = generateECPrivate(
hd: utxo.bitcoinAddressRecord.isHidden hd: utxo.bitcoinAddressRecord.isHidden ? sideHd : mainHd,
? sideHd
: mainHd,
index: utxo.bitcoinAddressRecord.index, index: utxo.bitcoinAddressRecord.index,
network: network); network: network);
final digest = tx2.getTransactionSegwitDigit( final digest = tx2.getTransactionSegwitDigit(
@@ -1284,8 +1288,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
} }
} }
void addTransactionListener(PendingBitcoinTransaction tx, void addTransactionListener(
List<String> inputAddresses, bool isPegIn, bool isPegOut) { PendingBitcoinTransaction tx, List<String> inputAddresses, bool isPegIn, bool isPegOut) {
tx.addListener((transaction) async { tx.addListener((transaction) async {
final addresses = <String>{}; final addresses = <String>{};
transaction.inputAddresses?.addAll(inputAddresses); transaction.inputAddresses?.addAll(inputAddresses);
@@ -1575,7 +1579,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
rawTx: rawTx, rawTx: rawTx,
ownerDetails: utxo.ownerDetails, ownerDetails: utxo.ownerDetails,
ownerDerivationPath: publicKeyAndDerivationPath.derivationPath, ownerDerivationPath: publicKeyAndDerivationPath.derivationPath,
ownerMasterFingerprint:masterFingerprint, ownerMasterFingerprint: masterFingerprint,
ownerPublicKey: publicKeyAndDerivationPath.publicKey, ownerPublicKey: publicKeyAndDerivationPath.publicKey,
)); ));
} }
+13 -7
View File
@@ -59,9 +59,11 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
? hex.decode(scanSecretOverride!) ? hex.decode(scanSecretOverride!)
: mwebHd?.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw ?? List.filled(32, 0); : mwebHd?.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw ?? List.filled(32, 0);
List<int> get spendPubkey => (spendPubkeyOverride != null && spendPubkeyOverride?.isNotEmpty == true) List<int> get spendPubkey =>
? hex.decode(spendPubkeyOverride!) (spendPubkeyOverride != null && spendPubkeyOverride?.isNotEmpty == true)
: mwebHd?.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed ?? List.filled(32, 0); ? hex.decode(spendPubkeyOverride!)
: mwebHd?.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed ??
List.filled(32, 0);
@override @override
Future<void> init() async { Future<void> init() async {
@@ -81,7 +83,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
return null; return null;
} }
if ((scanSecret.length < 1 || scanSecret.reduce((a, b) => a + b) == 0) && 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; return null;
} }
@@ -230,12 +232,16 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
// don't use mweb addresses for exchange refund address: // don't use mweb addresses for exchange refund address:
final current = getFreshAddress(); final current = getFreshAddress();
final bool isMweb = receiveAddresses final bool isMweb =
.any((e) => e.address == current && e.type == SegwitAddresType.mweb); receiveAddresses.any((e) => e.address == current && e.type == SegwitAddresType.mweb);
if (isMweb) { if (isMweb) {
final segwit = receiveAddresses 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) .map((e) => e.address)
.toList(); .toList();
+9 -8
View File
@@ -48,7 +48,8 @@ class LitecoinWalletService extends WalletService<
password: credentials.password!, password: credentials.password!,
passphrase: credentials.passphrase, passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()), derivationInfo:
credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
); );
@@ -64,7 +65,6 @@ class LitecoinWalletService extends WalletService<
@override @override
Future<LitecoinWallet> openWallet(String name, String password) async { Future<LitecoinWallet> openWallet(String name, String password) async {
final walletInfo = await WalletInfo.get(name, getType()); final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) { if (walletInfo == null) {
throw Exception('Wallet not found'); throw Exception('Wallet not found');
@@ -125,8 +125,9 @@ class LitecoinWalletService extends WalletService<
} }
} }
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where( final unspentCoinsToDelete = unspentCoinsInfoSource.values
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList(); .where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
.toList();
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).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; final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
credentials.walletInfo?.network = network.value; credentials.walletInfo?.network = network.value;
final derivationInfo = await credentials.walletInfo!.getDerivationInfo(); final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
derivationInfo.derivationPath = derivationInfo.derivationPath = credentials.hwAccountData.derivationPath;
credentials.hwAccountData.derivationPath;
await derivationInfo.save(); await derivationInfo.save();
credentials.walletInfo!.save(); credentials.walletInfo!.save();
@@ -170,7 +170,7 @@ class LitecoinWalletService extends WalletService<
@override @override
Future<LitecoinWallet> restoreFromKeys(LitecoinWalletFromKeysCredentials credentials, Future<LitecoinWallet> restoreFromKeys(LitecoinWalletFromKeysCredentials credentials,
{bool? isTestnet}) async { {bool? isTestnet}) async {
final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet; final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
credentials.walletInfo?.network = network.value; credentials.walletInfo?.network = network.value;
@@ -202,7 +202,8 @@ class LitecoinWalletService extends WalletService<
passphrase: credentials.passphrase, passphrase: credentials.passphrase,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()), derivationInfo:
credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
); );
+13 -18
View File
@@ -108,16 +108,14 @@ class PayjoinManager {
Future<Sender> initSender( Future<Sender> initSender(
String pjUriString, String originalPsbt, int networkFeesSatPerVb) async { String pjUriString, String originalPsbt, int networkFeesSatPerVb) async {
try { try {
final pjUri = final pjUri = (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
(await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250); final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250);
final senderBuilder = await SenderBuilder.fromPsbtAndUri( final senderBuilder = await SenderBuilder.fromPsbtAndUri(
psbtBase64: originalPsbt, psbtBase64: originalPsbt,
pjUri: pjUri, pjUri: pjUri,
); );
final persister = PayjoinSenderPersister.impl(); final persister = PayjoinSenderPersister.impl();
final newSender = final newSender = await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
final senderToken = await newSender.persist(persister: persister); final senderToken = await newSender.persist(persister: persister);
return Sender.load(token: senderToken, persister: persister); return Sender.load(token: senderToken, persister: persister);
@@ -133,8 +131,7 @@ class PayjoinManager {
bool isTestnet = false, bool isTestnet = false,
}) async { }) async {
final pjUri = Uri.parse(pjUrl).queryParameters['pj']!; final pjUri = Uri.parse(pjUrl).queryParameters['pj']!;
await _payjoinStorage.insertSenderSession( await _payjoinStorage.insertSenderSession(sender, pjUri, _wallet.id, amount);
sender, pjUri, _wallet.id, amount);
return _spawnSender(isTestnet: isTestnet, sender: sender, pjUri: pjUri); return _spawnSender(isTestnet: isTestnet, sender: sender, pjUri: pjUri);
} }
@@ -207,8 +204,7 @@ class PayjoinManager {
return completer.future; return completer.future;
} }
Future<Receiver> getUnusedReceiver(String address, Future<Receiver> getUnusedReceiver(String address, [bool isTestnet = false]) async {
[bool isTestnet = false]) async {
final session = _payjoinStorage.getUnusedActiveReceiverSession(_wallet.id); final session = _payjoinStorage.getUnusedActiveReceiverSession(_wallet.id);
if (session != null) { if (session != null) {
@@ -220,7 +216,8 @@ class PayjoinManager {
return initReceiver(address); 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"); if (retryCount > 0) writePayjoinLog("Retrying initReceiver ${retryCount + 1} attempt");
try { try {
@@ -245,7 +242,6 @@ class PayjoinManager {
} catch (e) { } catch (e) {
writePayjoinLog(e.toString()); writePayjoinLog(e.toString());
if (e.toString().contains("error sending request for url") && retryCount < 5) { if (e.toString().contains("error sending request for url") && retryCount < 5) {
return initReceiver(address, isTestnet, ++retryCount); return initReceiver(address, isTestnet, ++retryCount);
} else { } else {
rethrow; rethrow;
@@ -273,13 +269,11 @@ class PayjoinManager {
rawAmount = getOutputAmountFromTx(tx, _wallet); rawAmount = getOutputAmountFromTx(tx, _wallet);
break; break;
case PayjoinReceiverRequestTypes.checkIsOwned: case PayjoinReceiverRequestTypes.checkIsOwned:
(_wallet.walletAddresses as BitcoinWalletAddresses) (_wallet.walletAddresses as BitcoinWalletAddresses).newPayjoinReceiver();
.newPayjoinReceiver();
_payjoinStorage.markReceiverSessionInProgress(receiver.id()); _payjoinStorage.markReceiverSessionInProgress(receiver.id());
final inputScript = message['input_script'] as Uint8List; final inputScript = message['input_script'] as Uint8List;
final isOwned = final isOwned = _wallet.isMine(Script.fromRaw(byteData: inputScript));
_wallet.isMine(Script.fromRaw(byteData: inputScript));
mainToIsolateSendPort?.send({ mainToIsolateSendPort?.send({
'requestId': message['requestId'], 'requestId': message['requestId'],
'result': isOwned, 'result': isOwned,
@@ -288,8 +282,7 @@ class PayjoinManager {
case PayjoinReceiverRequestTypes.checkIsReceiverOutput: case PayjoinReceiverRequestTypes.checkIsReceiverOutput:
final outputScript = message['output_script'] as Uint8List; final outputScript = message['output_script'] as Uint8List;
final isReceiverOutput = final isReceiverOutput = _wallet.isMine(Script.fromRaw(byteData: outputScript));
_wallet.isMine(Script.fromRaw(byteData: outputScript));
mainToIsolateSendPort?.send({ mainToIsolateSendPort?.send({
'requestId': message['requestId'], 'requestId': message['requestId'],
'result': isReceiverOutput, 'result': isReceiverOutput,
@@ -310,7 +303,8 @@ class PayjoinManager {
case PayjoinReceiverRequestTypes.processPsbt: case PayjoinReceiverRequestTypes.processPsbt:
final psbt = message['psbt'] as String; 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); final signedPsbt = await _wallet.signPsbt(psbt, utxos);
mainToIsolateSendPort?.send({ mainToIsolateSendPort?.send({
@@ -322,7 +316,8 @@ class PayjoinManager {
case PayjoinReceiverRequestTypes.proposalSent: case PayjoinReceiverRequestTypes.proposalSent:
_cleanupSession(receiver.id()); _cleanupSession(receiver.id());
final psbt = message['psbt'] as String; final psbt = message['psbt'] as String;
writePayjoinLog("Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.proposalSent: $psbt"); writePayjoinLog(
"Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.proposalSent: $psbt");
await _payjoinStorage.markReceiverSessionComplete( await _payjoinStorage.markReceiverSessionComplete(
receiver.id(), getTxIdFromPsbtV0(psbt), rawAmount); receiver.id(), getTxIdFromPsbtV0(psbt), rawAmount);
@@ -47,8 +47,7 @@ class PayjoinReceiverWorker {
try { try {
final receiver = Receiver.fromJson(json: receiverJson); final receiver = Receiver.fromJson(json: receiverJson);
final uncheckedProposal = final uncheckedProposal = await worker.receiveUncheckedProposal(receiver);
await worker.receiveUncheckedProposal(receiver);
final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast(); final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast();
sendPort.send({ sendPort.send({
@@ -112,8 +111,7 @@ class PayjoinReceiverWorker {
final httpRequest = await client.post(url, final httpRequest = await client.post(url,
headers: {'Content-Type': request.contentType}, body: request.body); headers: {'Content-Type': request.contentType}, body: request.body);
final proposal = await session.processRes( final proposal = await session.processRes(body: httpRequest.bodyBytes, ctx: extractReq.$2);
body: httpRequest.bodyBytes, ctx: extractReq.$2);
if (proposal != null) return proposal; if (proposal != null) return proposal;
sleep(Duration(seconds: 2)); sleep(Duration(seconds: 2));
} }
@@ -140,8 +138,7 @@ class PayjoinReceiverWorker {
return await finalProposal.psbt(); return await finalProposal.psbt();
} }
Future<PayjoinProposal> processPayjoinProposal( Future<PayjoinProposal> processPayjoinProposal(UncheckedProposal proposal) async {
UncheckedProposal proposal) async {
await proposal.extractTxToScheduleBroadcast(); await proposal.extractTxToScheduleBroadcast();
// TODO Handle this. send to the main port on a timer? // TODO Handle this. send to the main port on a timer?
@@ -174,20 +171,17 @@ class PayjoinReceiverWorker {
); );
final pj5 = await pj4.commitOutputs(); final pj5 = await pj4.commitOutputs();
final listUnspent = final listUnspent = await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
final unspent = listUnspent as List<UtxoWithPrivateKey>; final unspent = listUnspent as List<UtxoWithPrivateKey>;
if (unspent.isEmpty) throw RecoverableError('No unspent outputs available'); if (unspent.isEmpty) throw RecoverableError('No unspent outputs available');
final candidateInputs = final candidateInputs = await Future.wait(unspent.map(_inputPairFromUtxo));
await Future.wait(unspent.map(_inputPairFromUtxo));
// Prefer a UTXO that avoids the Unnecessary Input Heuristic (UIH2); // Prefer a UTXO that avoids the Unnecessary Input Heuristic (UIH2);
// fall back to the first candidate if none preserves privacy. // fall back to the first candidate if none preserves privacy.
InputPair selectedUtxo = candidateInputs.first; InputPair selectedUtxo = candidateInputs.first;
try { try {
selectedUtxo = selectedUtxo = await pj5.tryPreservingPrivacy(candidateInputs: candidateInputs);
await pj5.tryPreservingPrivacy(candidateInputs: candidateInputs);
} catch (_) {} } catch (_) {}
final pj6 = await pj5.contributeInputs(replacementInputs: [selectedUtxo]); final pj6 = await pj5.contributeInputs(replacementInputs: [selectedUtxo]);
@@ -196,8 +190,8 @@ class PayjoinReceiverWorker {
// Finalize proposal // Finalize proposal
final payjoinProposal = await pj7.finalizeProposal( final payjoinProposal = await pj7.finalizeProposal(
processPsbt: (String psbt) async { processPsbt: (String psbt) async {
final result = await _sendRequest( final result =
PayjoinReceiverRequestTypes.processPsbt, {'psbt': psbt}); await _sendRequest(PayjoinReceiverRequestTypes.processPsbt, {'psbt': psbt});
return result as String; return result as String;
}, },
// TODO set maxFeeRateSatPerVb // TODO set maxFeeRateSatPerVb
@@ -213,15 +207,12 @@ class PayjoinReceiverWorker {
Future<InputPair> _inputPairFromUtxo(UtxoWithPrivateKey utxo) async { Future<InputPair> _inputPairFromUtxo(UtxoWithPrivateKey utxo) async {
final txout = TxOut( final txout = TxOut(
value: utxo.utxo.value, value: utxo.utxo.value,
scriptPubkey: Uint8List.fromList( scriptPubkey: Uint8List.fromList(utxo.ownerDetails.address.toScriptPubKey().toBytes()),
utxo.ownerDetails.address.toScriptPubKey().toBytes()),
); );
final psbtin = final psbtin = PsbtInput(witnessUtxo: txout, redeemScript: null, witnessScript: null);
PsbtInput(witnessUtxo: txout, redeemScript: null, witnessScript: null);
final previousOutput = final previousOutput = OutPoint(txid: utxo.utxo.txHash, vout: utxo.utxo.vout);
OutPoint(txid: utxo.utxo.txHash, vout: utxo.utxo.vout);
final txin = TxIn( final txin = TxIn(
previousOutput: previousOutput, previousOutput: previousOutput,
@@ -46,11 +46,11 @@ class PayjoinSenderWorker {
sendPort.send(e); sendPort.send(e);
} }
} }
final client = ProxyWrapper().getHttpIOClient(); final client = ProxyWrapper().getHttpIOClient();
/// Run a payjoin sender (V2 protocol first, fallback to V1). /// Run a payjoin sender (V2 protocol first, fallback to V1).
Future<String> runSender(Sender sender) async { Future<String> runSender(Sender sender) async {
try { try {
return await _runSenderV2(sender); return await _runSenderV2(sender);
} catch (e) { } catch (e) {
@@ -70,13 +70,11 @@ class PayjoinSenderWorker {
Future<String> _runSenderV2(Sender sender) async { Future<String> _runSenderV2(Sender sender) async {
try { try {
final postRequest = await sender.extractV2( final postRequest = await sender.extractV2(
ohttpProxyUrl: ohttpProxyUrl: await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
); );
final postResult = await _postRequest(postRequest.$1); final postResult = await _postRequest(postRequest.$1);
final getContext = final getContext = await postRequest.$2.processResponse(response: postResult);
await postRequest.$2.processResponse(response: postResult);
sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted, "pj": pjUrl}); sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted, "pj": pjUrl});
+14 -20
View File
@@ -23,16 +23,14 @@ class PayjoinStorage {
), ),
); );
PayjoinSession? getUnusedActiveReceiverSession(String walletId) => PayjoinSession? getUnusedActiveReceiverSession(String walletId) => _payjoinSessionSources.values
_payjoinSessionSources.values .where((session) =>
.where((session) => session.walletId == walletId &&
session.walletId == walletId && session.status == PayjoinSessionStatus.created.name &&
session.status == PayjoinSessionStatus.created.name && !session.isSenderSession)
!session.isSenderSession) .firstOrNull;
.firstOrNull;
Future<void> markReceiverSessionComplete( Future<void> markReceiverSessionComplete(String sessionId, String txId, String amount) async {
String sessionId, String txId, String amount) async {
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!; final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
session.status = PayjoinSessionStatus.success.name; session.status = PayjoinSessionStatus.success.name;
@@ -41,8 +39,7 @@ class PayjoinStorage {
await session.save(); await session.save();
} }
Future<void> markReceiverSessionUnrecoverable( Future<void> markReceiverSessionUnrecoverable(String sessionId, String reason) async {
String sessionId, String reason) async {
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!; final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
session.status = PayjoinSessionStatus.unrecoverable.name; session.status = PayjoinSessionStatus.unrecoverable.name;
@@ -92,13 +89,10 @@ class PayjoinStorage {
await session.save(); await session.save();
} }
List<PayjoinSession> readAllOpenSessions(String walletId) => List<PayjoinSession> readAllOpenSessions(String walletId) => _payjoinSessionSources.values
_payjoinSessionSources.values .where((session) =>
.where((session) => session.walletId == walletId &&
session.walletId == walletId && ![PayjoinSessionStatus.success.name, PayjoinSessionStatus.unrecoverable.name]
![ .contains(session.status))
PayjoinSessionStatus.success.name, .toList();
PayjoinSessionStatus.unrecoverable.name
].contains(session.status))
.toList();
} }
+19 -38
View File
@@ -40,8 +40,7 @@ extension PsbtSigner on PsbtV2 {
return tx.buffer(); return tx.buffer();
} }
Future<void> signWithUTXO( Future<void> signWithUTXO(List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
[UTXOGetterCallBack? getTaprootPair]) async { [UTXOGetterCallBack? getTaprootPair]) async {
final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false)); final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false));
final tx = BtcTransaction.fromRaw(raw); final tx = BtcTransaction.fromRaw(raw);
@@ -53,8 +52,8 @@ extension PsbtSigner on PsbtV2 {
if (utxos.any((e) => e.utxo.isP2tr())) { if (utxos.any((e) => e.utxo.isP2tr())) {
for (final input in tx.inputs) { for (final input in tx.inputs) {
final utxo = utxos.firstWhereOrNull( final utxo = utxos
(u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex); .firstWhereOrNull((u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
if (utxo == null) { if (utxo == null) {
final trPair = await getTaprootPair!.call(input.txId, input.txIndex); final trPair = await getTaprootPair!.call(input.txId, input.txIndex);
@@ -81,8 +80,8 @@ extension PsbtSigner on PsbtV2 {
: BitcoinOpCodeConst.SIGHASH_ALL; : BitcoinOpCodeConst.SIGHASH_ALL;
/// We generate transaction digest for current input /// We generate transaction digest for current input
final digest = _generateTransactionDigest( final digest =
script, i, utxo.utxo, tx, taprootAmounts, taprootScripts); _generateTransactionDigest(script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
/// now we need sign the transaction digest /// now we need sign the transaction digest
final sig = signer(digest, utxo, utxo.privateKey, sighash); final sig = signer(digest, utxo, utxo.privateKey, sighash);
@@ -90,21 +89,14 @@ extension PsbtSigner on PsbtV2 {
if (utxo.utxo.isP2tr()) { if (utxo.utxo.isP2tr()) {
setInputTapKeySig(i, Uint8List.fromList(BytesUtils.fromHexString(sig))); setInputTapKeySig(i, Uint8List.fromList(BytesUtils.fromHexString(sig)));
} else { } else {
setInputPartialSig( setInputPartialSig(i, Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
i,
Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
Uint8List.fromList(BytesUtils.fromHexString(sig))); Uint8List.fromList(BytesUtils.fromHexString(sig)));
} }
} }
} }
List<int> _generateTransactionDigest( List<int> _generateTransactionDigest(Script scriptPubKeys, int input, BitcoinUtxo utxo,
Script scriptPubKeys, BtcTransaction transaction, List<BigInt> taprootAmounts, List<Script> tapRootPubKeys) {
int input,
BitcoinUtxo utxo,
BtcTransaction transaction,
List<BigInt> taprootAmounts,
List<Script> tapRootPubKeys) {
if (utxo.isSegwit()) { if (utxo.isSegwit()) {
if (utxo.isP2tr()) { if (utxo.isP2tr()) {
return transaction.getTransactionTaprootDigset( return transaction.getTransactionTaprootDigset(
@@ -116,8 +108,7 @@ extension PsbtSigner on PsbtV2 {
return transaction.getTransactionSegwitDigit( return transaction.getTransactionSegwitDigit(
txInIndex: input, script: scriptPubKeys, amount: utxo.value); txInIndex: input, script: scriptPubKeys, amount: utxo.value);
} }
return transaction.getTransactionDigest( return transaction.getTransactionDigest(txInIndex: input, script: scriptPubKeys);
txInIndex: input, script: scriptPubKeys);
} }
Script _findLockingScript(UtxoWithAddress utxo, bool isTaproot) { Script _findLockingScript(UtxoWithAddress utxo, bool isTaproot) {
@@ -172,11 +163,10 @@ extension PsbtSigner on PsbtV2 {
} }
} }
typedef UTXOSignerCallBack = String Function(List<int> trDigest, typedef UTXOSignerCallBack = String Function(
UtxoWithAddress utxo, ECPrivate privateKey, int sighash); List<int> trDigest, UtxoWithAddress utxo, ECPrivate privateKey, int sighash);
typedef UTXOGetterCallBack = Future<TaprootAmountScriptPair> Function( typedef UTXOGetterCallBack = Future<TaprootAmountScriptPair> Function(String txId, int vout);
String txId, int vout);
class TaprootAmountScriptPair { class TaprootAmountScriptPair {
final BigInt value; final BigInt value;
@@ -216,23 +206,17 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
} }
return UtxoWithPrivateKey( return UtxoWithPrivateKey(
utxo: input.utxo, utxo: input.utxo, ownerDetails: input.ownerDetails, privateKey: key.privkey);
ownerDetails: input.ownerDetails,
privateKey: key.privkey);
} }
factory UtxoWithPrivateKey.fromUnspent( factory UtxoWithPrivateKey.fromUnspent(BitcoinUnspent input, BitcoinWalletBase wallet) {
BitcoinUnspent input, BitcoinWalletBase wallet) { final address = RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
final address =
RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
final newHd = final newHd = input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.mainHd;
input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.mainHd;
ECPrivate privkey; ECPrivate privkey;
if (input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { if (input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
final unspentAddress = final unspentAddress = input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
privkey = wallet.walletAddresses.silentAddress!.b_spend.tweakAdd( privkey = wallet.walletAddresses.silentAddress!.b_spend.tweakAdd(
BigintUtils.fromBytes( BigintUtils.fromBytes(
BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!), BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!),
@@ -240,9 +224,7 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
); );
} else { } else {
privkey = generateECPrivate( privkey = generateECPrivate(
hd: newHd, hd: newHd, index: input.bitcoinAddressRecord.index, network: BitcoinNetwork.mainnet);
index: input.bitcoinAddressRecord.index,
network: BitcoinNetwork.mainnet);
} }
return UtxoWithPrivateKey( return UtxoWithPrivateKey(
@@ -251,8 +233,7 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
value: BigInt.from(input.value), value: BigInt.from(input.value),
vout: input.vout, vout: input.vout,
scriptType: input.bitcoinAddressRecord.type, scriptType: input.bitcoinAddressRecord.type,
isSilentPayment: isSilentPayment: input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
), ),
ownerDetails: UtxoAddressDetails( ownerDetails: UtxoAddressDetails(
publicKey: privkey.getPublic().toHex(), publicKey: privkey.getPublic().toHex(),
+14 -17
View File
@@ -11,7 +11,10 @@ class PSBTTransactionBuild {
final PsbtV2 psbt = PsbtV2(); final PsbtV2 psbt = PsbtV2();
PSBTTransactionBuild( 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.setGlobalTxVersion(2);
psbt.setGlobalInputCount(inputs.length); psbt.setGlobalInputCount(inputs.length);
psbt.setGlobalOutputCount(outputs.length); psbt.setGlobalOutputCount(outputs.length);
@@ -23,11 +26,11 @@ class PSBTTransactionBuild {
printV(input.utxo.isSegwit()); printV(input.utxo.isSegwit());
printV(input.utxo.isP2shSegwit()); 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.setInputOutputIndex(i, input.utxo.vout);
psbt.setInputSequence(i, enableRBF ? 0xfffffffd : 0xffffffff); psbt.setInputSequence(i, enableRBF ? 0xfffffffd : 0xffffffff);
if (input.utxo.isSegwit()) { if (input.utxo.isSegwit()) {
setInputSegwit(i, input); setInputSegwit(i, input);
} else if (input.utxo.isP2shSegwit()) { } else if (input.utxo.isP2shSegwit()) {
@@ -48,10 +51,10 @@ class PSBTTransactionBuild {
if (cwOutputs.isNotEmpty) { if (cwOutputs.isNotEmpty) {
try { try {
final cwOutput = cwOutputs final cwOutput = cwOutputs
.where((e) => [e.address, e.extractedAddress] .where((e) => [e.address, e.extractedAddress]
.map((e) => e?.toLowerCase()) .map((e) => e?.toLowerCase())
.contains(output.address.toAddress().toLowerCase())) .contains(output.address.toAddress().toLowerCase()))
.firstOrNull; .firstOrNull;
if (cwOutput != null && if (cwOutput != null &&
cwOutput.extra.containsKey('bip353_name') && cwOutput.extra.containsKey('bip353_name') &&
cwOutput.extra.containsKey('bip353_proof')) { cwOutput.extra.containsKey('bip353_proof')) {
@@ -80,20 +83,14 @@ class PSBTTransactionBuild {
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) { void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx))); psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
psbt.setInputBip32Derivation( psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
i, input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
input.ownerMasterFingerprint,
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
} }
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) { void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx))); psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
psbt.setInputBip32Derivation( psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
i, input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
input.ownerMasterFingerprint,
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)), psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes())); 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) { String getTxIdFromPsbtV0(String psbt) {
final psbtV2 = PsbtV2()..deserializeV0(base64.decode(psbt)); final psbtV2 = PsbtV2()..deserializeV0(base64.decode(psbt));
return BtcTransaction.fromRaw( return BtcTransaction.fromRaw(BytesUtils.toHexString(psbtV2.extractUnsignedTX(false))).txId();
BytesUtils.toHexString(psbtV2.extractUnsignedTX(false)))
.txId();
} }
String getOutputAmountFromPsbt(String psbtV0, BitcoinWalletBase wallet) { 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; import 'package:ledger_bitcoin/src/utils/uint8list_extension.dart' as ext;
extension PsbtSigner on PsbtV2 { extension PsbtSigner on PsbtV2 {
void deserializeV0(Uint8List psbt) { void deserializeV0(Uint8List psbt) {
final bufferReader = BufferReader(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"); throw Exception("Invalid magic bytes");
} }
while (_readKeyPair(globalMap, bufferReader)) {} while (_readKeyPair(globalMap, bufferReader)) {}
@@ -26,7 +26,8 @@ extension PsbtSigner on PsbtV2 {
while (_readKeyPair(inputMaps[i], bufferReader)) {} while (_readKeyPair(inputMaps[i], bufferReader)) {}
final input = tx.inputs[i]; final input = tx.inputs[i];
setInputOutputIndex(i, input.txIndex); 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)); setInputSequence(i, Uint8List.fromList(input.sequence).readUint32LE(0));
} }
for (var i = 0; i < getGlobalOutputCount(); i++) { 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 /// through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
extension InputFinalizer on PsbtV2 { extension InputFinalizer on PsbtV2 {
void finalizeV0() { void finalizeV0() {
// First check that each input has a signature // First check that each input has a signature
for (var i = 0; i < getGlobalInputCount(); i++) { for (var i = 0; i < getGlobalInputCount(); i++) {
if (_isFinalized(i)) continue; if (_isFinalized(i)) continue;
@@ -31,8 +30,7 @@ extension InputFinalizer on PsbtV2 {
} }
if (legacyPubkeys.isNotEmpty) { if (legacyPubkeys.isNotEmpty) {
if (legacyPubkeys.length > 1) { if (legacyPubkeys.length > 1) {
throw Exception( throw Exception('Expected exactly one signature, got ${legacyPubkeys.length}');
'Expected exactly one signature, got ${legacyPubkeys.length}');
} }
if (taprootSig != null) { if (taprootSig != null) {
throw Exception('Both taproot and non-taproot signatures present.'); throw Exception('Both taproot and non-taproot signatures present.');
@@ -55,8 +53,7 @@ extension InputFinalizer on PsbtV2 {
setInputFinalScriptwitness(i, witnessBuf.buffer()); setInputFinalScriptwitness(i, witnessBuf.buffer());
if (isWrappedSegwit) { if (isWrappedSegwit) {
if (redeemScript.isEmpty) { if (redeemScript.isEmpty) {
throw Exception( throw Exception("Expected non-empty redeemscript. Can't finalize input $i");
"Expected non-empty redeemscript. Can't finalize input $i");
} }
final scriptSigBuf = BufferWriter() final scriptSigBuf = BufferWriter()
..writeUInt8(redeemScript.length) // Push redeemScript length ..writeUInt8(redeemScript.length) // Push redeemScript length
@@ -87,8 +87,9 @@ class BitcoinCashWalletService extends WalletService<
} }
await WalletInfo.delete(walletInfo); await WalletInfo.delete(walletInfo);
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where( final unspentCoinsToDelete = unspentCoinsInfoSource.values
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList(); .where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
.toList();
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList(); final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
@@ -122,8 +123,7 @@ class BitcoinCashWalletService extends WalletService<
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
passphrase: credentials.passphrase passphrase: credentials.passphrase);
);
await wallet.save(); await wallet.save();
await wallet.init(); await wallet.init();
return wallet; return wallet;
-1
View File
@@ -1,7 +1,6 @@
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
abstract class AccountList<T> { abstract class AccountList<T> {
ObservableList<T> get accounts; ObservableList<T> get accounts;
void update(); void update();
+1 -3
View File
@@ -41,7 +41,5 @@ class AddressInfoAdapter extends TypeAdapter<AddressInfo> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is AddressInfoAdapter && other is AddressInfoAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
-1
View File
@@ -10,7 +10,6 @@ String _smartAmountSanitizer(String amount) {
return amount.replaceAll(",", "."); return amount.replaceAll(",", ".");
} }
extension SanitizerUserAmounts on String { extension SanitizerUserAmounts on String {
String sanitized() => _smartAmountSanitizer(this); String sanitized() => _smartAmountSanitizer(this);
} }
+4 -5
View File
@@ -9,19 +9,18 @@ class AmountConverter {
amount = amount.replaceAll(',', ''); amount = amount.replaceAll(',', '');
final parts = amount.split('.'); final parts = amount.split('.');
final whole = parts[0].isEmpty ? '0' : parts[0]; 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 fracPadded = (frac + '0' * decimals).substring(0, decimals);
final pow10 = BigInt.from(10).pow(decimals); final pow10 = BigInt.from(10).pow(decimals);
final wholeBI = BigInt.parse(whole); 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; final res = wholeBI * pow10 + fracBI;
return neg ? '-${res.toString()}' : res.toString(); return neg ? '-${res.toString()}' : res.toString();
} }
static String fromBaseUnits(String units, int decimals) { static String fromBaseUnits(String units, int decimals) {
units = units.trim(); units = units.trim();
if (units.isEmpty) return '0'; if (units.isEmpty) return '0';
@@ -35,14 +34,14 @@ class AmountConverter {
if (units.length <= decimals) { if (units.length <= decimals) {
final s = units.padLeft(decimals + 1, '0'); // ensures at least "0xxxx" final s = units.padLeft(decimals + 1, '0'); // ensures at least "0xxxx"
final intPart = s.substring(0, s.length - decimals); 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+$'), ''); fracPart = fracPart.replaceFirst(RegExp(r'0+$'), '');
final out = fracPart.isEmpty ? intPart : '$intPart.$fracPart'; final out = fracPart.isEmpty ? intPart : '$intPart.$fracPart';
return neg ? '-$out' : out; return neg ? '-$out' : out;
} }
final intPart = units.substring(0, units.length - decimals); 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+$'), ''); fracPart = fracPart.replaceFirst(RegExp(r'0+$'), '');
final out = fracPart.isEmpty ? intPart : '$intPart.$fracPart'; final out = fracPart.isEmpty ? intPart : '$intPart.$fracPart';
return neg ? '-$out' : out; return neg ? '-$out' : out;
+12 -16
View File
@@ -14,13 +14,13 @@ class BalanceCardStyleSettings {
BalanceCardStyleSettings( BalanceCardStyleSettings(
{required this.walletInfoId, {required this.walletInfoId,
required this.accountIndex, required this.accountIndex,
required this.gradientIndex, required this.gradientIndex,
required this.useSpecialDesign, required this.useSpecialDesign,
required this.backgroundImagePath, required this.backgroundImagePath,
this.iconStyleIndex = 0, this.iconStyleIndex = 0,
this.isGradientOnly = false, this.isGradientOnly = false,
required this.cardOrder}); required this.cardOrder});
static const tableName = "BalanceCardStyleSettings"; static const tableName = "BalanceCardStyleSettings";
@@ -59,21 +59,17 @@ class BalanceCardStyleSettings {
int iconStyleIndex = 0, int iconStyleIndex = 0,
int? gradientIndexOverride, int? gradientIndexOverride,
}) { }) {
final int gradientIndex = gradientIndexOverride ?? final int gradientIndex =
CardDesign.allGradients.indexOf(design.gradient); gradientIndexOverride ?? CardDesign.allGradients.indexOf(design.gradient);
return BalanceCardStyleSettings( return BalanceCardStyleSettings(
walletInfoId: walletInfoId, walletInfoId: walletInfoId,
accountIndex: accountIndex, accountIndex: accountIndex,
gradientIndex: gradientIndex, gradientIndex: gradientIndex,
useSpecialDesign: useSpecialDesign: design.backgroundType == CardDesignBackgroundTypes.svgFull,
design.backgroundType == CardDesignBackgroundTypes.svgFull,
backgroundImagePath: backgroundImagePath:
design.backgroundType == CardDesignBackgroundTypes.image design.backgroundType == CardDesignBackgroundTypes.image ? design.imagePath : "",
? design.imagePath
: "",
iconStyleIndex: iconStyleIndex, iconStyleIndex: iconStyleIndex,
isGradientOnly: isGradientOnly: design.backgroundType == CardDesignBackgroundTypes.gradientOnly,
design.backgroundType == CardDesignBackgroundTypes.gradientOnly,
cardOrder: cardOrder, cardOrder: cardOrder,
); );
} }
+122 -109
View File
@@ -12,14 +12,13 @@ class CardColorCombination {
const CardColorCombination( const CardColorCombination(
{required this.textColor, {required this.textColor,
required this.textColorSecondary, required this.textColorSecondary,
required this.backgroundImageColor}); required this.backgroundImageColor});
static const light = CardColorCombination( static const light = CardColorCombination(
textColor: Color.fromARGB(200, 255, 255, 255), textColor: Color.fromARGB(200, 255, 255, 255),
textColorSecondary: Colors.white54, textColorSecondary: Colors.white54,
backgroundImageColor: Color.fromARGB(200, 0, 0, 0) backgroundImageColor: Color.fromARGB(200, 0, 0, 0));
);
static const dark = CardColorCombination( static const dark = CardColorCombination(
textColor: Color.fromARGB(200, 0, 0, 0), textColor: Color.fromARGB(200, 0, 0, 0),
@@ -50,10 +49,11 @@ class CardDesign {
const CardDesign( const CardDesign(
{this.backgroundType = CardDesignBackgroundTypes.svgIcon, {this.backgroundType = CardDesignBackgroundTypes.svgIcon,
this.gradient = const LinearGradient(colors: [Colors.black], begin: Alignment.topCenter, end: Alignment.bottomCenter), this.gradient = const LinearGradient(
this.imagePath = "assets/new-ui/blank.svg", colors: [Colors.black], begin: Alignment.topCenter, end: Alignment.bottomCenter),
this.colors = CardColorCombination.dark, this.imagePath = "assets/new-ui/blank.svg",
this.preColoredIcon = false}); this.colors = CardColorCombination.dark,
this.preColoredIcon = false});
static const LinearGradient gradientOrange = LinearGradient( static const LinearGradient gradientOrange = LinearGradient(
colors: <Color>[Color(0xFFFF7C02), Color(0xFFFF5602)], colors: <Color>[Color(0xFFFF7C02), Color(0xFFFF5602)],
@@ -129,71 +129,50 @@ class CardDesign {
static const genericDefault = CardDesign(gradient: gradientBlue); static const genericDefault = CardDesign(gradient: gradientBlue);
static const gradientOnlyDesign = CardDesign( static const gradientOnlyDesign =
backgroundType: CardDesignBackgroundTypes.gradientOnly, CardDesign(backgroundType: CardDesignBackgroundTypes.gradientOnly, gradient: gradientBlue);
gradient: gradientBlue);
static const btc = CardDesign( static const btc = CardDesign(imagePath: "assets/new-ui/balance_card_icons/bitcoin.svg");
imagePath: "assets/new-ui/balance_card_icons/bitcoin.svg");
static const eth = CardDesign( static const eth = CardDesign(imagePath: "assets/new-ui/balance_card_icons/ethereum.svg");
imagePath: "assets/new-ui/balance_card_icons/ethereum.svg");
static const btcln = CardDesign( static const btcln = CardDesign(imagePath: "assets/new-ui/balance_card_icons/lightning.svg");
imagePath: "assets/new-ui/balance_card_icons/lightning.svg");
static const xmr = CardDesign( static const xmr = CardDesign(imagePath: "assets/new-ui/balance_card_icons/monero.svg");
imagePath: "assets/new-ui/balance_card_icons/monero.svg");
static const ltc = CardDesign( static const ltc = CardDesign(imagePath: "assets/new-ui/balance_card_icons/litecoin.svg");
imagePath: "assets/new-ui/balance_card_icons/litecoin.svg");
static const bch = CardDesign( static const bch = CardDesign(imagePath: "assets/new-ui/balance_card_icons/bitcoin_cash.svg");
imagePath: "assets/new-ui/balance_card_icons/bitcoin_cash.svg");
static const doge = CardDesign( static const doge = CardDesign(imagePath: "assets/new-ui/balance_card_icons/dogecoin.svg");
imagePath: "assets/new-ui/balance_card_icons/dogecoin.svg");
static const base = CardDesign( static const base = CardDesign(imagePath: "assets/new-ui/balance_card_icons/base.svg");
imagePath: "assets/new-ui/balance_card_icons/base.svg");
static const pol = CardDesign( static const pol = CardDesign(imagePath: "assets/new-ui/balance_card_icons/polygon.svg");
imagePath: "assets/new-ui/balance_card_icons/polygon.svg");
static const sol = CardDesign( static const sol = CardDesign(imagePath: "assets/new-ui/balance_card_icons/solana.svg");
imagePath: "assets/new-ui/balance_card_icons/solana.svg");
static const tron = CardDesign( static const tron = CardDesign(imagePath: "assets/new-ui/balance_card_icons/tron.svg");
imagePath: "assets/new-ui/balance_card_icons/tron.svg");
static const nano = CardDesign( static const nano = CardDesign(imagePath: "assets/new-ui/balance_card_icons/nano.svg");
imagePath: "assets/new-ui/balance_card_icons/nano.svg");
static const zano = CardDesign( static const zano = CardDesign(imagePath: "assets/new-ui/balance_card_icons/zano.svg");
imagePath: "assets/new-ui/balance_card_icons/zano.svg");
static const wow = CardDesign( static const wow = CardDesign(imagePath: "assets/new-ui/balance_card_icons/wownero.svg");
imagePath: "assets/new-ui/balance_card_icons/wownero.svg");
static const dcr = CardDesign( static const dcr = CardDesign(imagePath: "assets/new-ui/balance_card_icons/decred.svg");
imagePath: "assets/new-ui/balance_card_icons/decred.svg");
static const arbitrum = CardDesign( static const arbitrum = CardDesign(imagePath: "assets/new-ui/balance_card_icons/arbitrum.svg");
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 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( static const ethSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF6259FF), Color(0xFF3B20E6)], colors: <Color>[Color(0xFF6259FF), Color(0xFF3B20E6)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
colors: CardColorCombination.light, colors: CardColorCombination.light,
imagePath: "assets/new-ui/balance_card_backgrounds/ethereum.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/ethereum.svg");
@@ -201,24 +180,24 @@ class CardDesign {
static const btcSpecial = CardDesign( static const btcSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFFFFBF00), Color(0xFFFF6A00)], colors: <Color>[Color(0xFFFFBF00), Color(0xFFFF6A00)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/bitcoin.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/bitcoin.svg");
static const xmrSpecial = CardDesign( static const xmrSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFFFF5900), Color(0xFFE62E00)], colors: <Color>[Color(0xFFFF5900), Color(0xFFE62E00)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/monero.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/monero.svg");
static const ltcSpecial = CardDesign( static const ltcSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF2145BF), Color(0xFF072071)], colors: <Color>[Color(0xFF2145BF), Color(0xFF072071)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
colors: CardColorCombination.light, colors: CardColorCombination.light,
imagePath: "assets/new-ui/balance_card_backgrounds/litecoin.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/litecoin.svg");
@@ -226,8 +205,8 @@ class CardDesign {
static const lnSpecial = CardDesign( static const lnSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFFFFBF00), Color(0xFFFF6A00)], colors: <Color>[Color(0xFFFFBF00), Color(0xFFFF6A00)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
colors: CardColorCombination.dark, colors: CardColorCombination.dark,
imagePath: "assets/new-ui/balance_card_backgrounds/lightning.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/lightning.svg");
@@ -235,8 +214,8 @@ class CardDesign {
static const tronSpecial = CardDesign( static const tronSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFFFF1313), Color(0xFFB40000)], colors: <Color>[Color(0xFFFF1313), Color(0xFFB40000)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
colors: CardColorCombination.light, colors: CardColorCombination.light,
imagePath: "assets/new-ui/balance_card_backgrounds/tron.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/tron.svg");
@@ -244,8 +223,8 @@ class CardDesign {
static const solSpecial = CardDesign( static const solSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF4701AA), Color(0xFF19004B)], colors: <Color>[Color(0xFF4701AA), Color(0xFF19004B)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
colors: CardColorCombination.light, colors: CardColorCombination.light,
imagePath: "assets/new-ui/balance_card_backgrounds/solana.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/solana.svg");
@@ -253,40 +232,40 @@ class CardDesign {
static const bchSpecial = CardDesign( static const bchSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF36DA4C), Color(0xFF008D57)], colors: <Color>[Color(0xFF36DA4C), Color(0xFF008D57)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/bitcoin-cash.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/bitcoin-cash.svg");
static const wowSpecial = CardDesign( static const wowSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFFFF6CD3), Color(0xFFF200A9)], colors: <Color>[Color(0xFFFF6CD3), Color(0xFFF200A9)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/wownero.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/wownero.svg");
static const dogeSpecial = CardDesign( static const dogeSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFFCBA818), Color(0xFF885D00)], colors: <Color>[Color(0xFFCBA818), Color(0xFF885D00)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/dogecoin.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/dogecoin.svg");
static const nanoSpecial = CardDesign( static const nanoSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF209CE9), Color(0xFF0073CB)], colors: <Color>[Color(0xFF209CE9), Color(0xFF0073CB)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/nano.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/nano.svg");
static const polSpecial = CardDesign( static const polSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF863FE3), Color(0xFF59098E)], colors: <Color>[Color(0xFF863FE3), Color(0xFF59098E)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
colors: CardColorCombination.light, colors: CardColorCombination.light,
imagePath: "assets/new-ui/balance_card_backgrounds/polygon.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/polygon.svg");
@@ -294,8 +273,8 @@ class CardDesign {
static const dcrSpecial = CardDesign( static const dcrSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF2871FF), Color(0xFF0057FF)], colors: <Color>[Color(0xFF2871FF), Color(0xFF0057FF)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
colors: CardColorCombination.light, colors: CardColorCombination.light,
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/decred.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/decred.svg");
@@ -303,8 +282,8 @@ class CardDesign {
static const zanoSpecial = CardDesign( static const zanoSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF0004B4), Color(0xFF170069)], colors: <Color>[Color(0xFF0004B4), Color(0xFF170069)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
colors: CardColorCombination.black, colors: CardColorCombination.black,
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/zano.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/zano.svg");
@@ -312,8 +291,8 @@ class CardDesign {
static const baseSpecial = CardDesign( static const baseSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF003CFF), Color(0xFF000087)], colors: <Color>[Color(0xFF003CFF), Color(0xFF000087)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
colors: CardColorCombination.light, colors: CardColorCombination.light,
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/base.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/base.svg");
@@ -321,8 +300,8 @@ class CardDesign {
static const arbitrumSpecial = CardDesign( static const arbitrumSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF173F76), Color(0xFF031A39)], colors: <Color>[Color(0xFF173F76), Color(0xFF031A39)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
colors: CardColorCombination.light, colors: CardColorCombination.light,
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/arbitrum.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/arbitrum.svg");
@@ -330,16 +309,16 @@ class CardDesign {
static const zecSpecial = CardDesign( static const zecSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFFF6C527), Color(0xFFCD8C00)], colors: <Color>[Color(0xFFF6C527), Color(0xFFCD8C00)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/zcash.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/zcash.svg");
static const bnbSpecial = CardDesign( static const bnbSpecial = CardDesign(
gradient: const LinearGradient( gradient: const LinearGradient(
colors: <Color>[Color(0xFF603000), Color(0xFFF0B90B)], colors: <Color>[Color(0xFF603000), Color(0xFFF0B90B)],
begin: Alignment.topCenter, end: Alignment.bottomCenter begin: Alignment.topCenter,
), end: Alignment.bottomCenter),
colors: CardColorCombination.light, colors: CardColorCombination.light,
backgroundType: CardDesignBackgroundTypes.svgFull, backgroundType: CardDesignBackgroundTypes.svgFull,
imagePath: "assets/new-ui/balance_card_backgrounds/bnb.svg"); imagePath: "assets/new-ui/balance_card_backgrounds/bnb.svg");
@@ -351,12 +330,14 @@ class CardDesign {
backgroundType: backgroundType, backgroundType: backgroundType,
preColoredIcon: preColoredIcon); preColoredIcon: preColoredIcon);
CardDesign withGradientAndColorCombination(Gradient gradient, CardColorCombination cardColorCombination) => CardDesign( CardDesign withGradientAndColorCombination(
gradient: gradient, Gradient gradient, CardColorCombination cardColorCombination) =>
colors: cardColorCombination, CardDesign(
imagePath: imagePath, gradient: gradient,
backgroundType: backgroundType, colors: cardColorCombination,
preColoredIcon: preColoredIcon); imagePath: imagePath,
backgroundType: backgroundType,
preColoredIcon: preColoredIcon);
CardDesign withIcon(CardIconPath icon) => CardDesign( CardDesign withIcon(CardIconPath icon) => CardDesign(
gradient: gradient, gradient: gradient,
@@ -374,8 +355,10 @@ class CardDesign {
static const Map<CryptoCurrency, _CurrencyIconNames> _iconNames = { static const Map<CryptoCurrency, _CurrencyIconNames> _iconNames = {
CryptoCurrency.arbEth: _CurrencyIconNames(ticker: 'arb', longName: 'arbitrum'), CryptoCurrency.arbEth: _CurrencyIconNames(ticker: 'arb', longName: 'arbitrum'),
CryptoCurrency.baseEth: _CurrencyIconNames(ticker: 'base', longName: 'base', chainFile: 'base_icon'), CryptoCurrency.baseEth:
CryptoCurrency.bch: _CurrencyIconNames(ticker: 'bch', longName: 'bitcoin_cash', chainFile: 'bitcoin-cash'), _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.btc: _CurrencyIconNames(ticker: 'btc', longName: 'bitcoin', outlineFile: 'BTC'),
CryptoCurrency.bnb: _CurrencyIconNames(ticker: 'bnb', longName: 'bnb'), CryptoCurrency.bnb: _CurrencyIconNames(ticker: 'bnb', longName: 'bnb'),
CryptoCurrency.dcr: _CurrencyIconNames(ticker: 'dcr', longName: 'decred'), CryptoCurrency.dcr: _CurrencyIconNames(ticker: 'dcr', longName: 'decred'),
@@ -383,13 +366,13 @@ class CardDesign {
CryptoCurrency.eth: _CurrencyIconNames(ticker: 'eth', longName: 'ethereum'), CryptoCurrency.eth: _CurrencyIconNames(ticker: 'eth', longName: 'ethereum'),
CryptoCurrency.btcln: _CurrencyIconNames(ticker: 'ln', longName: 'lightning'), CryptoCurrency.btcln: _CurrencyIconNames(ticker: 'ln', longName: 'lightning'),
CryptoCurrency.ltc: _CurrencyIconNames(ticker: 'ltc', longName: 'litecoin'), CryptoCurrency.ltc: _CurrencyIconNames(ticker: 'ltc', longName: 'litecoin'),
CryptoCurrency.xmr: _CurrencyIconNames( CryptoCurrency.xmr:
ticker: 'xmr', longName: 'monero', ogPath: 'assets/images/xmr-og.webp'), _CurrencyIconNames(ticker: 'xmr', longName: 'monero', ogPath: 'assets/images/xmr-og.webp'),
CryptoCurrency.nano: _CurrencyIconNames(ticker: 'xno', longName: 'nano'), CryptoCurrency.nano: _CurrencyIconNames(ticker: 'xno', longName: 'nano'),
CryptoCurrency.maticpoly: _CurrencyIconNames(ticker: 'pol', longName: 'polygon'), CryptoCurrency.maticpoly: _CurrencyIconNames(ticker: 'pol', longName: 'polygon'),
CryptoCurrency.sol: _CurrencyIconNames(ticker: 'sol', longName: 'solana'), CryptoCurrency.sol: _CurrencyIconNames(ticker: 'sol', longName: 'solana'),
CryptoCurrency.trx: _CurrencyIconNames( CryptoCurrency.trx:
ticker: 'trx', longName: 'tron', ogPath: '$_ogIconPrefix/tron-og.svg'), _CurrencyIconNames(ticker: 'trx', longName: 'tron', ogPath: '$_ogIconPrefix/tron-og.svg'),
CryptoCurrency.zano: _CurrencyIconNames(ticker: 'zano', longName: 'zano'), CryptoCurrency.zano: _CurrencyIconNames(ticker: 'zano', longName: 'zano'),
CryptoCurrency.zec: _CurrencyIconNames(ticker: 'zec', longName: 'zcash'), 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) { static CardDesign forCurrencySpecial(CryptoCurrency currency) {
return specialDesignsForCurrencies[currency] ?? genericDefault; return specialDesignsForCurrencies[currency] ?? genericDefault;
@@ -437,7 +453,6 @@ class CardDesign {
CryptoCurrency.zec: zec, CryptoCurrency.zec: zec,
CryptoCurrency.dcr: dcr, CryptoCurrency.dcr: dcr,
CryptoCurrency.bnb: bnb, CryptoCurrency.bnb: bnb,
}; };
static const Map<CryptoCurrency, CardDesign> specialDesignsForCurrencies = { static const Map<CryptoCurrency, CardDesign> specialDesignsForCurrencies = {
@@ -504,9 +519,9 @@ class CardDesign {
if (setting.isGradientOnly) { if (setting.isGradientOnly) {
final gradient = gradientForStoredIndex(setting.gradientIndex, walletCurrency); final gradient = gradientForStoredIndex(setting.gradientIndex, walletCurrency);
final textColors = preferredColorCombinations[gradient] final textColors = preferredColorCombinations[gradient] ??
?? specialDesignsForCurrencies[walletCurrency]?.colors specialDesignsForCurrencies[walletCurrency]?.colors ??
?? gradientOnlyDesign.colors; gradientOnlyDesign.colors;
return gradientOnlyDesign.withGradientAndColorCombination(gradient, textColors); return gradientOnlyDesign.withGradientAndColorCombination(gradient, textColors);
} }
@@ -521,11 +536,9 @@ class CardDesign {
} }
if (!setting.useSpecialDesign && setting.gradientIndex == -1) { if (!setting.useSpecialDesign && setting.gradientIndex == -1) {
final specialColors = final specialColors = specialDesignsForCurrencies[walletCurrency] ?? genericDefault;
specialDesignsForCurrencies[walletCurrency] ?? genericDefault;
final design = CardDesign.forCurrencyIcon(walletCurrency) final design = CardDesign.forCurrencyIcon(walletCurrency)
.withGradientAndColorCombination( .withGradientAndColorCombination(specialColors.gradient, specialColors.colors);
specialColors.gradient, specialColors.colors);
return design.withIconStyleIndex(setting.iconStyleIndex, walletCurrency); return design.withIconStyleIndex(setting.iconStyleIndex, walletCurrency);
} }
if (setting.useSpecialDesign) { if (setting.useSpecialDesign) {
@@ -534,9 +547,9 @@ class CardDesign {
if (setting.gradientIndex != -1) { if (setting.gradientIndex != -1) {
final baseIcon = CardDesign.forCurrencyIcon(walletCurrency); final baseIcon = CardDesign.forCurrencyIcon(walletCurrency);
final gradient = gradientForStoredIndex(setting.gradientIndex, walletCurrency); final gradient = gradientForStoredIndex(setting.gradientIndex, walletCurrency);
final textColors = preferredColorCombinations[gradient] final textColors = preferredColorCombinations[gradient] ??
?? specialDesignsForCurrencies[walletCurrency]?.colors specialDesignsForCurrencies[walletCurrency]?.colors ??
?? baseIcon.colors; baseIcon.colors;
return baseIcon return baseIcon
.withGradientAndColorCombination(gradient, textColors) .withGradientAndColorCombination(gradient, textColors)
.withIconStyleIndex(setting.iconStyleIndex, walletCurrency); .withIconStyleIndex(setting.iconStyleIndex, walletCurrency);
-1
View File
@@ -37,7 +37,6 @@ extension MaxDecimals on String {
return parts.join("."); return parts.join(".");
} }
/// Format a stringified number to a localized representation /// Format a stringified number to a localized representation
/// 1.000.000,00 in de_DE /// 1.000.000,00 in de_DE
/// 1’000’000.00 in de_CH /// 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) { if (type == WalletType.arbitrum) {
return CryptoCurrency.arb; 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 dbFile = File("${(await getAppDir()).path}/cake.db");
final jsonText = JsonEncoder.withIndent(" ").convert(await dumpCustomDb(dbFile.path)); final jsonText = JsonEncoder.withIndent(" ").convert(await dumpCustomDb(dbFile.path));
setState(() { setState(() {
log = jsonText; log = jsonText;
}); });
} catch (e, s) { } catch (e, s) {
setState(() { setState(() {
+25 -26
View File
@@ -3,40 +3,39 @@ import 'dart:typed_data';
import 'package:cw_core/utils/file.dart' as file; import 'package:cw_core/utils/file.dart' as file;
import 'package:cake_backup/backup.dart' as cwb; import 'package:cake_backup/backup.dart' as cwb;
EncryptionFileUtils encryptionFileUtilsFor(bool direct) EncryptionFileUtils encryptionFileUtilsFor(bool direct) =>
=> direct direct ? XChaCha20EncryptionFileUtils() : Salsa20EncryhptionFileUtils();
? XChaCha20EncryptionFileUtils()
: Salsa20EncryhptionFileUtils();
abstract class EncryptionFileUtils { abstract class EncryptionFileUtils {
Future<void> write({required String path, required String password, required String data}); Future<void> write({required String path, required String password, required String data});
Future<String> read({required String path, required String password}); Future<String> read({required String path, required String password});
} }
class Salsa20EncryhptionFileUtils extends EncryptionFileUtils { class Salsa20EncryhptionFileUtils extends EncryptionFileUtils {
// Requires legacy complex key + iv as password // Requires legacy complex key + iv as password
@override @override
Future<void> write({required String path, required String password, required String data}) async Future<void> write(
=> await file.write(path: path, password: password, data: data); {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 // Requires legacy complex key + iv as password
@override @override
Future<String> read({required String path, required String password}) async Future<String> read({required String path, required String password}) async =>
=> await file.read(path: path, password: password); await file.read(path: path, password: password);
} }
class XChaCha20EncryptionFileUtils extends EncryptionFileUtils { class XChaCha20EncryptionFileUtils extends EncryptionFileUtils {
@override @override
Future<void> write({required String path, required String password, required String data}) async { Future<void> write({required String path, required String password, required String data}) async {
final encrypted = await cwb.encrypt(password, Uint8List.fromList(data.codeUnits)); final encrypted = await cwb.encrypt(password, Uint8List.fromList(data.codeUnits));
await File(path).writeAsBytes(encrypted); await File(path).writeAsBytes(encrypted);
} }
@override @override
Future<String> read({required String path, required String password}) async { Future<String> read({required String path, required String password}) async {
final file = File(path); final file = File(path);
final encrypted = await file.readAsBytes(); final encrypted = await file.readAsBytes();
final bytes = await cwb.decrypt(password, encrypted); final bytes = await cwb.decrypt(password, encrypted);
return String.fromCharCodes(bytes); 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'; part of 'erc20_token.dart';
@@ -55,7 +55,5 @@ class Erc20TokenAdapter extends TypeAdapter<Erc20Token> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is Erc20TokenAdapter && other is Erc20TokenAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
+2 -2
View File
@@ -1,7 +1,7 @@
String formatAmount(String amount) { String formatAmount(String amount) {
if ((!amount.contains('.'))&&(!amount.contains(','))) { if ((!amount.contains('.')) && (!amount.contains(','))) {
return amount + '.00'; return amount + '.00';
} else if ((amount.endsWith('.'))||(amount.endsWith(','))) { } else if ((amount.endsWith('.')) || (amount.endsWith(','))) {
return amount + '00'; return amount + '00';
} }
return amount; return amount;
+3 -6
View File
@@ -1,7 +1,6 @@
import 'package:cw_core/parse_fixed.dart'; import 'package:cw_core/parse_fixed.dart';
String formatFixed(BigInt value, int? decimals, String formatFixed(BigInt value, int? decimals, {int? fractionalDigits, bool trimZeros = true}) {
{int? fractionalDigits, bool trimZeros = true}) {
decimals ??= 0; decimals ??= 0;
fractionalDigits ??= decimals; fractionalDigits ??= decimals;
@@ -10,10 +9,8 @@ String formatFixed(BigInt value, int? decimals,
var negative = value.isNegative; var negative = value.isNegative;
if (negative) value = value * BigInt.from(-1); if (negative) value = value * BigInt.from(-1);
var fraction = value var fraction =
.modPow(BigInt.one, BigInt.parse(multiplier)) value.modPow(BigInt.one, BigInt.parse(multiplier)).toString().padLeft(decimals, "0");
.toString()
.padLeft(decimals, "0");
if (fractionalDigits < 0) fractionalDigits = 0; if (fractionalDigits < 0) fractionalDigits = 0;
if (fractionalDigits > decimals) fractionalDigits = decimals; if (fractionalDigits > decimals) fractionalDigits = decimals;
+2 -1
View File
@@ -6,7 +6,8 @@ import 'dart:convert';
// FIXME: Hardcoded values; Works only for monero // 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 = { const havenDates = {
"2023-05": 1352995, "2023-05": 1352995,
-1
View File
@@ -1,4 +1,3 @@
class MoneroHeight { 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 // 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 = [ static final List<_Checkpoint> _points = [
+24 -24
View File
@@ -1,24 +1,24 @@
const CONTACT_TYPE_ID = 0; const CONTACT_TYPE_ID = 0;
const NODE_TYPE_ID = 1; const NODE_TYPE_ID = 1;
const TRANSACTION_TYPE_ID = 2; const TRANSACTION_TYPE_ID = 2;
const TRADE_TYPE_ID = 3; const TRADE_TYPE_ID = 3;
const WALLET_INFO_TYPE_ID = 4; const WALLET_INFO_TYPE_ID = 4;
const WALLET_TYPE_TYPE_ID = 5; const WALLET_TYPE_TYPE_ID = 5;
const TEMPLATE_TYPE_ID = 6; const TEMPLATE_TYPE_ID = 6;
const EXCHANGE_TEMPLATE_TYPE_ID = 7; const EXCHANGE_TEMPLATE_TYPE_ID = 7;
const ORDER_TYPE_ID = 8; const ORDER_TYPE_ID = 8;
const UNSPENT_COINS_INFO_TYPE_ID = 9; const UNSPENT_COINS_INFO_TYPE_ID = 9;
const ANONPAY_INVOICE_INFO_TYPE_ID = 10; const ANONPAY_INVOICE_INFO_TYPE_ID = 10;
const ADDRESS_INFO_TYPE_ID = 11; const ADDRESS_INFO_TYPE_ID = 11;
const ERC20_TOKEN_TYPE_ID = 12; const ERC20_TOKEN_TYPE_ID = 12;
const NANO_ACCOUNT_TYPE_ID = 13; const NANO_ACCOUNT_TYPE_ID = 13;
const POW_NODE_TYPE_ID = 14; const POW_NODE_TYPE_ID = 14;
const DERIVATION_TYPE_TYPE_ID = 15; const DERIVATION_TYPE_TYPE_ID = 15;
const SPL_TOKEN_TYPE_ID = 16; const SPL_TOKEN_TYPE_ID = 16;
const DERIVATION_INFO_TYPE_ID = 17; const DERIVATION_INFO_TYPE_ID = 17;
const TRON_TOKEN_TYPE_ID = 18; const TRON_TOKEN_TYPE_ID = 18;
const HARDWARE_WALLET_TYPE_TYPE_ID = 19; const HARDWARE_WALLET_TYPE_TYPE_ID = 19;
const MWEB_UTXO_TYPE_ID = 20; const MWEB_UTXO_TYPE_ID = 20;
const HAVEN_SEED_STORE_TYPE_ID = 21; const HAVEN_SEED_STORE_TYPE_ID = 21;
const ZANO_ASSET_TYPE_ID = 22; const ZANO_ASSET_TYPE_ID = 22;
const PAYJOIN_SESSION_TYPE_ID = 23; const PAYJOIN_SESSION_TYPE_ID = 23;
+2 -1
View File
@@ -16,7 +16,8 @@ List<String> extractKeys(String key) {
return [_key, iv]; 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 encrypter = encrypt.Encrypter(encrypt.Salsa20(key));
final encrypted = encrypter.encrypt(data, iv: iv); final encrypted = encrypter.encrypt(data, iv: iv);
+6 -9
View File
@@ -12,12 +12,11 @@ bool isBolt11ZeroInvoice(String invoice) {
try { try {
final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length); final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
final prefix =
final prefix = _BOLT_PREFIXES _BOLT_PREFIXES.firstWhere((prefix) => request.hrp.startsWith(prefix), orElse: () => "");
.firstWhere((prefix) => request.hrp.startsWith(prefix), orElse: () => "");
return request.hrp.length == prefix.length; return request.hrp.length == prefix.length;
} catch(e) { } catch (e) {
return false; return false;
} }
} }
@@ -27,7 +26,7 @@ int? _getAmountBolt11Msat(String invoice) {
final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length); final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
final prefix = _BOLT_PREFIXES.firstWhere( final prefix = _BOLT_PREFIXES.firstWhere(
(prefix) => request.hrp.startsWith(prefix), (prefix) => request.hrp.startsWith(prefix),
orElse: () => throw FormatException('Invalid BOLT11 invoice: unknown HRP prefix.'), orElse: () => throw FormatException('Invalid BOLT11 invoice: unknown HRP prefix.'),
); );
@@ -86,8 +85,7 @@ class LNURL {
final maxSendable = body["maxSendable"] as int?; final maxSendable = body["maxSendable"] as int?;
// if minSendable and maxSendable are the same we assume a specific payment request // if minSendable and maxSendable are the same we assume a specific payment request
if (msat != maxSendable) if (msat != maxSendable) return null;
return null;
if (msat == null || msat % 1000 != 0) return null; if (msat == null || msat % 1000 != 0) return null;
return Money.fromInt(msat ~/ 1000, CryptoCurrency.btcln); return Money.fromInt(msat ~/ 1000, CryptoCurrency.btcln);
@@ -136,8 +134,7 @@ class LNURL {
String _findLnUrl(String input) { String _findLnUrl(String input) {
final res = RegExp(r',*?((lnurl)([0-9]+[a-z0-9]+))').allMatches(input.toLowerCase()); final res = RegExp(r',*?((lnurl)([0-9]+[a-z0-9]+))').allMatches(input.toLowerCase());
if (res.length != 1) if (res.length != 1) throw ArgumentError('Not a valid lnurl string');
throw ArgumentError('Not a valid lnurl string');
return res.first.group(0)!; return res.first.group(0)!;
} }
+5 -5
View File
@@ -1,11 +1,11 @@
class MoneroWalletKeys { class MoneroWalletKeys {
const MoneroWalletKeys( const MoneroWalletKeys(
{required this.primaryAddress, {required this.primaryAddress,
required this.privateSpendKey, required this.privateSpendKey,
required this.privateViewKey, required this.privateViewKey,
required this.publicSpendKey, required this.publicSpendKey,
required this.publicViewKey, required this.publicViewKey,
required this.passphrase}); required this.passphrase});
final String primaryAddress; final String primaryAddress;
final String publicViewKey; final String publicViewKey;
+1 -3
View File
@@ -50,7 +50,5 @@ class MwebUtxoAdapter extends TypeAdapter<MwebUtxo> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is MwebUtxoAdapter && other is MwebUtxoAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
+1 -3
View File
@@ -44,7 +44,5 @@ class NanoAccountAdapter extends TypeAdapter<NanoAccount> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is NanoAccountAdapter && other is NanoAccountAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
+30 -34
View File
@@ -25,11 +25,9 @@ Future<void> validateBuiltinNodes() async {
for (final listNode in builtinFromList) { for (final listNode in builtinFromList) {
// preserve proxy settings from user // preserve proxy settings from user
try { try {
final matchedDbNode = builtinFromDb.firstWhere( final matchedDbNode = builtinFromDb.firstWhere((dbNode) => dbNode.uri == listNode.uri);
(dbNode) => dbNode.uri == listNode.uri
);
listNode.socksProxyAddress = matchedDbNode.socksProxyAddress; listNode.socksProxyAddress = matchedDbNode.socksProxyAddress;
} catch(e) {} } catch (e) {}
} }
final dbSet = builtinFromDb.toSet(); final dbSet = builtinFromDb.toSet();
@@ -121,7 +119,7 @@ class Node {
"label": label, "label": label,
'password': password, 'password': password,
"isPow": isPow ? 1 : 0, "isPow": isPow ? 1 : 0,
'useSSL': (useSSL??false) ? 1 : 0, 'useSSL': (useSSL ?? false) ? 1 : 0,
"typeRaw": typeRaw, "typeRaw": typeRaw,
'trusted': trusted ? 1 : 0, 'trusted': trusted ? 1 : 0,
'socksProxyAddress': socksProxyAddress, 'socksProxyAddress': socksProxyAddress,
@@ -165,7 +163,6 @@ class Node {
} }
Future<int> save() async { Future<int> save() async {
final json = toMap(); final json = toMap();
if (json[selfIdColumn] == 0) { if (json[selfIdColumn] == 0) {
json[selfIdColumn] = null; json[selfIdColumn] = null;
@@ -174,9 +171,9 @@ class Node {
return id; return id;
} }
static Future<List<Node>> selectList(String where, List<dynamic> whereArgs,
static Future<List<Node>> selectList(String where, List<dynamic> whereArgs, {String? orderBy}) async { {String? orderBy}) async {
if(orderBy == null) { if (orderBy == null) {
orderBy = selfIdColumn; orderBy = selfIdColumn;
} }
final list = await db!.query( final list = await db!.query(
@@ -189,7 +186,7 @@ class Node {
} }
static Future<Node?> select(String where, List<dynamic> whereArgs, {String? orderBy}) async { static Future<Node?> select(String where, List<dynamic> whereArgs, {String? orderBy}) async {
if(orderBy == null) { if (orderBy == null) {
orderBy = selfIdColumn; orderBy = selfIdColumn;
} }
final list = await db!.query( final list = await db!.query(
@@ -201,7 +198,6 @@ class Node {
return list.isEmpty ? null : Node.fromMap(list.first); return list.isEmpty ? null : Node.fromMap(list.first);
} }
static Future<List<Node>> getAll() async { static Future<List<Node>> getAll() async {
return selectList("isPow = ?", [0]); return selectList("isPow = ?", [0]);
} }
@@ -219,11 +215,15 @@ class Node {
} }
static Future<Node?> getDefaultForWalletType(WalletType type) async { 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 { 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 { static Future<List<Node>> getAllForWalletTypePow(WalletType type) async {
@@ -238,7 +238,6 @@ class Node {
return select("${selfIdColumn} = ?", [id]); return select("${selfIdColumn} = ?", [id]);
} }
int id; int id;
late String uriRaw; late String uriRaw;
String? login; String? login;
@@ -262,7 +261,6 @@ class Node {
static String get tableName => "Node"; static String get tableName => "Node";
static String get selfIdColumn => "${tableName}Id"; static String get selfIdColumn => "${tableName}Id";
bool get isSSL => useSSL ?? false; bool get isSSL => useSSL ?? false;
bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty; bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty;
@@ -333,7 +331,6 @@ class Node {
socksProxyAddress.hashCode ^ socksProxyAddress.hashCode ^
path.hashCode; path.hashCode;
WalletType get type => deserializeFromInt(typeRaw); WalletType get type => deserializeFromInt(typeRaw);
set type(WalletType type) => typeRaw = serializeToInt(type); set type(WalletType type) => typeRaw = serializeToInt(type);
@@ -387,7 +384,6 @@ class Node {
body: jsonBody, body: jsonBody,
); );
final resBody = json.decode(response.body) as Map<String, dynamic>; final resBody = json.decode(response.body) as Map<String, dynamic>;
return resBody['result']['height'] != null; return resBody['result']['height'] != null;
@@ -407,15 +403,13 @@ class Node {
final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName}; final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName};
try { try {
final jsonBody = json.encode(body); final jsonBody = json.encode(body);
final response = await ProxyWrapper().post( final response = await ProxyWrapper().post(
clearnetUri: rpcUri, clearnetUri: rpcUri,
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: jsonBody, body: jsonBody,
allowMitmMoneroBypassSSLCheck: true allowMitmMoneroBypassSSLCheck: true);
);
// Check if we received a 401 Unauthorized response // Check if we received a 401 Unauthorized response
if (response.statusCode == 401) { if (response.statusCode == 401) {
final daemonRpc = DaemonRpc( final daemonRpc = DaemonRpc(
@@ -494,7 +488,6 @@ class Node {
final ProxySocket socket; final ProxySocket socket;
socket = await ProxyWrapper().getSocksSocket(useSSL ?? false, uri.host, uri.port); socket = await ProxyWrapper().getSocksSocket(useSSL ?? false, uri.host, uri.port);
socket.destroy(); socket.destroy();
return true; return true;
} catch (_) { } catch (_) {
@@ -531,9 +524,12 @@ class Node {
Future<bool> requestEthereumServer() async { Future<bool> requestEthereumServer() async {
try { try {
final req = await ProxyWrapper().getHttpClient() final req = await ProxyWrapper()
.getUrl(uri,) .getHttpClient()
.timeout(Duration(seconds: 15)); .getUrl(
uri,
)
.timeout(Duration(seconds: 15));
final response = await req.close(); final response = await req.close();
return response.statusCode >= 200 && response.statusCode < 300; return response.statusCode >= 200 && response.statusCode < 300;
@@ -544,13 +540,13 @@ class Node {
} }
Future<bool> requestDecredNode() async { Future<bool> requestDecredNode() async {
if (uri.host == "default-spv-nodes") { if (uri.host == "default-spv-nodes") {
// Just show default port as ok. The wallet will connect to a list of known // Just show default port as ok. The wallet will connect to a list of known
// nodes automatically. // nodes automatically.
return true; return true;
} }
try { try {
final socket = await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5)); final socket = await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5));
socket.destroy(); socket.destroy();
return true; return true;
} catch (_) { } catch (_) {
+17 -27
View File
@@ -19,17 +19,15 @@ import 'node.dart' as node_new;
part 'node_legacy.part.dart'; part 'node_legacy.part.dart';
Future<void> performNodeHiveMigration() async { Future<void> performNodeHiveMigration() async {
if(!CakeHive.isAdapterRegistered(Node.typeId)) { if (!CakeHive.isAdapterRegistered(Node.typeId)) {
CakeHive.registerAdapter(NodeAdapter()); CakeHive.registerAdapter(NodeAdapter());
} }
final nodeBox = await CakeHive.openBox<Node>(Node.boxName); 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 builtinNodes = await loadAllDefaultNodes();
final builtinPowNodes = await loadDefaultNanoPowNodes(); final builtinPowNodes = await loadDefaultNanoPowNodes();
await Node.migrateAllToSqlite(nodeBox, powNodeBox, builtinNodes, builtinPowNodes); await Node.migrateAllToSqlite(nodeBox, powNodeBox, builtinNodes, builtinPowNodes);
} }
Uri createUriFromElectrumAddress(String address, String path) => Uri createUriFromElectrumAddress(String address, String path) =>
@@ -62,31 +60,26 @@ class Node extends HiveObject with Keyable {
final list = nodeBox.values.toList(); final list = nodeBox.values.toList();
final powList = powNodeBox.values.toList(); final powList = powNodeBox.values.toList();
for (final node in list) { 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. // default nodes will be added by validateBuiltinNodes(), no need to migrate them.
await node.delete(); await node.delete();
continue; continue;
} }
await node.migrateToSqlite( await node.migrateToSqlite(isPow: false, isBuiltin: false, isOfficial: false);
isPow: false,
isBuiltin: false,
isOfficial: false);
await node.delete(); await node.delete();
} }
for (final node in powList) { for (final node in powList) {
if(defaultPowNodes.any((item)=>item.uri == node.uri)) { if (defaultPowNodes.any((item) => item.uri == node.uri)) {
await node.delete(); await node.delete();
continue; continue;
} }
await node.migrateToSqlite( await node.migrateToSqlite(isPow: true, isBuiltin: false, isOfficial: false);
isPow: true,
isBuiltin: false,
isOfficial: false);
await node.delete(); 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( final newNode = node_new.Node(
id: key as int, id: key as int,
login: login, login: login,
@@ -106,7 +99,6 @@ class Node extends HiveObject with Keyable {
await newNode.save(); await newNode.save();
} }
Node.fromMap(Map<String, Object?> map) Node.fromMap(Map<String, Object?> map)
: uriRaw = map['uri'] as String? ?? '', : uriRaw = map['uri'] as String? ?? '',
path = map['path'] as String? ?? '', path = map['path'] as String? ?? '',
@@ -208,15 +200,15 @@ class Node extends HiveObject with Keyable {
@override @override
bool operator ==(other) => bool operator ==(other) =>
other is Node && other is Node &&
(other.uriRaw == uriRaw && (other.uriRaw == uriRaw &&
other.login == login && other.login == login &&
other.label == label && other.label == label &&
other.password == password && other.password == password &&
other.typeRaw == typeRaw && other.typeRaw == typeRaw &&
other.useSSL == useSSL && other.useSSL == useSSL &&
other.trusted == trusted && other.trusted == trusted &&
other.socksProxyAddress == socksProxyAddress && other.socksProxyAddress == socksProxyAddress &&
other.path == path); other.path == path);
@override @override
int get hashCode => int get hashCode =>
@@ -241,6 +233,4 @@ class Node extends HiveObject with Keyable {
set type(WalletType type) => typeRaw = serializeToInt(type); set type(WalletType type) => typeRaw = serializeToInt(type);
dynamic _keyIndex; dynamic _keyIndex;
} }
+2 -5
View File
@@ -24,8 +24,7 @@ class NodeAdapter extends TypeAdapter<Node> {
trusted: fields[5] == null ? false : fields[5] as bool, trusted: fields[5] == null ? false : fields[5] as bool,
socksProxyAddress: fields[6] as String?, socksProxyAddress: fields[6] as String?,
path: fields[7] == null ? '' : fields[7] as String?, path: fields[7] == null ? '' : fields[7] as String?,
isEnabledForAutoSwitching: isEnabledForAutoSwitching: fields[11] == null ? false : fields[11] as bool,
fields[11] == null ? false : fields[11] as bool,
) )
..uriRaw = fields[0] == null ? '' : fields[0] as String ..uriRaw = fields[0] == null ? '' : fields[0] as String
..typeRaw = fields[3] == null ? 0 : fields[3] as int ..typeRaw = fields[3] == null ? 0 : fields[3] as int
@@ -72,7 +71,5 @@ class NodeAdapter extends TypeAdapter<Node> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is NodeAdapter && other is NodeAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
+8 -13
View File
@@ -18,7 +18,7 @@ Future<List<Node>> loadDefaultNodes(WalletType type) async {
case WalletType.haven: case WalletType.haven:
path = 'assets/haven_node_list.yml'; path = 'assets/haven_node_list.yml';
break; 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: case WalletType.ethereum:
path = 'assets/ethereum_server_list.yml'; path = 'assets/ethereum_server_list.yml';
break; break;
@@ -147,7 +147,6 @@ Future<List<Node>> loadAllDefaultNodes() async {
} }
Future<void> resetToDefault() async { Future<void> resetToDefault() async {
final nodes = await loadAllDefaultNodes(); final nodes = await loadAllDefaultNodes();
final currentNodes = await Node.getAll(); final currentNodes = await Node.getAll();
@@ -157,15 +156,14 @@ Future<void> resetToDefault() async {
for (final node in nodes) { for (final node in nodes) {
try { try {
final curr = currentNodes.firstWhere( 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; node.id = curr.id;
} catch(e) {} } catch (e) {}
} }
await Node.deleteAll(); await Node.deleteAll();
for(final node in nodes) { for (final node in nodes) {
await node.save(); await node.save();
} }
} }
@@ -173,7 +171,7 @@ Future<void> resetToDefault() async {
Future<void> resetPowToDefault() async { Future<void> resetPowToDefault() async {
final nanoPowNodes = await loadDefaultNanoPowNodes(); final nanoPowNodes = await loadDefaultNanoPowNodes();
await Node.deleteAllPow(); await Node.deleteAllPow();
for(final node in nanoPowNodes) { for (final node in nanoPowNodes) {
node.save(); node.save();
} }
} }
@@ -181,12 +179,9 @@ Future<void> resetPowToDefault() async {
Future<Node> getDefaultNodeFromFiles(WalletType type, {bool isPow = false}) async { Future<Node> getDefaultNodeFromFiles(WalletType type, {bool isPow = false}) async {
final nodes = isPow ? await loadDefaultNanoPowNodes() : await loadDefaultNodes(type); final nodes = isPow ? await loadDefaultNanoPowNodes() : await loadDefaultNodes(type);
try { try {
return nodes.firstWhere((item)=>item.isDefault); return nodes.firstWhere((item) => item.isDefault);
} catch(e) { } catch (e) {
// ideally the yamls would always have a default, but this is better than nothing // ideally the yamls would always have a default, but this is better than nothing
return nodes.first; return nodes.first;
} }
} }
+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"); var fraction = (comps.length == 2 ? comps[1] : "0").padRight(decimals, "0");
if (fraction.length > multiplier.length - 1) { 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); 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 => Future<String> pathForWallet({required String name, required WalletType type}) async =>
await pathForWalletDir(name: name, type: type) await pathForWalletDir(name: name, type: type).then((path) => path + '/$name');
.then((path) => path + '/$name');
Future<String> outdatedAndroidPathForWalletDir({required String name}) async { Future<String> outdatedAndroidPathForWalletDir({required String name}) async {
final directory = await getAppDir(); final directory = await getAppDir();
-1
View File
@@ -56,7 +56,6 @@ class PayjoinSession extends HiveObject {
BigInt get amount => BigInt.parse(rawAmount ?? "0"); BigInt get amount => BigInt.parse(rawAmount ?? "0");
set amount(BigInt amount) => rawAmount = amount.toString(); set amount(BigInt amount) => rawAmount = amount.toString();
} }
enum PayjoinSessionStatus { enum PayjoinSessionStatus {
+1 -3
View File
@@ -59,7 +59,5 @@ class PayjoinSessionAdapter extends TypeAdapter<PayjoinSession> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is PayjoinSessionAdapter && other is PayjoinSessionAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
+3 -4
View File
@@ -408,14 +408,14 @@ class ERC681URI extends PaymentURI {
static int _getChainID(String path) { static int _getChainID(String path) {
return int.parse(RegExp( return int.parse(RegExp(
r'@\d*', r'@\d*',
).firstMatch(path)?.group(0)?.replaceAll("@", "") ?? ).firstMatch(path)?.group(0)?.replaceAll("@", "") ??
"1"); "1");
} }
static (bool, String) _getTargetAddress(String path) { static (bool, String) _getTargetAddress(String path) {
final targetAddress = 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); return (path.contains("/"), targetAddress);
} }
@@ -508,4 +508,3 @@ class LightningPaymentRequest extends PaymentURI {
@override @override
String toString() => bolt11Invoice ?? "lightning:$lnURL"; String toString() => bolt11Invoice ?? "lightning:$lnURL";
} }
+13 -9
View File
@@ -23,8 +23,7 @@ bool get isNonAmnesticTails {
bool showNotice = true; bool showNotice = true;
void setRootDirFromEnv() => void setRootDirFromEnv() => _rootDirPath = Platform.environment['CAKE_WALLET_DIR'];
_rootDirPath = Platform.environment['CAKE_WALLET_DIR'];
void copyDirectory(Directory source, Directory destination) { void copyDirectory(Directory source, Directory destination) {
source.listSync(recursive: false).forEach((var entity) { source.listSync(recursive: false).forEach((var entity) {
@@ -41,7 +40,8 @@ void copyDirectory(Directory source, Directory destination) {
Future<void> linuxSymlinkSharedPreferences() async { Future<void> linuxSymlinkSharedPreferences() async {
if (!Platform.isLinux) return; // nuh-uh 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']; var cakeNames = ['com.example.cake_wallet', 'cake_wallet'];
for (String name in cakeNames) { for (String name in cakeNames) {
final oldPath = p.join(dataHome, name); final oldPath = p.join(dataHome, name);
@@ -54,7 +54,7 @@ Future<void> linuxSymlinkSharedPreferences() async {
printV("not creating, link exists"); printV("not creating, link exists");
} else { } else {
if (newDir.existsSync()) { if (newDir.existsSync()) {
newDir.renameSync("${newPath}_${DateTime.now().millisecondsSinceEpoch~/1000}"); newDir.renameSync("${newPath}_${DateTime.now().millisecondsSinceEpoch ~/ 1000}");
} }
copyDirectory(oldDir, newDir); copyDirectory(oldDir, newDir);
oldDir.deleteSync(recursive: true); oldDir.deleteSync(recursive: true);
@@ -94,9 +94,11 @@ Future<Directory> getAppDir() async {
// for storing things that users in general back-up // for storing things that users in general back-up
var linuxAppPath = [ var linuxAppPath = [
if (appDirPath != null) appDirPath, // old preferred if (appDirPath != null) appDirPath, // old preferred
p.join('/home', Platform.environment['USER']??"null", appName), // old fallback 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)
if (Platform.environment['HOME'] != null) p.join(Platform.environment['HOME']!, '.config', appName), // old fallback but using HOME 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) if (isNonAmnesticTails) p.join(_tailsData, ".$appName") // tails (if persistance is enabled)
]; ];
@@ -109,8 +111,10 @@ Future<Directory> getAppDir() async {
if (useThisOne) { if (useThisOne) {
if (showNotice) { if (showNotice) {
showNotice = false; showNotice = false;
printV("Not using $preferredPath because $notSoPreferredPath exists, falling back for backwards compatibility"); printV(
printV("Can't see your wallet? Check\n - ${linuxAppPath.join("\n - ")}\n and move directory that to $preferredPath"); "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}"); printV("Or use CAKE_WALLET_DIR=/path/to/app/ ${Platform.executable}");
} }
preferredPath = notSoPreferredPath; preferredPath = notSoPreferredPath;
+2 -1
View File
@@ -14,7 +14,8 @@ Future<Uint8List> secRandom(int count) async {
final rng = Random.secure(); final rng = Random.secure();
return Uint8List.fromList(List<int>.generate(count, (_) => rng.nextInt(byteSize))); 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 (_) { } on PlatformException catch (_) {
return Uint8List.fromList([]); return Uint8List.fromList([]);
} }
+2 -4
View File
@@ -4,14 +4,12 @@ import 'package:on_chain/solana/solana.dart';
class SolanaRPCHTTPService implements SolanaJSONRPCService { class SolanaRPCHTTPService implements SolanaJSONRPCService {
SolanaRPCHTTPService( SolanaRPCHTTPService(
{required this.url, {required this.url, this.defaultRequestTimeout = const Duration(seconds: 30)});
this.defaultRequestTimeout = const Duration(seconds: 30)});
@override @override
final String url; final String url;
final Duration defaultRequestTimeout; final Duration defaultRequestTimeout;
Future<Map<String, dynamic>> call(SolanaRequestDetails params, Future<Map<String, dynamic>> call(SolanaRequestDetails params, [Duration? timeout]) async {
[Duration? timeout]) async {
final response = await ProxyWrapper().post( final response = await ProxyWrapper().post(
clearnetUri: Uri.parse(url), clearnetUri: Uri.parse(url),
body: params.toRequestBody(), body: params.toRequestBody(),
+1 -3
View File
@@ -58,7 +58,5 @@ class SPLTokenAdapter extends TypeAdapter<SPLToken> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is SPLTokenAdapter && other is SPLTokenAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
+4 -2
View File
@@ -7,7 +7,8 @@ TransactionDirection parseTransactionDirectionFromInt(int raw) {
case 1: case 1:
return TransactionDirection.outgoing; return TransactionDirection.outgoing;
default: 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": case "1":
return TransactionDirection.outgoing; return TransactionDirection.outgoing;
default: 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'; import 'package:cw_core/transaction_info.dart';
abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> { abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
TransactionHistoryBase() TransactionHistoryBase() : transactions = ObservableMap<String, TransactionType>();
: transactions = ObservableMap<String, TransactionType>();
@observable @observable
ObservableMap<String, TransactionType> transactions; ObservableMap<String, TransactionType> transactions;
+3 -3
View File
@@ -1,9 +1,9 @@
import 'package:cw_core/enumerable_item.dart'; import 'package:cw_core/enumerable_item.dart';
abstract class TransactionPriority extends EnumerableItem<int> abstract class TransactionPriority extends EnumerableItem<int> with Serializable<int> {
with Serializable<int> {
final String? description; final String? description;
final String? hint; 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 @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is TronTokenAdapter && other is TronTokenAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
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); writeData(path: path, password: password, data: data);
Future<void> writeData( Future<void> writeData(
{required String path, {required String path, required String password, required String data}) async {
required String password,
required String data}) async {
final keys = extractKeys(password); final keys = extractKeys(password);
final key = encrypt.Key.fromBase64(keys.first); final key = encrypt.Key.fromBase64(keys.first);
final iv = encrypt.IV.fromBase64(keys.last); final iv = encrypt.IV.fromBase64(keys.last);
@@ -50,14 +50,16 @@ class MemoryProxyLogger implements ProxyLogger {
required String? error, required String? error,
}) { }) {
final trace = StackTrace.current; final trace = StackTrace.current;
logs.add(MemoryProxyLoggerEntry( logs.add(
method: method, MemoryProxyLoggerEntry(
trace: trace, method: method,
uri: uri, trace: trace,
body: body, uri: uri,
network: network, body: body,
response: response, network: network,
error: error,), response: response,
error: error,
),
); );
} }
} }
+9 -6
View File
@@ -16,19 +16,21 @@ class ProxyAddress {
} }
abstract class ProxySocket { 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) { if (CakeTor.instance!.started) {
var socksSocket = await SOCKSSocket.create( var socksSocket = await SOCKSSocket.create(
proxyHost: InternetAddress.loopbackIPv4.address, proxyHost: InternetAddress.loopbackIPv4.address,
proxyPort: CakeTor.instance!.port, proxyPort: CakeTor.instance!.port,
sslEnabled: sslEnabled, sslEnabled: sslEnabled,
); );
await socksSocket.connect(); await socksSocket.connect();
await socksSocket.connectTo(address.host, address.port); await socksSocket.connectTo(address.host, address.port);
return ProxySocketSocks(socksSocket); return ProxySocketSocks(socksSocket);
} }
if (sslEnabled == false) { 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 { } else {
return ProxySocketSecure(await SecureSocket.connect( return ProxySocketSecure(await SecureSocket.connect(
address.host, address.host,
@@ -43,6 +45,7 @@ abstract class ProxySocket {
void destroy(); void destroy();
void write(String data); void write(String data);
bool get isClosed; 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; ProxyAddress get address;
} }
+5 -4
View File
@@ -1,4 +1,3 @@
import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_socket/abstract.dart'; import 'package:cw_core/utils/proxy_socket/abstract.dart';
import 'dart:async'; import 'dart:async';
@@ -12,12 +11,13 @@ class ProxySocketInsecure implements ProxySocket {
bool isClosed = false; 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 @override
Future<void> close() async { Future<void> close() async {
try { try {
if (isClosed) return; if (isClosed) return;
isClosed = true; isClosed = true;
return socket.close(); return socket.close();
} catch (e) { } catch (e) {
@@ -53,7 +53,8 @@ class ProxySocketInsecure implements ProxySocket {
} }
@override @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( return socket.listen(
(data) { (data) {
onData(Uint8List.fromList(data)); onData(Uint8List.fromList(data));
+5 -3
View File
@@ -12,7 +12,8 @@ class ProxySocketSecure implements ProxySocket {
ProxySocketSecure(this.socket); 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 @override
Future<void> close() async { Future<void> close() async {
@@ -29,7 +30,7 @@ class ProxySocketSecure implements ProxySocket {
@override @override
void destroy() async { void destroy() async {
try { try {
if (isClosed) return; if (isClosed) return;
isClosed = true; isClosed = true;
socket.destroy(); socket.destroy();
} catch (e) { } catch (e) {
@@ -59,7 +60,8 @@ class ProxySocketSecure implements ProxySocket {
} }
@override @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( return socket.listen(
(data) { (data) {
onData(Uint8List.fromList(data)); onData(Uint8List.fromList(data));
+2 -2
View File
@@ -43,7 +43,8 @@ class ProxySocketSocks implements ProxySocket {
} }
@override @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( return socket.listen(
(data) { (data) {
onData(Uint8List.fromList(data)); onData(Uint8List.fromList(data));
@@ -54,4 +55,3 @@ class ProxySocketSocks implements ProxySocket {
); );
} }
} }
+22 -26
View File
@@ -18,20 +18,21 @@ class ProxyWrapper {
} }
ProxyWrapper._internal(); 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( logger?.log(
uri: Uri( uri: Uri(
scheme: sslEnabled ? "https" : "http", scheme: sslEnabled ? "https" : "http",
host: host, host: host,
port: port, port: port,
), ),
method: RequestMethod.newProxySocket, method: RequestMethod.newProxySocket,
body: Uint8List(0), body: Uint8List(0),
response: null, response: null,
network: requestNetwork(), network: requestNetwork(),
error: null error: null);
); return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port),
return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port), connectionTimeout: connectionTimeout); connectionTimeout: connectionTimeout);
} }
RequestNetwork requestNetwork() { RequestNetwork requestNetwork() {
@@ -56,17 +57,17 @@ class ProxyWrapper {
int getPort() => CakeTor.instance!.port; 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}) { HttpClient getHttpClient({int? portOverride, bool internal = false}) {
if (!internal) { if (!internal) {
logger?.log( logger?.log(
uri: null, uri: null,
method: RequestMethod.newProxySocket, method: RequestMethod.newProxySocket,
body: Uint8List(0), body: Uint8List(0),
response: null, response: null,
network: requestNetwork(), network: requestNetwork(),
error: null error: null);
);
} }
if (CakeTor.instance!.started) { if (CakeTor.instance!.started) {
// Assign connection factory. // Assign connection factory.
@@ -84,8 +85,6 @@ class ProxyWrapper {
} }
} }
Future<Response> _make({ Future<Response> _make({
required RequestMethod method, required RequestMethod method,
required ioc.IOClient client, required ioc.IOClient client,
@@ -217,7 +216,6 @@ class ProxyWrapper {
throw Exception("Unable to connect to server"); throw Exception("Unable to connect to server");
} }
Future<Response> post({ Future<Response> post({
Map<String, String>? headers, Map<String, String>? headers,
int? portOverride, int? portOverride,
@@ -235,7 +233,6 @@ class ProxyWrapper {
ioc.IOClient clearnetClient = ioc.IOClient(cleatnetHttpClient); ioc.IOClient clearnetClient = ioc.IOClient(cleatnetHttpClient);
bool torEnabled = CakeTor.instance!.started; bool torEnabled = CakeTor.instance!.started;
if (torEnabled) { if (torEnabled) {
@@ -439,7 +436,6 @@ class ProxyWrapper {
} }
} }
class CakeTor { class CakeTor {
static CakeTorInstance? instance; static CakeTorInstance? instance;
} }
+4 -4
View File
@@ -1,4 +1,3 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:blockchain_utils/blockchain_utils.dart';
@@ -31,9 +30,10 @@ String convertZpubToXpub(String zpub) {
return zpub; return zpub;
} }
final xpubVersionBytes = isZpub ? final xpubVersionBytes = isZpub
[0x04, 0x88, 0xb2, 0x1e] : // xpub mainnet ? [0x04, 0x88, 0xb2, 0x1e]
[0x04, 0x35, 0x87, 0xcf]; // tpub testnet : // xpub mainnet
[0x04, 0x35, 0x87, 0xcf]; // tpub testnet
final newExtendedKey = Uint8List.fromList([ final newExtendedKey = Uint8List.fromList([
...xpubVersionBytes, ...xpubVersionBytes,
+119 -121
View File
@@ -327,79 +327,77 @@ class DerivationInfo {
class WalletInfo { class WalletInfo {
WalletInfo( WalletInfo(
this.internalId, this.internalId,
this.id, this.id,
this.name, this.name,
this.type, this.type,
this.isRecovery, this.isRecovery,
this.restoreHeight, this.restoreHeight,
this.timestamp, this.timestamp,
this.dirPath, this.dirPath,
this.path, this.path,
this.address, this.address,
this.yatEid, this.yatEid,
this.yatLastUsedAddressRaw, this.yatLastUsedAddressRaw,
this.showIntroCakePayCard, this.showIntroCakePayCard,
this.derivationInfoId, this.derivationInfoId,
this.hardwareWalletType, this.hardwareWalletType,
this.parentAddress, this.parentAddress,
this.hashedWalletIdentifier, this.hashedWalletIdentifier,
this.isNonSeedWallet, this.isNonSeedWallet,
this.sortOrder, this.sortOrder,
this.addressPageType, this.addressPageType,
this.receiveInfoboxDismissed, this.receiveInfoboxDismissed,
this.showCombinedBalance, this.showCombinedBalance,
this.favoriteTokenAddress this.favoriteTokenAddress)
) : _yatLastUsedAddressController = StreamController<String>.broadcast(); : _yatLastUsedAddressController = StreamController<String>.broadcast();
factory WalletInfo.external({ factory WalletInfo.external(
required String id, {required String id,
required String name, required String name,
required WalletType type, required WalletType type,
required bool isRecovery, required bool isRecovery,
required int restoreHeight, required int restoreHeight,
required DateTime date, required DateTime date,
required String dirPath, required String dirPath,
required String path, required String path,
required String address, required String address,
bool? showIntroCakePayCard, bool? showIntroCakePayCard,
String yatEid = '', String yatEid = '',
String yatLastUsedAddressRaw = '', String yatLastUsedAddressRaw = '',
int? derivationInfoId, int? derivationInfoId,
HardwareWalletType? hardwareWalletType, HardwareWalletType? hardwareWalletType,
String? parentAddress, String? parentAddress,
String? hashedWalletIdentifier, String? hashedWalletIdentifier,
bool? isNonSeedWallet, bool? isNonSeedWallet,
int? sortOrder, int? sortOrder,
bool? receiveInfoboxDismissed, bool? receiveInfoboxDismissed,
bool? showCombinedBalance, bool? showCombinedBalance,
String? favoriteTokenAddress String? favoriteTokenAddress}) {
}) {
return WalletInfo( return WalletInfo(
0, 0,
id, id,
name, name,
type, type,
isRecovery, isRecovery,
restoreHeight, restoreHeight,
date.millisecondsSinceEpoch, date.millisecondsSinceEpoch,
dirPath, dirPath,
path, path,
address, address,
yatEid, yatEid,
yatLastUsedAddressRaw, yatLastUsedAddressRaw,
showIntroCakePayCard, showIntroCakePayCard,
derivationInfoId ?? -1, derivationInfoId ?? -1,
hardwareWalletType, hardwareWalletType,
parentAddress, parentAddress,
hashedWalletIdentifier, hashedWalletIdentifier,
isNonSeedWallet ?? false, isNonSeedWallet ?? false,
sortOrder ?? 0, sortOrder ?? 0,
null, null,
receiveInfoboxDismissed ?? false, receiveInfoboxDismissed ?? false,
showCombinedBalance ?? true, showCombinedBalance ?? true,
favoriteTokenAddress favoriteTokenAddress);
);
} }
static String get tableName => 'walletInfo'; static String get tableName => 'walletInfo';
@@ -565,59 +563,59 @@ class WalletInfo {
StreamController<String> _yatLastUsedAddressController; StreamController<String> _yatLastUsedAddressController;
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
selfIdColumn: internalId, selfIdColumn: internalId,
"id": id, "id": id,
"name": name, "name": name,
"type": type.index, "type": type.index,
"isRecovery": isRecovery ? 1 : 0, "isRecovery": isRecovery ? 1 : 0,
"restoreHeight": restoreHeight, "restoreHeight": restoreHeight,
"timestamp": timestamp, "timestamp": timestamp,
"dirPath": dirPath, "dirPath": dirPath,
"path": path, "path": path,
"address": address, "address": address,
"yatEid": yatEid, "yatEid": yatEid,
"yatLastUsedAddressRaw": yatLastUsedAddressRaw, "yatLastUsedAddressRaw": yatLastUsedAddressRaw,
"showIntroCakePayCard": showIntroCakePayCard == true ? 1 : 0, // SQL regression: null -> false "showIntroCakePayCard":
"walletInfoDerivationInfoId": derivationInfoId, showIntroCakePayCard == true ? 1 : 0, // SQL regression: null -> false
"hardwareWalletType": hardwareWalletType?.index, "walletInfoDerivationInfoId": derivationInfoId,
"parentAddress": parentAddress, "hardwareWalletType": hardwareWalletType?.index,
"hashedWalletIdentifier": hashedWalletIdentifier, "parentAddress": parentAddress,
"isNonSeedWallet": isNonSeedWallet ? 1 : 0, "hashedWalletIdentifier": hashedWalletIdentifier,
"sortOrder": sortOrder, "isNonSeedWallet": isNonSeedWallet ? 1 : 0,
"addressPageType": addressPageType, "sortOrder": sortOrder,
"receiveInfoboxDismissed": receiveInfoboxDismissed ? 1 : 0, "addressPageType": addressPageType,
"showCombinedBalance": showCombinedBalance ? 1 : 0, "receiveInfoboxDismissed": receiveInfoboxDismissed ? 1 : 0,
"favoriteTokenAddress": favoriteTokenAddress "showCombinedBalance": showCombinedBalance ? 1 : 0,
}; "favoriteTokenAddress": favoriteTokenAddress
};
factory WalletInfo.fromJson(Map<String, dynamic> json) { factory WalletInfo.fromJson(Map<String, dynamic> json) {
return WalletInfo( return WalletInfo(
json[selfIdColumn] as int, json[selfIdColumn] as int,
json['id'] as String, json['id'] as String,
json['name'] as String, json['name'] as String,
WalletType.values[json['type'] as int], WalletType.values[json['type'] as int],
(json['isRecovery'] as int) == 1, (json['isRecovery'] as int) == 1,
json['restoreHeight'] as int, json['restoreHeight'] as int,
json['timestamp'] as int, json['timestamp'] as int,
json['dirPath'] as String, json['dirPath'] as String,
json['path'] as String, json['path'] as String,
json['address'] as String, json['address'] as String,
json['yatEid'] as String?, json['yatEid'] as String?,
json['yatLastUsedAddressRaw'] as String?, json['yatLastUsedAddressRaw'] as String?,
(json['showIntroCakePayCard'] as int) == 1, (json['showIntroCakePayCard'] as int) == 1,
json['walletInfoDerivationInfoId'] as int, json['walletInfoDerivationInfoId'] as int,
json['hardwareWalletType'] == null json['hardwareWalletType'] == null
? null ? null
: HardwareWalletType.values[json['hardwareWalletType'] as int], : HardwareWalletType.values[json['hardwareWalletType'] as int],
json['parentAddress'] as String?, json['parentAddress'] as String?,
json['hashedWalletIdentifier'] as String?, json['hashedWalletIdentifier'] as String?,
(json['isNonSeedWallet'] as int) == 1, (json['isNonSeedWallet'] as int) == 1,
json['sortOrder'] as int? ?? 0, json['sortOrder'] as int? ?? 0,
json['addressPageType'] as String? ?? null, json['addressPageType'] as String? ?? null,
json['receiveInfoboxDismissed'] != 0, json['receiveInfoboxDismissed'] != 0,
json["showCombinedBalance"] != 0, json["showCombinedBalance"] != 0,
json["favoriteTokenAddress"] as String? ?? null json["favoriteTokenAddress"] as String? ?? null);
);
} }
Future<int> save() async { Future<int> save() async {
+5 -11
View File
@@ -53,9 +53,7 @@ class DerivationInfoAdapter extends TypeAdapter<DerivationInfo> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is DerivationInfoAdapter && other is DerivationInfoAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
class WalletInfoAdapter extends TypeAdapter<WalletInfo> { class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
@@ -88,8 +86,8 @@ class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
fields[26] == null ? false : fields[26] as bool, fields[26] == null ? false : fields[26] as bool,
) )
..addresses = (fields[10] as Map?)?.cast<String, String>() ..addresses = (fields[10] as Map?)?.cast<String, String>()
..addressInfos = (fields[14] as Map?)?.map((dynamic k, dynamic v) => ..addressInfos = (fields[14] as Map?)
MapEntry(k as int, (v as List).cast<AddressInfo>())) ?.map((dynamic k, dynamic v) => MapEntry(k as int, (v as List).cast<AddressInfo>()))
..usedAddresses = (fields[15] as List?)?.cast<String>() ..usedAddresses = (fields[15] as List?)?.cast<String>()
..derivationType = fields[16] as newWi.DerivationType? ..derivationType = fields[16] as newWi.DerivationType?
..derivationPath = fields[17] as String? ..derivationPath = fields[17] as String?
@@ -163,9 +161,7 @@ class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is WalletInfoAdapter && other is WalletInfoAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
class DerivationTypeAdapter extends TypeAdapter<newWi.DerivationType> { class DerivationTypeAdapter extends TypeAdapter<newWi.DerivationType> {
@@ -217,9 +213,7 @@ class DerivationTypeAdapter extends TypeAdapter<newWi.DerivationType> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is DerivationTypeAdapter && other is DerivationTypeAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
class HardwareWalletTypeAdapter extends TypeAdapter<newWi.HardwareWalletType> { class HardwareWalletTypeAdapter extends TypeAdapter<newWi.HardwareWalletType> {
+8 -1
View File
@@ -101,7 +101,14 @@ class WalletKeysData {
final String? scanSecret; final String? scanSecret;
final String? spendPubkey; 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({ String toJSON() => jsonEncode({
"privateKey": privateKey, "privateKey": privateKey,
+12 -11
View File
@@ -334,17 +334,18 @@ WalletType? _cryptoCurrencyToWalletType(CryptoCurrency type) {
WalletType? cryptoCurrencyOrTokenToWalletType(CryptoCurrency type) { WalletType? cryptoCurrencyOrTokenToWalletType(CryptoCurrency type) {
try { try {
if(type.tag == CryptoCurrency.bnb.tag) { if (type.tag == CryptoCurrency.bnb.tag) {
return _cryptoCurrencyToWalletType(CryptoCurrency.bnb); return _cryptoCurrencyToWalletType(CryptoCurrency.bnb);
} }
if(type.tag != null && ![CryptoCurrency.btcln.tag, CryptoCurrency.bnb.tag].contains(type.tag)) { if (type.tag != null &&
return _cryptoCurrencyToWalletType(CryptoCurrency.fromString(type.tag!)); ![CryptoCurrency.btcln.tag, CryptoCurrency.bnb.tag].contains(type.tag)) {
} else { return _cryptoCurrencyToWalletType(CryptoCurrency.fromString(type.tag!));
return _cryptoCurrencyToWalletType(type); } else {
return _cryptoCurrencyToWalletType(type);
}
} catch (e) {
printV(e);
return null;
} }
} catch (e) {
printV(e);
return null;
}
} }
+1 -3
View File
@@ -130,7 +130,5 @@ class WalletTypeAdapter extends TypeAdapter<WalletType> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is WalletTypeAdapter && other is WalletTypeAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
+1 -3
View File
@@ -70,7 +70,5 @@ class ZanoAssetAdapter extends TypeAdapter<ZanoAsset> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is ZanoAssetAdapter && other is ZanoAssetAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
runtimeType == other.runtimeType &&
typeId == other.typeId;
} }
@@ -3,27 +3,27 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
group('smartAmountSanitizer', () { group('smartAmountSanitizer', () {
test('sanitize decimal dot', (){ test('sanitize decimal dot', () {
final amount = "1,000.00".sanitized(); final amount = "1,000.00".sanitized();
expect(amount, "1000.00"); expect(amount, "1000.00");
}); });
test('sanitize decimal comma', (){ test('sanitize decimal comma', () {
final amount = "1.000,00".sanitized(); final amount = "1.000,00".sanitized();
expect(amount, "1000.00"); expect(amount, "1000.00");
}); });
test('general case', (){ test('general case', () {
final amount = "1000.00".sanitized(); final amount = "1000.00".sanitized();
expect(amount, "1000.00"); expect(amount, "1000.00");
}); });
test('no decimals', (){ test('no decimals', () {
final amount = "1,000".sanitized(); final amount = "1,000".sanitized();
expect(amount, "1.000"); expect(amount, "1.000");
}); });
test('no decimals', (){ test('no decimals', () {
final amount = "1.000".sanitized(); final amount = "1.000".sanitized();
expect(amount, "1.000"); expect(amount, "1.000");
}); });
+2 -2
View File
@@ -316,7 +316,7 @@ void main() {
}); });
}); });
group("toStringWithPrecision", (){ group("toStringWithPrecision", () {
test("with fractionalDigits and padded with zeros", () { test("with fractionalDigits and padded with zeros", () {
final money = Money.parse("1", CryptoCurrency.btc); final money = Money.parse("1", CryptoCurrency.btc);
expect(money.amount, equals(BigInt.parse("100000000"))); expect(money.amount, equals(BigInt.parse("100000000")));
@@ -332,7 +332,7 @@ void main() {
}); });
}); });
group("toStringWithSymbol", (){ group("toStringWithSymbol", () {
test("with fractionalDigits and padded with zeros", () { test("with fractionalDigits and padded with zeros", () {
final money = Money.parse("1", CryptoCurrency.btc); final money = Money.parse("1", CryptoCurrency.btc);
expect(money.amount, equals(BigInt.parse("100000000"))); expect(money.amount, equals(BigInt.parse("100000000")));
+4 -8
View File
@@ -14,14 +14,12 @@ void main() {
group('formatFixed, different fractional digits and trimming zeros', () { group('formatFixed, different fractional digits and trimming zeros', () {
test( test(
'should format 1000001 into 1', 'should format 1000001 into 1',
() => expect( () => expect(formatFixed(BigInt.parse("1000001"), 6, fractionalDigits: 5), '1'),
formatFixed(BigInt.parse("1000001"), 6, fractionalDigits: 5), '1'),
); );
test( test(
'should format 1000000 into 1, fractionalDigits > decimals', 'should format 1000000 into 1, fractionalDigits > decimals',
() => expect( () => expect(formatFixed(BigInt.parse("1000000"), 6, fractionalDigits: 12), '1'),
formatFixed(BigInt.parse("1000000"), 6, fractionalDigits: 12), '1'),
); );
test( test(
@@ -45,8 +43,7 @@ void main() {
test( test(
'should format 1000001 into 1.00000', 'should format 1000001 into 1.00000',
() => expect( () => expect(
formatFixed(BigInt.parse("1000001"), 6, formatFixed(BigInt.parse("1000001"), 6, fractionalDigits: 5, trimZeros: false),
fractionalDigits: 5, trimZeros: false),
'1.00000', '1.00000',
), ),
); );
@@ -54,8 +51,7 @@ void main() {
test( test(
'should format 1000000 into 1.000000', 'should format 1000000 into 1.000000',
() => expect( () => expect(
formatFixed(BigInt.parse("1000000"), 6, formatFixed(BigInt.parse("1000000"), 6, fractionalDigits: 12, trimZeros: false),
fractionalDigits: 12, trimZeros: false),
'1.000000', '1.000000',
), ),
); );
+18 -9
View File
@@ -39,49 +39,58 @@ void main() {
group("isBolt11ZeroInvoice", () { group("isBolt11ZeroInvoice", () {
test("should identify a zero amount invoice", () { test("should identify a zero amount invoice", () {
final invoice = "lnbc1p5564y8pp5vm48jp3w82yjssrtdhvrjacwwzp59hqjm2lq6fud5thfznyuswgqsp54mykkptx9mlqhpe93f9pcgt9p5ml5xhclyn7qsltqy9ldf25j3qqxqyz5vqnp4qvyndeaqzman7h898jxm98dzkm0mlrsx36s93smrur7h0azyyuxc5rzjqwghf7zxvfkxq5a6sr65g0gdkv768p83mhsnt0msszapamzx2qvuxqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjq25carzepgd4vqsyn44jrk85ezrpju92xyrk9apw4cdjh6yrwt5jgqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjqfvmpdwfesckajzfcf7ff2vqaz9jdgpcsa9xc0fq8ny749dy8y5geapyqr6zgqqqq8hxk2qqae4jsqyugqcqzpgdqq9qyyssqycdyx8r09wgm8vny3n8pf2e0crcdq9mgx3ncxvzsrkljxyrcuwx8zjtje7du40nzdy7x2he67v9asp6ac6ed75r33yyt9pzeepygcmsptc4ve4"; final invoice =
"lnbc1p5564y8pp5vm48jp3w82yjssrtdhvrjacwwzp59hqjm2lq6fud5thfznyuswgqsp54mykkptx9mlqhpe93f9pcgt9p5ml5xhclyn7qsltqy9ldf25j3qqxqyz5vqnp4qvyndeaqzman7h898jxm98dzkm0mlrsx36s93smrur7h0azyyuxc5rzjqwghf7zxvfkxq5a6sr65g0gdkv768p83mhsnt0msszapamzx2qvuxqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjq25carzepgd4vqsyn44jrk85ezrpju92xyrk9apw4cdjh6yrwt5jgqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjqfvmpdwfesckajzfcf7ff2vqaz9jdgpcsa9xc0fq8ny749dy8y5geapyqr6zgqqqq8hxk2qqae4jsqyugqcqzpgdqq9qyyssqycdyx8r09wgm8vny3n8pf2e0crcdq9mgx3ncxvzsrkljxyrcuwx8zjtje7du40nzdy7x2he67v9asp6ac6ed75r33yyt9pzeepygcmsptc4ve4";
expect(isBolt11ZeroInvoice(invoice), true); expect(isBolt11ZeroInvoice(invoice), true);
}); });
test("should identify a non zero amount invoice", () { test("should identify a non zero amount invoice", () {
final invoice = "lnbc1u1p5564nxpp5mrklx2pjaggkcfs9r5tfk84wszzy9gypcnrt9yjq7n6uf95s70xqsp59fph78twjra598n2mll2arw5enluy7a4uf4y9a94ddd30hm98njsxq9z0rgqnp4qvyndeaqzman7h898jxm98dzkm0mlrsx36s93smrur7h0azyyuxc5rzjqwghf7zxvfkxq5a6sr65g0gdkv768p83mhsnt0msszapamzx2qvuxqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjq25carzepgd4vqsyn44jrk85ezrpju92xyrk9apw4cdjh6yrwt5jgqqqqrt49lmtcqqqqqqqqqqq86qq9qcqzpgdql2djkuepqw3hjqsmpddjjq4mpd3kx2aq9qyyssq3nf60ey9fgkf0elu2y8j96rx5pm4gx2a976h8yadx83dyg0ltnysrxhep8e2p3yvz4kf597qk3rttgdy72wwqq0mwr8hdht3pqpchyqq0aa9v0"; final invoice =
"lnbc1u1p5564nxpp5mrklx2pjaggkcfs9r5tfk84wszzy9gypcnrt9yjq7n6uf95s70xqsp59fph78twjra598n2mll2arw5enluy7a4uf4y9a94ddd30hm98njsxq9z0rgqnp4qvyndeaqzman7h898jxm98dzkm0mlrsx36s93smrur7h0azyyuxc5rzjqwghf7zxvfkxq5a6sr65g0gdkv768p83mhsnt0msszapamzx2qvuxqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjq25carzepgd4vqsyn44jrk85ezrpju92xyrk9apw4cdjh6yrwt5jgqqqqrt49lmtcqqqqqqqqqqq86qq9qcqzpgdql2djkuepqw3hjqsmpddjjq4mpd3kx2aq9qyyssq3nf60ey9fgkf0elu2y8j96rx5pm4gx2a976h8yadx83dyg0ltnysrxhep8e2p3yvz4kf597qk3rttgdy72wwqq0mwr8hdht3pqpchyqq0aa9v0";
expect(isBolt11ZeroInvoice(invoice), false); expect(isBolt11ZeroInvoice(invoice), false);
}); });
test("should identify a zero amount prefixed invoice", () { test("should identify a zero amount prefixed invoice", () {
final invoice = "lightning:lnbc1p5564y8pp5vm48jp3w82yjssrtdhvrjacwwzp59hqjm2lq6fud5thfznyuswgqsp54mykkptx9mlqhpe93f9pcgt9p5ml5xhclyn7qsltqy9ldf25j3qqxqyz5vqnp4qvyndeaqzman7h898jxm98dzkm0mlrsx36s93smrur7h0azyyuxc5rzjqwghf7zxvfkxq5a6sr65g0gdkv768p83mhsnt0msszapamzx2qvuxqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjq25carzepgd4vqsyn44jrk85ezrpju92xyrk9apw4cdjh6yrwt5jgqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjqfvmpdwfesckajzfcf7ff2vqaz9jdgpcsa9xc0fq8ny749dy8y5geapyqr6zgqqqq8hxk2qqae4jsqyugqcqzpgdqq9qyyssqycdyx8r09wgm8vny3n8pf2e0crcdq9mgx3ncxvzsrkljxyrcuwx8zjtje7du40nzdy7x2he67v9asp6ac6ed75r33yyt9pzeepygcmsptc4ve4"; final invoice =
"lightning:lnbc1p5564y8pp5vm48jp3w82yjssrtdhvrjacwwzp59hqjm2lq6fud5thfznyuswgqsp54mykkptx9mlqhpe93f9pcgt9p5ml5xhclyn7qsltqy9ldf25j3qqxqyz5vqnp4qvyndeaqzman7h898jxm98dzkm0mlrsx36s93smrur7h0azyyuxc5rzjqwghf7zxvfkxq5a6sr65g0gdkv768p83mhsnt0msszapamzx2qvuxqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjq25carzepgd4vqsyn44jrk85ezrpju92xyrk9apw4cdjh6yrwt5jgqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjqfvmpdwfesckajzfcf7ff2vqaz9jdgpcsa9xc0fq8ny749dy8y5geapyqr6zgqqqq8hxk2qqae4jsqyugqcqzpgdqq9qyyssqycdyx8r09wgm8vny3n8pf2e0crcdq9mgx3ncxvzsrkljxyrcuwx8zjtje7du40nzdy7x2he67v9asp6ac6ed75r33yyt9pzeepygcmsptc4ve4";
expect(isBolt11ZeroInvoice(invoice), true); expect(isBolt11ZeroInvoice(invoice), true);
}); });
test("should identify a non zero amount prefixed invoice", () { test("should identify a non zero amount prefixed invoice", () {
final invoice = "lightning:lnbc1u1p5564nxpp5mrklx2pjaggkcfs9r5tfk84wszzy9gypcnrt9yjq7n6uf95s70xqsp59fph78twjra598n2mll2arw5enluy7a4uf4y9a94ddd30hm98njsxq9z0rgqnp4qvyndeaqzman7h898jxm98dzkm0mlrsx36s93smrur7h0azyyuxc5rzjqwghf7zxvfkxq5a6sr65g0gdkv768p83mhsnt0msszapamzx2qvuxqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjq25carzepgd4vqsyn44jrk85ezrpju92xyrk9apw4cdjh6yrwt5jgqqqqrt49lmtcqqqqqqqqqqq86qq9qcqzpgdql2djkuepqw3hjqsmpddjjq4mpd3kx2aq9qyyssq3nf60ey9fgkf0elu2y8j96rx5pm4gx2a976h8yadx83dyg0ltnysrxhep8e2p3yvz4kf597qk3rttgdy72wwqq0mwr8hdht3pqpchyqq0aa9v0"; final invoice =
"lightning:lnbc1u1p5564nxpp5mrklx2pjaggkcfs9r5tfk84wszzy9gypcnrt9yjq7n6uf95s70xqsp59fph78twjra598n2mll2arw5enluy7a4uf4y9a94ddd30hm98njsxq9z0rgqnp4qvyndeaqzman7h898jxm98dzkm0mlrsx36s93smrur7h0azyyuxc5rzjqwghf7zxvfkxq5a6sr65g0gdkv768p83mhsnt0msszapamzx2qvuxqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjq25carzepgd4vqsyn44jrk85ezrpju92xyrk9apw4cdjh6yrwt5jgqqqqrt49lmtcqqqqqqqqqqq86qq9qcqzpgdql2djkuepqw3hjqsmpddjjq4mpd3kx2aq9qyyssq3nf60ey9fgkf0elu2y8j96rx5pm4gx2a976h8yadx83dyg0ltnysrxhep8e2p3yvz4kf597qk3rttgdy72wwqq0mwr8hdht3pqpchyqq0aa9v0";
expect(isBolt11ZeroInvoice(invoice), false); expect(isBolt11ZeroInvoice(invoice), false);
}); });
}); });
group("getBolt11Amount", () { group("getBolt11Amount", () {
test("should identify a zero amount invoice", () { test("should identify a zero amount invoice", () {
final invoice = "lnbc1p5564y8pp5vm48jp3w82yjssrtdhvrjacwwzp59hqjm2lq6fud5thfznyuswgqsp54mykkptx9mlqhpe93f9pcgt9p5ml5xhclyn7qsltqy9ldf25j3qqxqyz5vqnp4qvyndeaqzman7h898jxm98dzkm0mlrsx36s93smrur7h0azyyuxc5rzjqwghf7zxvfkxq5a6sr65g0gdkv768p83mhsnt0msszapamzx2qvuxqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjq25carzepgd4vqsyn44jrk85ezrpju92xyrk9apw4cdjh6yrwt5jgqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjqfvmpdwfesckajzfcf7ff2vqaz9jdgpcsa9xc0fq8ny749dy8y5geapyqr6zgqqqq8hxk2qqae4jsqyugqcqzpgdqq9qyyssqycdyx8r09wgm8vny3n8pf2e0crcdq9mgx3ncxvzsrkljxyrcuwx8zjtje7du40nzdy7x2he67v9asp6ac6ed75r33yyt9pzeepygcmsptc4ve4"; final invoice =
"lnbc1p5564y8pp5vm48jp3w82yjssrtdhvrjacwwzp59hqjm2lq6fud5thfznyuswgqsp54mykkptx9mlqhpe93f9pcgt9p5ml5xhclyn7qsltqy9ldf25j3qqxqyz5vqnp4qvyndeaqzman7h898jxm98dzkm0mlrsx36s93smrur7h0azyyuxc5rzjqwghf7zxvfkxq5a6sr65g0gdkv768p83mhsnt0msszapamzx2qvuxqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjq25carzepgd4vqsyn44jrk85ezrpju92xyrk9apw4cdjh6yrwt5jgqqqqrt49lmtcqqqqqqqqqqq86qq9qrzjqfvmpdwfesckajzfcf7ff2vqaz9jdgpcsa9xc0fq8ny749dy8y5geapyqr6zgqqqq8hxk2qqae4jsqyugqcqzpgdqq9qyyssqycdyx8r09wgm8vny3n8pf2e0crcdq9mgx3ncxvzsrkljxyrcuwx8zjtje7du40nzdy7x2he67v9asp6ac6ed75r33yyt9pzeepygcmsptc4ve4";
expect(getBolt11Amount(invoice), null); expect(getBolt11Amount(invoice), null);
}); });
test("should get the amount 1 BTC encoded as bitcoin", () { test("should get the amount 1 BTC encoded as bitcoin", () {
final invoice = "lnbc11p5exaxspp5u6e6lx90rw2vqr2e5axt8ur3v98qnmqfsmh8r0l0mcm7ry70vyvsdqqcqzzsxqrrsssp5eqydmrufk39dwhuxzajwx4xyffn0mlcu3qa8vjwry6qev5gsepkq9qxpqysgqt0zldve9uxt3txjt8wp0gev52lquljgugy26nneue85pjwk5yak4hs2d8533tus07pq5ka6zrnd0ruaeepwres8p7ws5mx63xlwff5sqlxcy8a"; final invoice =
"lnbc11p5exaxspp5u6e6lx90rw2vqr2e5axt8ur3v98qnmqfsmh8r0l0mcm7ry70vyvsdqqcqzzsxqrrsssp5eqydmrufk39dwhuxzajwx4xyffn0mlcu3qa8vjwry6qev5gsepkq9qxpqysgqt0zldve9uxt3txjt8wp0gev52lquljgugy26nneue85pjwk5yak4hs2d8533tus07pq5ka6zrnd0ruaeepwres8p7ws5mx63xlwff5sqlxcy8a";
expect(getBolt11Amount(invoice), Money.fromInt(100000000, CryptoCurrency.btcln)); expect(getBolt11Amount(invoice), Money.fromInt(100000000, CryptoCurrency.btcln));
}); });
test("should get the amount 0,1 BTC encoded as millibitcoin", () { test("should get the amount 0,1 BTC encoded as millibitcoin", () {
final invoice = "lnbc100m1p5exagmpp5n4c5tml3h8h7k4gthc9papkyeymceqqvcldkng4nvxwzj7nu9jnsdqqcqzzsxqrrsssp58kgzrh7y3pevantduc4yg33aa6r79ed6xgmhr0dy40zc8pxrz5ms9qxpqysgqcq27km3v3wcz6pntmsfnz259zy6tff0uwjz9kamdrvmxmdv09gp44mh0ygn7gweqf4zdev4x8e67p5kcvahethchdkkjexhtk7emuqcpasz0lp"; final invoice =
"lnbc100m1p5exagmpp5n4c5tml3h8h7k4gthc9papkyeymceqqvcldkng4nvxwzj7nu9jnsdqqcqzzsxqrrsssp58kgzrh7y3pevantduc4yg33aa6r79ed6xgmhr0dy40zc8pxrz5ms9qxpqysgqcq27km3v3wcz6pntmsfnz259zy6tff0uwjz9kamdrvmxmdv09gp44mh0ygn7gweqf4zdev4x8e67p5kcvahethchdkkjexhtk7emuqcpasz0lp";
expect(getBolt11Amount(invoice), Money.fromInt(10000000, CryptoCurrency.btcln)); expect(getBolt11Amount(invoice), Money.fromInt(10000000, CryptoCurrency.btcln));
}); });
test("should get the amount 1000 SATS encoded as microbitcoin", () { test("should get the amount 1000 SATS encoded as microbitcoin", () {
final invoice = "lnbc10u1p5exfgvpp5n3s9dsw9ddax5c3h8437ya7y4g582uyufyna626yr9vqdfjrhwzqdqqcqzzsxqrrsssp5mmkyp5h35fpaxm4063f3kc7d4nmgd4gd8fv8086wxd784m76kmpq9qxpqysgqye9r0vahpjy9l2je6vakxzk3cjnsyx76r29c4amkz4cmapq5tqt5l7rl0emkd003jjvz2d8jqrw6wc4cduvapuyeseh7a855l82wutcpeq30kg"; final invoice =
"lnbc10u1p5exfgvpp5n3s9dsw9ddax5c3h8437ya7y4g582uyufyna626yr9vqdfjrhwzqdqqcqzzsxqrrsssp5mmkyp5h35fpaxm4063f3kc7d4nmgd4gd8fv8086wxd784m76kmpq9qxpqysgqye9r0vahpjy9l2je6vakxzk3cjnsyx76r29c4amkz4cmapq5tqt5l7rl0emkd003jjvz2d8jqrw6wc4cduvapuyeseh7a855l82wutcpeq30kg";
expect(getBolt11Amount(invoice), Money.fromInt(1000, CryptoCurrency.btcln)); expect(getBolt11Amount(invoice), Money.fromInt(1000, CryptoCurrency.btcln));
}); });
test("should get the amount 1 SAT encoded as nanobitcoin", () { test("should get the amount 1 SAT encoded as nanobitcoin", () {
final invoice = "lnbc10n1p5exazppp5szh273r6nzvwydzsdlg0ws0vhxwnc6nqtqt4sjt0vu6x6l8t5csqdqqcqzzsxqrrsssp5nkup6uht7xlfqaj8lxpe5kx0ejum9p4pvv3nlnndr9ykzge3393s9qxpqysgqufh05a7qs0ezeckvmh7tjkr9ngng96nq274twg20z45ysp7afpdrttm2ky73cpjwpjqsmtzzd79w6lyvszgmkanwn8mj0jcfunrwv3qprg3z3j"; final invoice =
"lnbc10n1p5exazppp5szh273r6nzvwydzsdlg0ws0vhxwnc6nqtqt4sjt0vu6x6l8t5csqdqqcqzzsxqrrsssp5nkup6uht7xlfqaj8lxpe5kx0ejum9p4pvv3nlnndr9ykzge3393s9qxpqysgqufh05a7qs0ezeckvmh7tjkr9ngng96nq274twg20z45ysp7afpdrttm2ky73cpjwpjqsmtzzd79w6lyvszgmkanwn8mj0jcfunrwv3qprg3z3j";
expect(getBolt11Amount(invoice), Money.fromInt(1, CryptoCurrency.btcln)); expect(getBolt11Amount(invoice), Money.fromInt(1, CryptoCurrency.btcln));
}); });
}); });
@@ -51,8 +51,7 @@ class _Visitor extends SimpleAstVisitor<void> {
if (uriString.startsWith("package:http")) { if (uriString.startsWith("package:http")) {
final prefixNode = node.prefix; final prefixNode = node.prefix;
if (prefixNode != null && if (prefixNode != null && prefixNode.name == "very_insecure_http_do_not_use") {
prefixNode.name == "very_insecure_http_do_not_use") {
return; return;
} }
@@ -17,8 +17,7 @@ class ReplaceWithPrintV extends ResolvedCorrectionProducer {
); );
@override @override
CorrectionApplicability get applicability => CorrectionApplicability get applicability => CorrectionApplicability.singleLocation;
CorrectionApplicability.singleLocation;
@override @override
FixKind get fixKind => _fixKind; FixKind get fixKind => _fixKind;
-1
View File
@@ -3,4 +3,3 @@ export 'src/dogecoin_wallet_addresses.dart';
export 'src/dogecoin_wallet_creation_credentials.dart'; export 'src/dogecoin_wallet_creation_credentials.dart';
export 'src/dogecoin_wallet_service.dart'; export 'src/dogecoin_wallet_service.dart';
export 'src/dogecoin_transaction_priority.dart'; export 'src/dogecoin_transaction_priority.dart';
@@ -7,11 +7,11 @@ class DogecoinTransactionPriority extends BitcoinTransactionPriority {
static const List<DogecoinTransactionPriority> all = [fast, medium, slow]; static const List<DogecoinTransactionPriority> all = [fast, medium, slow];
static const DogecoinTransactionPriority slow = static const DogecoinTransactionPriority slow =
DogecoinTransactionPriority(title: 'Slow', raw: 0); DogecoinTransactionPriority(title: 'Slow', raw: 0);
static const DogecoinTransactionPriority medium = static const DogecoinTransactionPriority medium =
DogecoinTransactionPriority(title: 'Medium', raw: 1); DogecoinTransactionPriority(title: 'Medium', raw: 1);
static const DogecoinTransactionPriority fast = static const DogecoinTransactionPriority fast =
DogecoinTransactionPriority(title: 'Fast', raw: 2); DogecoinTransactionPriority(title: 'Fast', raw: 2);
static DogecoinTransactionPriority deserialize({required int raw}) { static DogecoinTransactionPriority deserialize({required int raw}) {
switch (raw) { switch (raw) {
@@ -53,4 +53,3 @@ class DogecoinTransactionPriority extends BitcoinTransactionPriority {
return label; return label;
} }
} }
+1 -2
View File
@@ -75,14 +75,13 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
@override @override
int feeAmountForPriority(TransactionPriority priority, int inputsCount, int outputsCount, int feeAmountForPriority(TransactionPriority priority, int inputsCount, int outputsCount,
{int? size}) => {int? size}) =>
feeRate(priority) * (size ?? estimatedDogeCoinTransactionSize(inputsCount, outputsCount)); feeRate(priority) * (size ?? estimatedDogeCoinTransactionSize(inputsCount, outputsCount));
@override @override
int feeAmountWithFeeRate(int feeRate, int inputsCount, int outputsCount, {int? size}) => int feeAmountWithFeeRate(int feeRate, int inputsCount, int outputsCount, {int? size}) =>
feeRate * (size ?? estimatedDogeCoinTransactionSize(inputsCount, outputsCount)); feeRate * (size ?? estimatedDogeCoinTransactionSize(inputsCount, outputsCount));
static Future<DogeCoinWallet> create( static Future<DogeCoinWallet> create(
{required String mnemonic, {required String mnemonic,
required String password, required String password,
@@ -8,11 +8,9 @@ import 'package:mobx/mobx.dart';
part 'dogecoin_wallet_addresses.g.dart'; part 'dogecoin_wallet_addresses.g.dart';
class DogeCoinWalletAddresses = DogeCoinWalletAddressesBase class DogeCoinWalletAddresses = DogeCoinWalletAddressesBase with _$DogeCoinWalletAddresses;
with _$DogeCoinWalletAddresses;
abstract class DogeCoinWalletAddressesBase extends ElectrumWalletAddresses abstract class DogeCoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
with Store {
DogeCoinWalletAddressesBase( DogeCoinWalletAddressesBase(
WalletInfo walletInfo, { WalletInfo walletInfo, {
required super.mainHdByType, required super.mainHdByType,
@@ -36,6 +34,5 @@ abstract class DogeCoinWalletAddressesBase extends ElectrumWalletAddresses
generateP2PKHAddress(hd: hd, index: index, network: network); generateP2PKHAddress(hd: hd, index: index, network: network);
@override @override
PaymentURI getPaymentUri(String amount) => PaymentURI getPaymentUri(String amount) => DogeURI(address: address, amount: amount);
DogeURI(address: address, amount: amount);
} }
+1 -2
View File
@@ -3,6 +3,5 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:cw_dogecoin/cw_dogecoin.dart'; import 'package:cw_dogecoin/cw_dogecoin.dart';
void main() { void main() {
test('adds one to input values', () { test('adds one to input values', () {});
});
} }
-1
View File
@@ -43,4 +43,3 @@ class ArbitrumClient extends EVMChainClient {
@override @override
int get chainId => 42161; int get chainId => 42161;
} }
-1
View File
@@ -43,4 +43,3 @@ class BaseClient extends EVMChainClient {
@override @override
int get chainId => 8453; int get chainId => 8453;
} }

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