mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 20:39:51 +00:00
Cw 453 (#1306)
* feat: rebase btc-addr-types, migrate to bitcoin_base * feat: allow scanning elect-rs using get_tweaks * feat: scanning and adding addresses working with getTweaks, add btc SP address type * chore: pubspec.lock * chore: pubspec.lock * fix: scan when switching, fix multiple unspents in same tx * fix: initial scan * fix: initial scan * fix: scanning issues * fix: sync, storing silent unspents * chore: deps * fix: label issues, clear spent utxo * chore: deps * fix: build * fix: missing types * feat: new electrs API & changes, fixes for last block scanning * feat: Scan Silent Payments homepage toggle * chore: build configure * feat: generic fixes, testnet UI improvements, useSSL on bitcoin nodes * fix: invalid Object in sendData * feat: improve addresses page & address book displays * feat: silent payments labeled addresses disclaimer * fix: missing i18n * chore: print * feat: single block scan, rescan by date working for btc mainnet * feat: new cake features page replace market page, move sp scan toggle, auto switch node pop up alert * feat: delete silent addresses * fix: red dot in non ssl nodes * fix: inconsistent connection states, fix tx history * fix: tx & balance displays, cpfp sending * feat: new rust lib * chore: node path * fix: check node based on network * fix: missing txcount from addresses * style: padding in feature page cards * fix: restore not getting all wallet addresses by type * fix: auto switch node broken * fix: silent payment txs not being restored * feat: change scanning to subscription model, sync improvements * fix: scan re-subscription * fix: default nodes * fix: improve scanning by date, fix single block scan * refactor: common function for input tx selection * fix: nodes & build * fix: send all with multiple outs * refactor: unchanged file * Update pr_test_build.yml * chore: upgrade * chore: merge changes * refactor: unchanged files [skip ci] * fix: scan fixes, add date, allow sending while scanning * feat: sync fixes, sp settings * feat: fix resyncing * fix: date from height logic, status disconnected & chain tip get * fix: params * feat: electrum migration if using cake electrum * fix nodes update versions * re-enable tron * update sp_scanner to work on iOS [skip ci] * fix: wrong socket for old electrum nodes * Fix unchecked wallet type call * fix: double balance * feat: node domain * fix: menu name * fix: update tip on set scanning * fix: connection switching back and forth * feat: check if node is electrs, and supports sp * chore: fix build * minor enhancements * fixes and enhancements * solve conflicts with main * fix: status toggle * minor enhancement * Monero.com fixes * update sp_scanner to include windows and linux --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
faa49d21e8
commit
96b9b60f50
122 changed files with 3889 additions and 1252 deletions
|
@ -104,6 +104,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
|||
CryptoCurrency.digibyte,
|
||||
CryptoCurrency.usdtSol,
|
||||
CryptoCurrency.usdcTrc20,
|
||||
CryptoCurrency.tbtc,
|
||||
];
|
||||
|
||||
static const havenCurrencies = [
|
||||
|
@ -218,7 +219,8 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
|||
static const kaspa = CryptoCurrency(title: 'KAS', fullName: 'Kaspa', raw: 89, name: 'kas', iconPath: 'assets/images/kaspa_icon.png', decimals: 8);
|
||||
static const digibyte = CryptoCurrency(title: 'DGB', fullName: 'DigiByte', raw: 90, name: 'dgb', iconPath: 'assets/images/digibyte.png', decimals: 8);
|
||||
static const usdtSol = CryptoCurrency(title: 'USDT', tag: 'SOL', fullName: 'USDT Tether', raw: 91, name: 'usdtsol', iconPath: 'assets/images/usdt_icon.png', decimals: 6);
|
||||
static const usdcTrc20 = CryptoCurrency(title: 'USDC', tag: 'TRX', fullName: 'USDC Coin', raw: 92, name: 'usdctrc20', iconPath: 'assets/images/usdc_icon.png', decimals: 6);
|
||||
static const usdcTrc20 = CryptoCurrency(title: 'USDC', tag: 'TRX', fullName: 'USDC Coin', raw: 92, name: 'usdctrc20', iconPath: 'assets/images/usdc_icon.png', decimals: 6);
|
||||
static const tbtc = CryptoCurrency(title: 'tBTC', fullName: 'Testnet Bitcoin', raw: 93, name: 'tbtc', iconPath: 'assets/images/tbtc.png', decimals: 8);
|
||||
|
||||
|
||||
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
CryptoCurrency currencyForWalletType(WalletType type) {
|
||||
CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
|
||||
switch (type) {
|
||||
case WalletType.bitcoin:
|
||||
if (isTestnet == true) {
|
||||
return CryptoCurrency.tbtc;
|
||||
}
|
||||
return CryptoCurrency.btc;
|
||||
case WalletType.monero:
|
||||
return CryptoCurrency.xmr;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class TransactionWrongBalanceException implements Exception {
|
||||
TransactionWrongBalanceException(this.currency);
|
||||
TransactionWrongBalanceException(this.currency, {this.amount});
|
||||
|
||||
final CryptoCurrency currency;
|
||||
final int? amount;
|
||||
}
|
||||
|
||||
class TransactionNoInputsException implements Exception {}
|
||||
|
@ -32,3 +33,7 @@ class TransactionCommitFailedDustOutput implements Exception {}
|
|||
class TransactionCommitFailedDustOutputSendAll implements Exception {}
|
||||
|
||||
class TransactionCommitFailedVoutNegative implements Exception {}
|
||||
|
||||
class TransactionCommitFailedBIP68Final implements Exception {}
|
||||
|
||||
class TransactionInputNotSupported implements Exception {}
|
||||
|
|
|
@ -242,3 +242,57 @@ Future<int> getHavenCurrentHeight() async {
|
|||
throw Exception('Failed to load current blockchain height');
|
||||
}
|
||||
}
|
||||
|
||||
// Data taken from https://timechaincalendar.com/
|
||||
const bitcoinDates = {
|
||||
"2024-05": 841590,
|
||||
"2024-04": 837182,
|
||||
"2024-03": 832623,
|
||||
"2024-02": 828319,
|
||||
"2024-01": 823807,
|
||||
"2023-12": 819206,
|
||||
"2023-11": 814765,
|
||||
"2023-10": 810098,
|
||||
"2023-09": 805675,
|
||||
"2023-08": 801140,
|
||||
"2023-07": 796640,
|
||||
"2023-06": 792330,
|
||||
"2023-05": 787733,
|
||||
"2023-04": 783403,
|
||||
"2023-03": 778740,
|
||||
"2023-02": 774525,
|
||||
"2023-01": 769810,
|
||||
};
|
||||
|
||||
int getBitcoinHeightByDate({required DateTime date}) {
|
||||
String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}';
|
||||
final closestKey = bitcoinDates.keys
|
||||
.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => bitcoinDates.keys.last);
|
||||
final beginningBlock = bitcoinDates[dateKey] ?? bitcoinDates[closestKey]!;
|
||||
|
||||
final startOfMonth = DateTime(date.year, date.month);
|
||||
final daysDifference = date.difference(startOfMonth).inDays;
|
||||
|
||||
// approximately 6 blocks per hour, 24 hours per day
|
||||
int estimatedBlocksSinceStartOfMonth = (daysDifference * 24 * 6);
|
||||
|
||||
return beginningBlock + estimatedBlocksSinceStartOfMonth;
|
||||
}
|
||||
|
||||
DateTime getDateByBitcoinHeight(int height) {
|
||||
final closestEntry = bitcoinDates.entries
|
||||
.lastWhere((entry) => entry.value >= height, orElse: () => bitcoinDates.entries.first);
|
||||
final beginningBlock = closestEntry.value;
|
||||
|
||||
final startOfMonth = formatMapKey(closestEntry.key);
|
||||
final blocksDifference = height - beginningBlock;
|
||||
final hoursDifference = blocksDifference / 5.5;
|
||||
|
||||
final estimatedDate = startOfMonth.add(Duration(hours: hoursDifference.ceil()));
|
||||
|
||||
if (estimatedDate.isAfter(DateTime.now())) {
|
||||
return DateTime.now();
|
||||
}
|
||||
|
||||
return estimatedDate;
|
||||
}
|
||||
|
|
|
@ -244,8 +244,12 @@ class Node extends HiveObject with Keyable {
|
|||
|
||||
Future<bool> requestElectrumServer() async {
|
||||
try {
|
||||
await SecureSocket.connect(uri.host, uri.port,
|
||||
timeout: Duration(seconds: 5), onBadCertificate: (_) => true);
|
||||
if (useSSL == true) {
|
||||
await SecureSocket.connect(uri.host, uri.port,
|
||||
timeout: Duration(seconds: 5), onBadCertificate: (_) => true);
|
||||
} else {
|
||||
await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5));
|
||||
}
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
|
|
|
@ -14,6 +14,16 @@ class SyncingSyncStatus extends SyncStatus {
|
|||
|
||||
@override
|
||||
String toString() => '$blocksLeft';
|
||||
|
||||
factory SyncingSyncStatus.fromHeightValues(int chainTip, int initialSyncHeight, int syncHeight) {
|
||||
final track = chainTip - initialSyncHeight;
|
||||
final diff = track - (chainTip - syncHeight);
|
||||
final ptc = diff <= 0 ? 0.0 : diff / track;
|
||||
final left = chainTip - syncHeight;
|
||||
|
||||
// sum 1 because if at the chain tip, will say "0 blocks left"
|
||||
return SyncingSyncStatus(left + 1, ptc);
|
||||
}
|
||||
}
|
||||
|
||||
class SyncedSyncStatus extends SyncStatus {
|
||||
|
@ -21,6 +31,17 @@ class SyncedSyncStatus extends SyncStatus {
|
|||
double progress() => 1.0;
|
||||
}
|
||||
|
||||
class SyncedTipSyncStatus extends SyncedSyncStatus {
|
||||
SyncedTipSyncStatus(this.tip);
|
||||
|
||||
final int tip;
|
||||
}
|
||||
|
||||
class SyncronizingSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 0.0;
|
||||
}
|
||||
|
||||
class NotConnectedSyncStatus extends SyncStatus {
|
||||
const NotConnectedSyncStatus();
|
||||
|
||||
|
@ -33,10 +54,7 @@ class AttemptingSyncStatus extends SyncStatus {
|
|||
double progress() => 0.0;
|
||||
}
|
||||
|
||||
class FailedSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 1.0;
|
||||
}
|
||||
class FailedSyncStatus extends NotConnectedSyncStatus {}
|
||||
|
||||
class ConnectingSyncStatus extends SyncStatus {
|
||||
@override
|
||||
|
@ -48,7 +66,14 @@ class ConnectedSyncStatus extends SyncStatus {
|
|||
double progress() => 0.0;
|
||||
}
|
||||
|
||||
class LostConnectionSyncStatus extends SyncStatus {
|
||||
class UnsupportedSyncStatus extends NotConnectedSyncStatus {}
|
||||
|
||||
class TimedOutSyncStatus extends NotConnectedSyncStatus {
|
||||
@override
|
||||
double progress() => 1.0;
|
||||
}
|
||||
String toString() => 'Timed out';
|
||||
}
|
||||
|
||||
class LostConnectionSyncStatus extends NotConnectedSyncStatus {
|
||||
@override
|
||||
String toString() => 'Reconnecting';
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ class UnspentCoinsInfo extends HiveObject {
|
|||
required this.value,
|
||||
this.keyImage = null,
|
||||
this.isChange = false,
|
||||
this.accountIndex = 0
|
||||
this.accountIndex = 0,
|
||||
this.isSilentPayment = false,
|
||||
});
|
||||
|
||||
static const typeId = UNSPENT_COINS_INFO_TYPE_ID;
|
||||
|
@ -49,13 +50,16 @@ class UnspentCoinsInfo extends HiveObject {
|
|||
|
||||
@HiveField(8, defaultValue: null)
|
||||
String? keyImage;
|
||||
|
||||
|
||||
@HiveField(9, defaultValue: false)
|
||||
bool isChange;
|
||||
|
||||
@HiveField(10, defaultValue: 0)
|
||||
int accountIndex;
|
||||
|
||||
@HiveField(11, defaultValue: false)
|
||||
bool? isSilentPayment;
|
||||
|
||||
String get note => noteRaw ?? '';
|
||||
|
||||
set note(String value) => noteRaw = value;
|
||||
|
|
|
@ -17,5 +17,6 @@ class Unspent {
|
|||
int? confirmations;
|
||||
String note;
|
||||
|
||||
bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc');
|
||||
bool get isP2wpkh =>
|
||||
address.startsWith('bc') || address.startsWith('tb') || address.startsWith('ltc');
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
|||
|
||||
WalletType get type => walletInfo.type;
|
||||
|
||||
CryptoCurrency get currency => currencyForWalletType(type);
|
||||
CryptoCurrency get currency => currencyForWalletType(type, isTestnet: isTestnet);
|
||||
|
||||
String get id => walletInfo.id;
|
||||
|
||||
|
|
|
@ -66,21 +66,21 @@ class DerivationInfo extends HiveObject {
|
|||
@HiveType(typeId: WalletInfo.typeId)
|
||||
class WalletInfo extends HiveObject {
|
||||
WalletInfo(
|
||||
this.id,
|
||||
this.name,
|
||||
this.type,
|
||||
this.isRecovery,
|
||||
this.restoreHeight,
|
||||
this.timestamp,
|
||||
this.dirPath,
|
||||
this.path,
|
||||
this.address,
|
||||
this.yatEid,
|
||||
this.yatLastUsedAddressRaw,
|
||||
this.showIntroCakePayCard,
|
||||
this.derivationInfo,
|
||||
this.hardwareWalletType,
|
||||
): _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||
this.id,
|
||||
this.name,
|
||||
this.type,
|
||||
this.isRecovery,
|
||||
this.restoreHeight,
|
||||
this.timestamp,
|
||||
this.dirPath,
|
||||
this.path,
|
||||
this.address,
|
||||
this.yatEid,
|
||||
this.yatLastUsedAddressRaw,
|
||||
this.showIntroCakePayCard,
|
||||
this.derivationInfo,
|
||||
this.hardwareWalletType,
|
||||
) : _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||
|
||||
factory WalletInfo.external({
|
||||
required String id,
|
||||
|
@ -207,4 +207,9 @@ class WalletInfo extends HiveObject {
|
|||
Stream<String> get yatLastUsedAddressStream => _yatLastUsedAddressController.stream;
|
||||
|
||||
StreamController<String> _yatLastUsedAddressController;
|
||||
|
||||
Future<void> updateRestoreHeight(int height) async {
|
||||
restoreHeight = height;
|
||||
await save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,11 +173,14 @@ String walletTypeToDisplayName(WalletType type) {
|
|||
}
|
||||
}
|
||||
|
||||
CryptoCurrency walletTypeToCryptoCurrency(WalletType type) {
|
||||
CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false}) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return CryptoCurrency.xmr;
|
||||
case WalletType.bitcoin:
|
||||
if (isTestnet) {
|
||||
return CryptoCurrency.tbtc;
|
||||
}
|
||||
return CryptoCurrency.btc;
|
||||
case WalletType.litecoin:
|
||||
return CryptoCurrency.ltc;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue