mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-08 20:09:24 +00:00
Merge branch 'bitcoin-derivations' of https://github.com/cake-tech/cake_wallet into bip39-less-confusing
This commit is contained in:
commit
49d6a29065
20 changed files with 154 additions and 77 deletions
|
@ -37,7 +37,7 @@ class BitcoinTransactionPriority extends TransactionPriority {
|
||||||
|
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case BitcoinTransactionPriority.slow:
|
case BitcoinTransactionPriority.slow:
|
||||||
label = 'Slow ~24hrs'; // '${S.current.transaction_priority_slow} ~24hrs';
|
label = 'Slow ~24hrs+'; // '${S.current.transaction_priority_slow} ~24hrs';
|
||||||
break;
|
break;
|
||||||
case BitcoinTransactionPriority.medium:
|
case BitcoinTransactionPriority.medium:
|
||||||
label = 'Medium'; // S.current.transaction_priority_medium;
|
label = 'Medium'; // S.current.transaction_priority_medium;
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart';
|
import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
|
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
|
||||||
import 'package:cw_core/node.dart';
|
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_service.dart';
|
import 'package:cw_core/wallet_service.dart';
|
||||||
|
@ -127,8 +126,4 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<dynamic> getInfoFromSeed({required String seed, required Node node}) async {
|
|
||||||
throw UnimplementedError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,10 +206,14 @@ abstract class ElectrumWalletBase
|
||||||
List<ECPrivate> privateKeys = [];
|
List<ECPrivate> privateKeys = [];
|
||||||
int allInputsAmount = 0;
|
int allInputsAmount = 0;
|
||||||
|
|
||||||
|
bool spendsUnconfirmedTX = false;
|
||||||
|
|
||||||
for (int i = 0; i < unspentCoins.length; i++) {
|
for (int i = 0; i < unspentCoins.length; i++) {
|
||||||
final utx = unspentCoins[i];
|
final utx = unspentCoins[i];
|
||||||
|
|
||||||
if (utx.isSending) {
|
if (utx.isSending && !utx.isFrozen) {
|
||||||
|
if (!spendsUnconfirmedTX) spendsUnconfirmedTX = utx.confirmations == 0;
|
||||||
|
|
||||||
allInputsAmount += utx.value;
|
allInputsAmount += utx.value;
|
||||||
|
|
||||||
final address = addressTypeFromStr(utx.address, network);
|
final address = addressTypeFromStr(utx.address, network);
|
||||||
|
@ -267,6 +271,10 @@ abstract class ElectrumWalletBase
|
||||||
// Here, when sending all, the output amount equals to the input value - fee to fully spend every input on the transaction and have no amount left for change
|
// Here, when sending all, the output amount equals to the input value - fee to fully spend every input on the transaction and have no amount left for change
|
||||||
int amount = allInputsAmount - fee;
|
int amount = allInputsAmount - fee;
|
||||||
|
|
||||||
|
if (amount <= 0) {
|
||||||
|
throw BitcoinTransactionWrongBalanceException();
|
||||||
|
}
|
||||||
|
|
||||||
// Attempting to send less than the dust limit
|
// Attempting to send less than the dust limit
|
||||||
if (_isBelowDust(amount)) {
|
if (_isBelowDust(amount)) {
|
||||||
throw BitcoinTransactionNoDustException();
|
throw BitcoinTransactionNoDustException();
|
||||||
|
@ -291,6 +299,7 @@ abstract class ElectrumWalletBase
|
||||||
isSendAll: true,
|
isSendAll: true,
|
||||||
hasChange: false,
|
hasChange: false,
|
||||||
memo: memo,
|
memo: memo,
|
||||||
|
spendsUnconfirmedTX: spendsUnconfirmedTX,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,17 +309,25 @@ abstract class ElectrumWalletBase
|
||||||
int feeRate, {
|
int feeRate, {
|
||||||
int? inputsCount,
|
int? inputsCount,
|
||||||
String? memo,
|
String? memo,
|
||||||
|
bool? useUnconfirmed,
|
||||||
}) async {
|
}) async {
|
||||||
final utxos = <UtxoWithAddress>[];
|
final utxos = <UtxoWithAddress>[];
|
||||||
List<ECPrivate> privateKeys = [];
|
List<ECPrivate> privateKeys = [];
|
||||||
int allInputsAmount = 0;
|
int allInputsAmount = 0;
|
||||||
|
bool spendsUnconfirmedTX = false;
|
||||||
|
|
||||||
int leftAmount = credentialsAmount;
|
int leftAmount = credentialsAmount;
|
||||||
final sendingCoins = unspentCoins.where((utx) => utx.isSending).toList();
|
final sendingCoins = unspentCoins.where((utx) => utx.isSending && !utx.isFrozen).toList();
|
||||||
|
final unconfirmedCoins = sendingCoins.where((utx) => utx.confirmations == 0).toList();
|
||||||
|
|
||||||
for (int i = 0; i < sendingCoins.length; i++) {
|
for (int i = 0; i < sendingCoins.length; i++) {
|
||||||
final utx = sendingCoins[i];
|
final utx = sendingCoins[i];
|
||||||
|
|
||||||
|
final isUncormirmed = utx.confirmations == 0;
|
||||||
|
if (useUnconfirmed != true && isUncormirmed) continue;
|
||||||
|
|
||||||
|
if (!spendsUnconfirmedTX) spendsUnconfirmedTX = isUncormirmed;
|
||||||
|
|
||||||
allInputsAmount += utx.value;
|
allInputsAmount += utx.value;
|
||||||
leftAmount = leftAmount - utx.value;
|
leftAmount = leftAmount - utx.value;
|
||||||
|
|
||||||
|
@ -348,11 +365,23 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
final spendingAllCoins = sendingCoins.length == utxos.length;
|
final spendingAllCoins = sendingCoins.length == utxos.length;
|
||||||
|
final spendingAllConfirmedCoins =
|
||||||
|
!spendsUnconfirmedTX && utxos.length == sendingCoins.length - unconfirmedCoins.length;
|
||||||
|
|
||||||
// How much is being spent - how much is being sent
|
// How much is being spent - how much is being sent
|
||||||
int amountLeftForChangeAndFee = allInputsAmount - credentialsAmount;
|
int amountLeftForChangeAndFee = allInputsAmount - credentialsAmount;
|
||||||
|
|
||||||
if (amountLeftForChangeAndFee <= 0) {
|
if (amountLeftForChangeAndFee <= 0) {
|
||||||
|
if (!spendingAllCoins) {
|
||||||
|
return estimateTxForAmount(
|
||||||
|
credentialsAmount,
|
||||||
|
outputs,
|
||||||
|
feeRate,
|
||||||
|
inputsCount: utxos.length + 1,
|
||||||
|
memo: memo,
|
||||||
|
useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
|
||||||
|
);
|
||||||
|
}
|
||||||
throw BitcoinTransactionWrongBalanceException();
|
throw BitcoinTransactionWrongBalanceException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,6 +435,7 @@ abstract class ElectrumWalletBase
|
||||||
feeRate,
|
feeRate,
|
||||||
inputsCount: utxos.length + 1,
|
inputsCount: utxos.length + 1,
|
||||||
memo: memo,
|
memo: memo,
|
||||||
|
useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,6 +482,7 @@ abstract class ElectrumWalletBase
|
||||||
feeRate,
|
feeRate,
|
||||||
inputsCount: utxos.length + 1,
|
inputsCount: utxos.length + 1,
|
||||||
memo: memo,
|
memo: memo,
|
||||||
|
useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -464,6 +495,7 @@ abstract class ElectrumWalletBase
|
||||||
hasChange: true,
|
hasChange: true,
|
||||||
isSendAll: false,
|
isSendAll: false,
|
||||||
memo: memo,
|
memo: memo,
|
||||||
|
spendsUnconfirmedTX: spendsUnconfirmedTX,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,7 +566,7 @@ abstract class ElectrumWalletBase
|
||||||
network: network,
|
network: network,
|
||||||
memo: estimatedTx.memo,
|
memo: estimatedTx.memo,
|
||||||
outputOrdering: BitcoinOrdering.none,
|
outputOrdering: BitcoinOrdering.none,
|
||||||
enableRBF: true,
|
enableRBF: !estimatedTx.spendsUnconfirmedTX,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
txb = BitcoinTransactionBuilder(
|
txb = BitcoinTransactionBuilder(
|
||||||
|
@ -544,7 +576,7 @@ abstract class ElectrumWalletBase
|
||||||
network: network,
|
network: network,
|
||||||
memo: estimatedTx.memo,
|
memo: estimatedTx.memo,
|
||||||
outputOrdering: BitcoinOrdering.none,
|
outputOrdering: BitcoinOrdering.none,
|
||||||
enableRBF: true,
|
enableRBF: !estimatedTx.spendsUnconfirmedTX,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -727,6 +759,7 @@ abstract class ElectrumWalletBase
|
||||||
final tx = await fetchTransactionInfo(
|
final tx = await fetchTransactionInfo(
|
||||||
hash: coin.hash, height: 0, myAddresses: addressesSet);
|
hash: coin.hash, height: 0, myAddresses: addressesSet);
|
||||||
coin.isChange = tx?.direction == TransactionDirection.outgoing;
|
coin.isChange = tx?.direction == TransactionDirection.outgoing;
|
||||||
|
coin.confirmations = tx?.confirmations;
|
||||||
updatedUnspentCoins.add(coin);
|
updatedUnspentCoins.add(coin);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}))));
|
}))));
|
||||||
|
@ -751,6 +784,7 @@ abstract class ElectrumWalletBase
|
||||||
coin.isFrozen = coinInfo.isFrozen;
|
coin.isFrozen = coinInfo.isFrozen;
|
||||||
coin.isSending = coinInfo.isSending;
|
coin.isSending = coinInfo.isSending;
|
||||||
coin.note = coinInfo.note;
|
coin.note = coinInfo.note;
|
||||||
|
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||||
} else {
|
} else {
|
||||||
_addCoinInfo(coin);
|
_addCoinInfo(coin);
|
||||||
}
|
}
|
||||||
|
@ -1043,9 +1077,11 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
return Future.wait(addressesByType.map((addressRecord) async {
|
return Future.wait(addressesByType.map((addressRecord) async {
|
||||||
final history = await _fetchAddressHistory(addressRecord, addressesSet, currentHeight);
|
final history = await _fetchAddressHistory(addressRecord, addressesSet, currentHeight);
|
||||||
|
final balance = await electrumClient.getBalance(addressRecord.scriptHash!);
|
||||||
|
|
||||||
if (history.isNotEmpty) {
|
if (history.isNotEmpty) {
|
||||||
addressRecord.txCount = history.length;
|
addressRecord.txCount = history.length;
|
||||||
|
addressRecord.balance = balance['confirmed'] as int? ?? 0;
|
||||||
historiesWithDetails.addAll(history);
|
historiesWithDetails.addAll(history);
|
||||||
|
|
||||||
final matchedAddresses =
|
final matchedAddresses =
|
||||||
|
@ -1276,6 +1312,7 @@ class EstimatedTxResult {
|
||||||
required this.hasChange,
|
required this.hasChange,
|
||||||
required this.isSendAll,
|
required this.isSendAll,
|
||||||
this.memo,
|
this.memo,
|
||||||
|
required this.spendsUnconfirmedTX,
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<UtxoWithAddress> utxos;
|
final List<UtxoWithAddress> utxos;
|
||||||
|
@ -1285,6 +1322,7 @@ class EstimatedTxResult {
|
||||||
final bool hasChange;
|
final bool hasChange;
|
||||||
final bool isSendAll;
|
final bool isSendAll;
|
||||||
final String? memo;
|
final String? memo;
|
||||||
|
final bool spendsUnconfirmedTX;
|
||||||
}
|
}
|
||||||
|
|
||||||
BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
|
BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
|
||||||
|
|
|
@ -15,7 +15,9 @@ class BitcoinTransactionNoDustOnChangeException extends TransactionNoDustOnChang
|
||||||
BitcoinTransactionNoDustOnChangeException(super.max, super.min);
|
BitcoinTransactionNoDustOnChangeException(super.max, super.min);
|
||||||
}
|
}
|
||||||
|
|
||||||
class BitcoinTransactionCommitFailed extends TransactionCommitFailed {}
|
class BitcoinTransactionCommitFailed extends TransactionCommitFailed {
|
||||||
|
BitcoinTransactionCommitFailed({super.errorMessage});
|
||||||
|
}
|
||||||
|
|
||||||
class BitcoinTransactionCommitFailedDustChange extends TransactionCommitFailedDustChange {}
|
class BitcoinTransactionCommitFailedDustChange extends TransactionCommitFailedDustChange {}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
|
|
||||||
class LitecoinWalletService extends WalletService<
|
class LitecoinWalletService extends WalletService<
|
||||||
BitcoinNewWalletCredentials,
|
BitcoinNewWalletCredentials,
|
||||||
|
@ -100,7 +101,7 @@ class LitecoinWalletService extends WalletService<
|
||||||
@override
|
@override
|
||||||
Future<LitecoinWallet> restoreFromSeed(
|
Future<LitecoinWallet> restoreFromSeed(
|
||||||
BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
||||||
if (!validateMnemonic(credentials.mnemonic)) {
|
if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) {
|
||||||
throw LitecoinMnemonicIsIncorrectException();
|
throw LitecoinMnemonicIsIncorrectException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,9 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||||
if (error.contains("bad-txns-vout-negative")) {
|
if (error.contains("bad-txns-vout-negative")) {
|
||||||
throw BitcoinTransactionCommitFailedVoutNegative();
|
throw BitcoinTransactionCommitFailedVoutNegative();
|
||||||
}
|
}
|
||||||
|
throw BitcoinTransactionCommitFailed(errorMessage: error);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw BitcoinTransactionCommitFailed();
|
throw BitcoinTransactionCommitFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.2"
|
version: "2.5.0"
|
||||||
asn1lib:
|
asn1lib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -80,7 +80,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v2
|
ref: cake-update-v2
|
||||||
resolved-ref: "3fd81d238b990bb767fc7a4fdd5053a22a142e2e"
|
resolved-ref: "01d844a5f5a520a31df5254e34169af4664aa769"
|
||||||
url: "https://github.com/cake-tech/bitcoin_base.git"
|
url: "https://github.com/cake-tech/bitcoin_base.git"
|
||||||
source: git
|
source: git
|
||||||
version: "4.2.0"
|
version: "4.2.0"
|
||||||
|
@ -153,10 +153,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21"
|
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.8"
|
version: "2.4.9"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -177,10 +177,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6
|
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.9.0"
|
version: "8.9.2"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -309,10 +309,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_mobx
|
name: flutter_mobx
|
||||||
sha256: "4a5d062ff85ed3759f4aac6410ff0ffae32e324b2e71ca722ae1b37b32e865f4"
|
sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0+2"
|
version: "2.2.1+1"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -322,10 +322,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: frontend_server_client
|
name: frontend_server_client
|
||||||
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
|
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "4.0.0"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -466,10 +466,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mobx
|
name: mobx
|
||||||
sha256: "74ee54012dc7c1b3276eaa960a600a7418ef5f9997565deb8fca1fd88fb36b78"
|
sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0+1"
|
version: "2.3.3+2"
|
||||||
mobx_codegen:
|
mobx_codegen:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -570,10 +570,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.7.4"
|
version: "3.8.0"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -586,10 +586,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: provider
|
name: provider
|
||||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.1"
|
version: "6.1.2"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -62,7 +62,9 @@ class PendingBitcoinCashTransaction with PendingTransaction {
|
||||||
if (error.contains("bad-txns-vout-negative")) {
|
if (error.contains("bad-txns-vout-negative")) {
|
||||||
throw BitcoinTransactionCommitFailedVoutNegative();
|
throw BitcoinTransactionCommitFailedVoutNegative();
|
||||||
}
|
}
|
||||||
|
throw BitcoinTransactionCommitFailed(errorMessage: error);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw BitcoinTransactionCommitFailed();
|
throw BitcoinTransactionCommitFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,11 @@ class TransactionNoDustOnChangeException implements Exception {
|
||||||
final String min;
|
final String min;
|
||||||
}
|
}
|
||||||
|
|
||||||
class TransactionCommitFailed implements Exception {}
|
class TransactionCommitFailed implements Exception {
|
||||||
|
final String? errorMessage;
|
||||||
|
|
||||||
|
TransactionCommitFailed({this.errorMessage});
|
||||||
|
}
|
||||||
|
|
||||||
class TransactionCommitFailedDustChange implements Exception {}
|
class TransactionCommitFailedDustChange implements Exception {}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ class Unspent {
|
||||||
bool isChange;
|
bool isChange;
|
||||||
bool isSending;
|
bool isSending;
|
||||||
bool isFrozen;
|
bool isFrozen;
|
||||||
|
int? confirmations;
|
||||||
String note;
|
String note;
|
||||||
|
|
||||||
bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc');
|
bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc');
|
||||||
|
|
|
@ -416,13 +416,14 @@ class CWBitcoin extends Bitcoin {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount,
|
int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount,
|
||||||
{int? size}) {
|
{int? outputsCount, int? size}) {
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
return bitcoinWallet.feeAmountWithFeeRate(
|
return bitcoinWallet.calculateEstimatedFeeWithFeeRate(
|
||||||
feeRate,
|
feeRate,
|
||||||
inputsCount,
|
amount,
|
||||||
outputsCount,
|
outputsCount: outputsCount,
|
||||||
|
size: size,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,28 @@
|
||||||
import 'package:cake_wallet/core/generate_wallet_password.dart';
|
import 'package:cake_wallet/core/generate_wallet_password.dart';
|
||||||
import 'package:cake_wallet/core/key_service.dart';
|
import 'package:cake_wallet/core/key_service.dart';
|
||||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||||
|
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||||
|
import 'package:cw_core/cake_hive.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_service.dart';
|
import 'package:cw_core/wallet_service.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class WalletLoadingService {
|
class WalletLoadingService {
|
||||||
WalletLoadingService(
|
WalletLoadingService(this.sharedPreferences, this.keyService, this.walletServiceFactory);
|
||||||
this.sharedPreferences, this.keyService, this.walletServiceFactory);
|
|
||||||
|
|
||||||
final SharedPreferences sharedPreferences;
|
final SharedPreferences sharedPreferences;
|
||||||
final KeyService keyService;
|
final KeyService keyService;
|
||||||
final WalletService Function(WalletType type) walletServiceFactory;
|
final WalletService Function(WalletType type) walletServiceFactory;
|
||||||
|
|
||||||
Future<void> renameWallet(
|
Future<void> renameWallet(WalletType type, String name, String newName) async {
|
||||||
WalletType type, String name, String newName) async {
|
|
||||||
final walletService = walletServiceFactory.call(type);
|
final walletService = walletServiceFactory.call(type);
|
||||||
final password = await keyService.getWalletPassword(walletName: name);
|
final password = await keyService.getWalletPassword(walletName: name);
|
||||||
|
|
||||||
// Save the current wallet's password to the new wallet name's key
|
// Save the current wallet's password to the new wallet name's key
|
||||||
await keyService.saveWalletPassword(
|
await keyService.saveWalletPassword(walletName: newName, password: password);
|
||||||
walletName: newName, password: password);
|
|
||||||
// Delete previous wallet name from keyService to keep only new wallet's name
|
// Delete previous wallet name from keyService to keep only new wallet's name
|
||||||
// otherwise keeps duplicate (old and new names)
|
// otherwise keeps duplicate (old and new names)
|
||||||
await keyService.deleteWalletPassword(walletName: name);
|
await keyService.deleteWalletPassword(walletName: name);
|
||||||
|
@ -38,6 +39,7 @@ class WalletLoadingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<WalletBase> load(WalletType type, String name) async {
|
Future<WalletBase> load(WalletType type, String name) async {
|
||||||
|
try {
|
||||||
final walletService = walletServiceFactory.call(type);
|
final walletService = walletServiceFactory.call(type);
|
||||||
final password = await keyService.getWalletPassword(walletName: name);
|
final password = await keyService.getWalletPassword(walletName: name);
|
||||||
final wallet = await walletService.openWallet(name, password);
|
final wallet = await walletService.openWallet(name, password);
|
||||||
|
@ -47,6 +49,33 @@ class WalletLoadingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
} catch (error, stack) {
|
||||||
|
ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
|
||||||
|
|
||||||
|
// try opening another wallet that is not corrupted to give user access to the app
|
||||||
|
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
|
||||||
|
|
||||||
|
for (var walletInfo in walletInfoSource.values) {
|
||||||
|
try {
|
||||||
|
final walletService = walletServiceFactory.call(walletInfo.type);
|
||||||
|
final password = await keyService.getWalletPassword(walletName: walletInfo.name);
|
||||||
|
final wallet = await walletService.openWallet(walletInfo.name, password);
|
||||||
|
|
||||||
|
if (walletInfo.type == WalletType.monero) {
|
||||||
|
await updateMoneroWalletPassword(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
|
await sharedPreferences.setString(PreferencesKey.currentWalletName, wallet.name);
|
||||||
|
await sharedPreferences.setInt(
|
||||||
|
PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if all user's wallets are corrupted throw exception
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
|
Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
|
||||||
|
@ -61,11 +90,9 @@ class WalletLoadingService {
|
||||||
// Save new generated password with backup key for case where
|
// Save new generated password with backup key for case where
|
||||||
// wallet will change password, but it will fail to update in secure storage
|
// wallet will change password, but it will fail to update in secure storage
|
||||||
final bakWalletName = '#__${wallet.name}_bak__#';
|
final bakWalletName = '#__${wallet.name}_bak__#';
|
||||||
await keyService.saveWalletPassword(
|
await keyService.saveWalletPassword(walletName: bakWalletName, password: password);
|
||||||
walletName: bakWalletName, password: password);
|
|
||||||
await wallet.changePassword(password);
|
await wallet.changePassword(password);
|
||||||
await keyService.saveWalletPassword(
|
await keyService.saveWalletPassword(walletName: wallet.name, password: password);
|
||||||
walletName: wallet.name, password: password);
|
|
||||||
isPasswordUpdated = true;
|
isPasswordUpdated = true;
|
||||||
await sharedPreferences.setBool(key, isPasswordUpdated);
|
await sharedPreferences.setBool(key, isPasswordUpdated);
|
||||||
}
|
}
|
||||||
|
|
|
@ -379,19 +379,23 @@ class WalletRestorePage extends BasePage {
|
||||||
dInfo = derivations[derivationWithHistoryIndex];
|
dInfo = derivations[derivationWithHistoryIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the default derivation for this wallet type:
|
||||||
if (dInfo == null) {
|
if (dInfo == null) {
|
||||||
walletRestoreViewModel.state = InitialExecutionState();
|
// we only return 1 derivation if we're pretty sure we know which one to use:
|
||||||
return;
|
if (derivations.length == 1) {
|
||||||
|
dInfo = derivations.first;
|
||||||
|
} else {
|
||||||
|
// if we have multiple possible derivations, and none have histories
|
||||||
|
// we just default to the most common one:
|
||||||
|
dInfo = walletRestoreViewModel.getMostCommonDerivation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.derivationInfo = dInfo;
|
this.derivationInfo = dInfo;
|
||||||
|
|
||||||
// get the default derivation for this wallet type:
|
|
||||||
if (this.derivationInfo == null) {
|
if (this.derivationInfo == null) {
|
||||||
this.derivationInfo = walletRestoreViewModel.getMostCommonDerivation();
|
|
||||||
}
|
|
||||||
|
|
||||||
walletRestoreViewModel.state = InitialExecutionState();
|
walletRestoreViewModel.state = InitialExecutionState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
walletRestoreViewModel.create(options: _credentials());
|
walletRestoreViewModel.create(options: _credentials());
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ class SendPage extends BasePage {
|
||||||
AppBarStyle get appBarStyle => AppBarStyle.transparent;
|
AppBarStyle get appBarStyle => AppBarStyle.transparent;
|
||||||
|
|
||||||
double _sendCardHeight(BuildContext context) {
|
double _sendCardHeight(BuildContext context) {
|
||||||
double initialHeight = 450;
|
double initialHeight = 480;
|
||||||
if (sendViewModel.hasCoinControl) {
|
if (sendViewModel.hasCoinControl) {
|
||||||
initialHeight += 35;
|
initialHeight += 35;
|
||||||
}
|
}
|
||||||
|
|
|
@ -343,8 +343,10 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (this.mounted) {
|
||||||
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
|
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
conditionToDetermineIfToUse2FA:
|
conditionToDetermineIfToUse2FA:
|
||||||
widget.walletListViewModel.shouldRequireTOTP2FAForAccessingWallet,
|
widget.walletListViewModel.shouldRequireTOTP2FAForAccessingWallet,
|
||||||
|
|
|
@ -126,8 +126,8 @@ abstract class OutputBase with Store {
|
||||||
|
|
||||||
if (_wallet.type == WalletType.bitcoin) {
|
if (_wallet.type == WalletType.bitcoin) {
|
||||||
if (_settingsStore.priority[_wallet.type] == bitcoin!.getBitcoinTransactionPriorityCustom()) {
|
if (_settingsStore.priority[_wallet.type] == bitcoin!.getBitcoinTransactionPriorityCustom()) {
|
||||||
fee = bitcoin!.getFeeAmountWithFeeRate(
|
fee = bitcoin!.getEstimatedFeeWithFeeRate(_wallet,
|
||||||
_settingsStore.customBitcoinFeeRate, formattedCryptoAmount, 1, 1);
|
_settingsStore.customBitcoinFeeRate,formattedCryptoAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
|
return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
|
||||||
|
|
|
@ -562,7 +562,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
return S.current.tx_no_dust_exception;
|
return S.current.tx_no_dust_exception;
|
||||||
}
|
}
|
||||||
if (error is TransactionCommitFailed) {
|
if (error is TransactionCommitFailed) {
|
||||||
return S.current.tx_commit_failed;
|
return "${S.current.tx_commit_failed}${error.errorMessage != null ? "\n\n${error.errorMessage}" : ""}";
|
||||||
}
|
}
|
||||||
if (error is TransactionCommitFailedDustChange) {
|
if (error is TransactionCommitFailedDustChange) {
|
||||||
return S.current.tx_rejected_dust_change;
|
return S.current.tx_rejected_dust_change;
|
||||||
|
|
|
@ -390,11 +390,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
|
|
||||||
String setNewFee({double? value, required TransactionPriority priority}) {
|
String setNewFee({double? value, required TransactionPriority priority}) {
|
||||||
newFee = priority == bitcoin!.getBitcoinTransactionPriorityCustom() && value != null
|
newFee = priority == bitcoin!.getBitcoinTransactionPriorityCustom() && value != null
|
||||||
? bitcoin!.getFeeAmountWithFeeRate(
|
? bitcoin!.getEstimatedFeeWithFeeRate(wallet, value.round(), transactionInfo.amount)
|
||||||
wallet,
|
|
||||||
value.round(),
|
|
||||||
transactionInfo.inputAddresses?.length ?? 1,
|
|
||||||
transactionInfo.outputAddresses?.length ?? 1)
|
|
||||||
: bitcoin!.getFeeAmountForPriority(
|
: bitcoin!.getFeeAmountForPriority(
|
||||||
wallet,
|
wallet,
|
||||||
priority,
|
priority,
|
||||||
|
|
|
@ -99,14 +99,14 @@ abstract class WalletCreationVMBase with Store {
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
return DerivationInfo(
|
return DerivationInfo(
|
||||||
derivationType: DerivationType.electrum,
|
derivationType: DerivationType.electrum,
|
||||||
derivationPath: "m/0'/0",
|
derivationPath: "m/0'",
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DerivationInfo? getMostCommonDerivation() {
|
DerivationInfo? getCommonRestoreDerivation() {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case WalletType.nano:
|
case WalletType.nano:
|
||||||
return DerivationInfo(
|
return DerivationInfo(
|
||||||
|
@ -115,8 +115,10 @@ abstract class WalletCreationVMBase with Store {
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
return DerivationInfo(
|
return DerivationInfo(
|
||||||
derivationType: DerivationType.electrum,
|
derivationType: DerivationType.bip39,
|
||||||
derivationPath: "m/84'/0'/0'/0",
|
derivationPath: "m/84'/0'/0'/0",
|
||||||
|
description: "Standard BIP84 native segwit",
|
||||||
|
scriptType: "p2wpkh",
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -180,7 +180,7 @@ abstract class Bitcoin {
|
||||||
Future<bool> canReplaceByFee(Object wallet, String transactionHash);
|
Future<bool> canReplaceByFee(Object wallet, String transactionHash);
|
||||||
Future<bool> isChangeSufficientForFee(Object wallet, String txId, String newFee);
|
Future<bool> isChangeSufficientForFee(Object wallet, String txId, String newFee);
|
||||||
int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size});
|
int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size});
|
||||||
int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size});
|
int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount, {int? outputsCount, int? size});
|
||||||
int getMaxCustomFeeRate(Object wallet);
|
int getMaxCustomFeeRate(Object wallet);
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
Loading…
Reference in a new issue