mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-22 23:28:48 +00:00
various address and SWB fixes, as well as some electrumx_interface unused function cleanup
This commit is contained in:
parent
f1f8a0c49a
commit
f319aaf594
18 changed files with 153 additions and 418 deletions
|
@ -27,6 +27,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
import 'package:stackwallet/utilities/show_loading.dart';
|
import 'package:stackwallet/utilities/show_loading.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart';
|
||||||
import 'package:stackwallet/widgets/background.dart';
|
import 'package:stackwallet/widgets/background.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
|
@ -98,15 +99,21 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
||||||
final walletId = widget.routeOnSuccessArguments as String;
|
final walletId = widget.routeOnSuccessArguments as String;
|
||||||
|
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
if (wallet.info.coin == Coin.monero) {
|
final Future<void> loadFuture;
|
||||||
|
if (wallet is CwBasedInterface) {
|
||||||
|
loadFuture =
|
||||||
|
wallet.init().then((value) async => await (wallet).open());
|
||||||
|
} else {
|
||||||
|
loadFuture = wallet.init();
|
||||||
|
}
|
||||||
|
|
||||||
await showLoading(
|
await showLoading(
|
||||||
opaqueBG: true,
|
opaqueBG: true,
|
||||||
whileFuture: wallet.init(),
|
whileFuture: loadFuture,
|
||||||
context: context,
|
context: context,
|
||||||
message: "Loading ${wallet.info.coin.prettyName} wallet...",
|
message: "Loading ${wallet.info.coin.prettyName} wallet...",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
unawaited(
|
unawaited(
|
||||||
|
|
|
@ -368,7 +368,7 @@ abstract class SWB {
|
||||||
return backupJson;
|
return backupJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool> asyncRestore(
|
static Future<bool> _asyncRestore(
|
||||||
Tuple2<dynamic, WalletInfo> tuple,
|
Tuple2<dynamic, WalletInfo> tuple,
|
||||||
Prefs prefs,
|
Prefs prefs,
|
||||||
NodeService nodeService,
|
NodeService nodeService,
|
||||||
|
@ -757,7 +757,7 @@ abstract class SWB {
|
||||||
)) {
|
)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final bools = await asyncRestore(
|
final bools = await _asyncRestore(
|
||||||
tuple,
|
tuple,
|
||||||
_prefs,
|
_prefs,
|
||||||
nodeService,
|
nodeService,
|
||||||
|
@ -796,10 +796,10 @@ abstract class SWB {
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.instance.log("done with SWB restore", level: LogLevel.Warning);
|
Logging.instance.log("done with SWB restore", level: LogLevel.Warning);
|
||||||
if (Util.isDesktop) {
|
|
||||||
await Wallets.sharedInstance
|
await Wallets.sharedInstance
|
||||||
.loadAfterStackRestore(_prefs, uiState?.wallets ?? []);
|
.loadAfterStackRestore(_prefs, uiState?.wallets ?? [], Util.isDesktop);
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,7 +218,7 @@ class _StackRestoreProgressViewState
|
||||||
ref.read(pWallets).loadAfterStackRestore(
|
ref.read(pWallets).loadAfterStackRestore(
|
||||||
ref.read(prefsChangeNotifierProvider),
|
ref.read(prefsChangeNotifierProvider),
|
||||||
ref.read(stackRestoringUIStateProvider).wallets,
|
ref.read(stackRestoringUIStateProvider).wallets,
|
||||||
);
|
Util.isDesktop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -115,17 +115,21 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
await wallet.init();
|
|
||||||
|
final Future<void> loadFuture;
|
||||||
if (wallet is CwBasedInterface) {
|
if (wallet is CwBasedInterface) {
|
||||||
if (mounted) {
|
loadFuture =
|
||||||
|
wallet.init().then((value) async => await (wallet).open());
|
||||||
|
} else {
|
||||||
|
loadFuture = wallet.init();
|
||||||
|
}
|
||||||
await showLoading(
|
await showLoading(
|
||||||
whileFuture: wallet.open(),
|
whileFuture: loadFuture,
|
||||||
context: context,
|
context: context,
|
||||||
message: 'Opening ${wallet.info.name}',
|
message: 'Opening ${wallet.info.name}',
|
||||||
isDesktop: Util.isDesktop,
|
isDesktop: Util.isDesktop,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
if (Util.isDesktop) {
|
if (Util.isDesktop) {
|
||||||
await Navigator.of(context).pushNamed(
|
await Navigator.of(context).pushNamed(
|
||||||
|
|
|
@ -63,17 +63,19 @@ class WalletListItem extends ConsumerWidget {
|
||||||
.read(pWallets)
|
.read(pWallets)
|
||||||
.wallets
|
.wallets
|
||||||
.firstWhere((e) => e.info.coin == coin);
|
.firstWhere((e) => e.info.coin == coin);
|
||||||
await wallet.init();
|
final Future<void> loadFuture;
|
||||||
if (wallet is CwBasedInterface) {
|
if (wallet is CwBasedInterface) {
|
||||||
if (context.mounted) {
|
loadFuture =
|
||||||
|
wallet.init().then((value) async => await (wallet).open());
|
||||||
|
} else {
|
||||||
|
loadFuture = wallet.init();
|
||||||
|
}
|
||||||
await showLoading(
|
await showLoading(
|
||||||
whileFuture: wallet.open(),
|
whileFuture: loadFuture,
|
||||||
context: context,
|
context: context,
|
||||||
message: 'Opening ${wallet.info.name}',
|
message: 'Opening ${wallet.info.name}',
|
||||||
isDesktop: Util.isDesktop,
|
isDesktop: Util.isDesktop,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
unawaited(
|
unawaited(
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
|
|
|
@ -80,17 +80,20 @@ class CoinWalletsTable extends ConsumerWidget {
|
||||||
|
|
||||||
final wallet =
|
final wallet =
|
||||||
ref.read(pWallets).getWallet(walletIds[i]);
|
ref.read(pWallets).getWallet(walletIds[i]);
|
||||||
await wallet.init();
|
final Future<void> loadFuture;
|
||||||
if (wallet is CwBasedInterface) {
|
if (wallet is CwBasedInterface) {
|
||||||
if (context.mounted) {
|
loadFuture = wallet
|
||||||
|
.init()
|
||||||
|
.then((value) async => await (wallet).open());
|
||||||
|
} else {
|
||||||
|
loadFuture = wallet.init();
|
||||||
|
}
|
||||||
await showLoading(
|
await showLoading(
|
||||||
whileFuture: wallet.open(),
|
whileFuture: loadFuture,
|
||||||
context: context,
|
context: context,
|
||||||
message: 'Opening ${wallet.info.name}',
|
message: 'Opening ${wallet.info.name}',
|
||||||
isDesktop: Util.isDesktop,
|
isDesktop: Util.isDesktop,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
await Navigator.of(context).pushNamed(
|
await Navigator.of(context).pushNamed(
|
||||||
|
|
|
@ -25,6 +25,7 @@ import 'package:stackwallet/utilities/prefs.dart';
|
||||||
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/epiccash_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/impl/epiccash_wallet.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet.dart';
|
import 'package:stackwallet/wallets/wallet/wallet.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart';
|
||||||
|
|
||||||
class Wallets {
|
class Wallets {
|
||||||
Wallets._private();
|
Wallets._private();
|
||||||
|
@ -192,8 +193,7 @@ class Wallets {
|
||||||
final shouldSetAutoSync = shouldAutoSyncAll ||
|
final shouldSetAutoSync = shouldAutoSyncAll ||
|
||||||
walletIdsToEnableAutoSync.contains(walletInfo.walletId);
|
walletIdsToEnableAutoSync.contains(walletInfo.walletId);
|
||||||
|
|
||||||
if (walletInfo.coin == Coin.monero ||
|
if (wallet is CwBasedInterface) {
|
||||||
walletInfo.coin == Coin.wownero) {
|
|
||||||
// walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
|
// walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
|
||||||
} else {
|
} else {
|
||||||
walletInitFutures.add(wallet.init().then((_) {
|
walletInitFutures.add(wallet.init().then((_) {
|
||||||
|
@ -230,6 +230,7 @@ class Wallets {
|
||||||
Future<void> loadAfterStackRestore(
|
Future<void> loadAfterStackRestore(
|
||||||
Prefs prefs,
|
Prefs prefs,
|
||||||
List<Wallet> wallets,
|
List<Wallet> wallets,
|
||||||
|
bool isDesktop,
|
||||||
) async {
|
) async {
|
||||||
List<Future<void>> walletInitFutures = [];
|
List<Future<void>> walletInitFutures = [];
|
||||||
List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = [];
|
List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = [];
|
||||||
|
@ -259,8 +260,8 @@ class Wallets {
|
||||||
final shouldSetAutoSync = shouldAutoSyncAll ||
|
final shouldSetAutoSync = shouldAutoSyncAll ||
|
||||||
walletIdsToEnableAutoSync.contains(wallet.walletId);
|
walletIdsToEnableAutoSync.contains(wallet.walletId);
|
||||||
|
|
||||||
if (wallet.info.coin == Coin.monero ||
|
if (isDesktop) {
|
||||||
wallet.info.coin == Coin.wownero) {
|
if (wallet is CwBasedInterface) {
|
||||||
// walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
|
// walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
|
||||||
} else {
|
} else {
|
||||||
walletInitFutures.add(wallet.init().then((value) {
|
walletInitFutures.add(wallet.init().then((value) {
|
||||||
|
@ -269,6 +270,7 @@ class Wallets {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_wallets[wallet.walletId] = wallet;
|
_wallets[wallet.walletId] = wallet;
|
||||||
} else {
|
} else {
|
||||||
|
@ -278,6 +280,7 @@ class Wallets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDesktop) {
|
||||||
if (walletInitFutures.isNotEmpty && walletsToInitLinearly.isNotEmpty) {
|
if (walletInitFutures.isNotEmpty && walletsToInitLinearly.isNotEmpty) {
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
_initLinearly(walletsToInitLinearly),
|
_initLinearly(walletsToInitLinearly),
|
||||||
|
@ -289,6 +292,7 @@ class Wallets {
|
||||||
await _initLinearly(walletsToInitLinearly);
|
await _initLinearly(walletsToInitLinearly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _initLinearly(
|
Future<void> _initLinearly(
|
||||||
List<({Wallet wallet, bool shouldAutoSync})> dataList,
|
List<({Wallet wallet, bool shouldAutoSync})> dataList,
|
||||||
|
|
|
@ -484,6 +484,11 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
FilterOperation? get receivingAddressFilterOperation =>
|
FilterOperation? get receivingAddressFilterOperation =>
|
||||||
FilterGroup.and(standardReceivingAddressFilters);
|
FilterGroup.and(standardReceivingAddressFilters);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> checkSaveInitialReceivingAddress() async {
|
||||||
|
// epiccash seems ok with nothing here?
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> init({bool? isRestore}) async {
|
Future<void> init({bool? isRestore}) async {
|
||||||
if (isRestore != true) {
|
if (isRestore != true) {
|
||||||
|
|
|
@ -86,25 +86,6 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
||||||
_credentials = web3.EthPrivateKey.fromHex(privateKey);
|
_credentials = web3.EthPrivateKey.fromHex(privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _generateAndSaveAddress(
|
|
||||||
String mnemonic,
|
|
||||||
String mnemonicPassphrase,
|
|
||||||
) async {
|
|
||||||
await _initCredentials(mnemonic, mnemonicPassphrase);
|
|
||||||
|
|
||||||
final address = Address(
|
|
||||||
walletId: walletId,
|
|
||||||
value: _credentials.address.hexEip55,
|
|
||||||
publicKey: [], // maybe store address bytes here? seems a waste of space though
|
|
||||||
derivationIndex: 0,
|
|
||||||
derivationPath: DerivationPath()..value = "$hdPathEthereum/0",
|
|
||||||
type: AddressType.ethereum,
|
|
||||||
subType: AddressSubType.receiving,
|
|
||||||
);
|
|
||||||
|
|
||||||
await mainDB.updateOrPutAddresses([address]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== Overrides ============================================
|
// ==================== Overrides ============================================
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -127,15 +108,27 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
||||||
FilterGroup.and(standardReceivingAddressFilters);
|
FilterGroup.and(standardReceivingAddressFilters);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> init({bool? isRestore}) async {
|
Future<void> checkSaveInitialReceivingAddress() async {
|
||||||
final address = await getCurrentReceivingAddress();
|
final address = await getCurrentReceivingAddress();
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
await _generateAndSaveAddress(
|
await _initCredentials(
|
||||||
await getMnemonic(),
|
await getMnemonic(),
|
||||||
await getMnemonicPassphrase(),
|
await getMnemonicPassphrase(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final address = Address(
|
||||||
|
walletId: walletId,
|
||||||
|
value: _credentials.address.hexEip55,
|
||||||
|
publicKey: [],
|
||||||
|
// maybe store address bytes here? seems a waste of space though
|
||||||
|
derivationIndex: 0,
|
||||||
|
derivationPath: DerivationPath()..value = "$hdPathEthereum/0",
|
||||||
|
type: AddressType.ethereum,
|
||||||
|
subType: AddressSubType.receiving,
|
||||||
|
);
|
||||||
|
|
||||||
|
await mainDB.updateOrPutAddresses([address]);
|
||||||
}
|
}
|
||||||
return super.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -475,17 +468,11 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface {
|
||||||
await refreshMutex.protect(() async {
|
await refreshMutex.protect(() async {
|
||||||
if (isRescan) {
|
if (isRescan) {
|
||||||
await mainDB.deleteWalletBlockchainData(walletId);
|
await mainDB.deleteWalletBlockchainData(walletId);
|
||||||
await _generateAndSaveAddress(
|
await checkSaveInitialReceivingAddress();
|
||||||
await getMnemonic(),
|
|
||||||
await getMnemonicPassphrase(),
|
|
||||||
);
|
|
||||||
await updateBalance();
|
await updateBalance();
|
||||||
await updateTransactions(isRescan: true);
|
await updateTransactions(isRescan: true);
|
||||||
} else {
|
} else {
|
||||||
await _generateAndSaveAddress(
|
await checkSaveInitialReceivingAddress();
|
||||||
await getMnemonic(),
|
|
||||||
await getMnemonicPassphrase(),
|
|
||||||
);
|
|
||||||
unawaited(updateBalance());
|
unawaited(updateBalance());
|
||||||
unawaited(updateTransactions());
|
unawaited(updateTransactions());
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ class StellarWallet extends Bip39Wallet<Stellar> {
|
||||||
FilterGroup.and(standardReceivingAddressFilters);
|
FilterGroup.and(standardReceivingAddressFilters);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> init() async {
|
Future<void> checkSaveInitialReceivingAddress() async {
|
||||||
try {
|
try {
|
||||||
final address = await getCurrentReceivingAddress();
|
final address = await getCurrentReceivingAddress();
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
|
@ -121,11 +121,10 @@ class StellarWallet extends Bip39Wallet<Stellar> {
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
// do nothing, still allow user into wallet
|
// do nothing, still allow user into wallet
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"$runtimeType init() failed: $e\n$s",
|
"$runtimeType checkSaveInitialReceivingAddress() failed: $e\n$s",
|
||||||
level: LogLevel.Error,
|
level: LogLevel.Error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return super.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -527,4 +527,9 @@ class EthTokenWallet extends Wallet {
|
||||||
value: TransactionSubType.ethToken,
|
value: TransactionSubType.ethToken,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> checkSaveInitialReceivingAddress() async {
|
||||||
|
await ethWallet.checkSaveInitialReceivingAddress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> init() async {
|
Future<void> checkSaveInitialReceivingAddress() async {
|
||||||
try {
|
try {
|
||||||
final _address = await getCurrentReceivingAddress();
|
final _address = await getCurrentReceivingAddress();
|
||||||
if (_address == null) {
|
if (_address == null) {
|
||||||
|
@ -155,12 +155,10 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
// do nothing, still allow user into wallet
|
// do nothing, still allow user into wallet
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"$runtimeType init() failed: $e\n$s",
|
"$runtimeType checkSaveInitialReceivingAddress() failed: $e\n$s",
|
||||||
level: LogLevel.Error,
|
level: LogLevel.Error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await super.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -61,6 +61,20 @@ abstract class Bip39HDWallet<T extends Bip39HDCurrency> extends Bip39Wallet<T>
|
||||||
await mainDB.updateOrPutAddresses([address]);
|
await mainDB.updateOrPutAddresses([address]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> checkSaveInitialReceivingAddress() async {
|
||||||
|
final current = await getCurrentChangeAddress();
|
||||||
|
if (current == null) {
|
||||||
|
final address = await _generateAddress(
|
||||||
|
chain: 0, // receiving
|
||||||
|
index: 0, // initial index
|
||||||
|
derivePathType: DerivePathTypeExt.primaryFor(info.coin),
|
||||||
|
);
|
||||||
|
|
||||||
|
await mainDB.updateOrPutAddresses([address]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ========== Subclasses may override ========================================
|
// ========== Subclasses may override ========================================
|
||||||
|
|
||||||
/// To be overridden by crypto currencies that do extra address conversions
|
/// To be overridden by crypto currencies that do extra address conversions
|
||||||
|
|
|
@ -435,6 +435,8 @@ abstract class Wallet<T extends CryptoCurrency> {
|
||||||
|
|
||||||
Future<bool> pingCheck();
|
Future<bool> pingCheck();
|
||||||
|
|
||||||
|
Future<void> checkSaveInitialReceivingAddress();
|
||||||
|
|
||||||
//===========================================
|
//===========================================
|
||||||
/// add transaction to local db temporarily. Used for quickly updating ui
|
/// add transaction to local db temporarily. Used for quickly updating ui
|
||||||
/// before refresh can fetch data from server
|
/// before refresh can fetch data from server
|
||||||
|
@ -600,6 +602,7 @@ abstract class Wallet<T extends CryptoCurrency> {
|
||||||
|
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
|
await checkSaveInitialReceivingAddress();
|
||||||
final address = await getCurrentReceivingAddress();
|
final address = await getCurrentReceivingAddress();
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
await info.updateReceivingAddress(
|
await info.updateReceivingAddress(
|
||||||
|
@ -607,9 +610,6 @@ abstract class Wallet<T extends CryptoCurrency> {
|
||||||
isar: mainDB.isar,
|
isar: mainDB.isar,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make sure subclasses override this if they require some set up
|
|
||||||
// especially xmr/wow/epiccash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
|
@ -179,6 +179,11 @@ mixin CwBasedInterface<T extends CryptonoteCurrency> on CryptonoteWallet<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> checkSaveInitialReceivingAddress() async {
|
||||||
|
// this doesn't work without opening the wallet first which takes a while
|
||||||
|
}
|
||||||
|
|
||||||
// ============ Interface ====================================================
|
// ============ Interface ====================================================
|
||||||
|
|
||||||
Future<Amount> get availableBalance;
|
Future<Amount> get availableBalance;
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:bip47/src/util.dart';
|
import 'package:bip47/src/util.dart';
|
||||||
import 'package:bitcoindart/bitcoindart.dart' as bitcoindart;
|
import 'package:bitcoindart/bitcoindart.dart' as bitcoindart;
|
||||||
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
||||||
import 'package:decimal/decimal.dart';
|
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart';
|
import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart';
|
||||||
import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
|
import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
|
||||||
|
@ -831,55 +829,6 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<({Transaction transaction, Address address})>>
|
|
||||||
fetchTransactionsV1({
|
|
||||||
required List<Address> addresses,
|
|
||||||
required int currentChainHeight,
|
|
||||||
}) async {
|
|
||||||
final List<({String txHash, int height, String address})> allTxHashes =
|
|
||||||
(await fetchHistory(addresses.map((e) => e.value).toList()))
|
|
||||||
.map(
|
|
||||||
(e) => (
|
|
||||||
txHash: e["tx_hash"] as String,
|
|
||||||
height: e["height"] as int,
|
|
||||||
address: e["address"] as String,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
List<Map<String, dynamic>> allTransactions = [];
|
|
||||||
|
|
||||||
for (final data in allTxHashes) {
|
|
||||||
final tx = await electrumXCachedClient.getTransaction(
|
|
||||||
txHash: data.txHash,
|
|
||||||
verbose: true,
|
|
||||||
coin: cryptoCurrency.coin,
|
|
||||||
);
|
|
||||||
|
|
||||||
// check for duplicates before adding to list
|
|
||||||
if (allTransactions
|
|
||||||
.indexWhere((e) => e["txid"] == tx["txid"] as String) ==
|
|
||||||
-1) {
|
|
||||||
tx["address"] = addresses.firstWhere((e) => e.value == data.address);
|
|
||||||
tx["height"] = data.height;
|
|
||||||
allTransactions.add(tx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<({Transaction transaction, Address address})> txnsData = [];
|
|
||||||
|
|
||||||
for (final txObject in allTransactions) {
|
|
||||||
final data = await _parseTransactionV1(
|
|
||||||
txObject,
|
|
||||||
addresses,
|
|
||||||
);
|
|
||||||
|
|
||||||
txnsData.add(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return txnsData;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<ElectrumXNode> getCurrentElectrumXNode() async {
|
Future<ElectrumXNode> getCurrentElectrumXNode() async {
|
||||||
final node = getCurrentNode();
|
final node = getCurrentNode();
|
||||||
|
|
||||||
|
@ -1185,257 +1134,6 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
||||||
return utxo;
|
return utxo;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<({Transaction transaction, Address address})> _parseTransactionV1(
|
|
||||||
Map<String, dynamic> txData,
|
|
||||||
List<Address> myAddresses,
|
|
||||||
) async {
|
|
||||||
Set<String> receivingAddresses = myAddresses
|
|
||||||
.where((e) =>
|
|
||||||
e.subType == AddressSubType.receiving ||
|
|
||||||
e.subType == AddressSubType.paynymReceive ||
|
|
||||||
e.subType == AddressSubType.paynymNotification)
|
|
||||||
.map((e) => e.value)
|
|
||||||
.toSet();
|
|
||||||
Set<String> changeAddresses = myAddresses
|
|
||||||
.where((e) => e.subType == AddressSubType.change)
|
|
||||||
.map((e) => e.value)
|
|
||||||
.toSet();
|
|
||||||
|
|
||||||
Set<String> inputAddresses = {};
|
|
||||||
Set<String> outputAddresses = {};
|
|
||||||
|
|
||||||
Amount totalInputValue = Amount(
|
|
||||||
rawValue: BigInt.zero,
|
|
||||||
fractionDigits: cryptoCurrency.coin.decimals,
|
|
||||||
);
|
|
||||||
Amount totalOutputValue = Amount(
|
|
||||||
rawValue: BigInt.zero,
|
|
||||||
fractionDigits: cryptoCurrency.coin.decimals,
|
|
||||||
);
|
|
||||||
|
|
||||||
Amount amountSentFromWallet = Amount(
|
|
||||||
rawValue: BigInt.zero,
|
|
||||||
fractionDigits: cryptoCurrency.coin.decimals,
|
|
||||||
);
|
|
||||||
Amount amountReceivedInWallet = Amount(
|
|
||||||
rawValue: BigInt.zero,
|
|
||||||
fractionDigits: cryptoCurrency.coin.decimals,
|
|
||||||
);
|
|
||||||
Amount changeAmount = Amount(
|
|
||||||
rawValue: BigInt.zero,
|
|
||||||
fractionDigits: cryptoCurrency.coin.decimals,
|
|
||||||
);
|
|
||||||
|
|
||||||
// parse inputs
|
|
||||||
for (final input in txData["vin"] as List) {
|
|
||||||
final prevTxid = input["txid"] as String;
|
|
||||||
final prevOut = input["vout"] as int;
|
|
||||||
|
|
||||||
// fetch input tx to get address
|
|
||||||
final inputTx = await electrumXCachedClient.getTransaction(
|
|
||||||
txHash: prevTxid,
|
|
||||||
coin: cryptoCurrency.coin,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (final output in inputTx["vout"] as List) {
|
|
||||||
// check matching output
|
|
||||||
if (prevOut == output["n"]) {
|
|
||||||
// get value
|
|
||||||
final value = Amount.fromDecimal(
|
|
||||||
Decimal.parse(output["value"].toString()),
|
|
||||||
fractionDigits: cryptoCurrency.coin.decimals,
|
|
||||||
);
|
|
||||||
|
|
||||||
// add value to total
|
|
||||||
totalInputValue += value;
|
|
||||||
|
|
||||||
// get input(prevOut) address
|
|
||||||
final address = output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
|
||||||
output["scriptPubKey"]?["address"] as String?;
|
|
||||||
|
|
||||||
if (address != null) {
|
|
||||||
inputAddresses.add(address);
|
|
||||||
|
|
||||||
// if input was from my wallet, add value to amount sent
|
|
||||||
if (receivingAddresses.contains(address) ||
|
|
||||||
changeAddresses.contains(address)) {
|
|
||||||
amountSentFromWallet += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse outputs
|
|
||||||
for (final output in txData["vout"] as List) {
|
|
||||||
// get value
|
|
||||||
final value = Amount.fromDecimal(
|
|
||||||
Decimal.parse(output["value"].toString()),
|
|
||||||
fractionDigits: cryptoCurrency.coin.decimals,
|
|
||||||
);
|
|
||||||
|
|
||||||
// add value to total
|
|
||||||
totalOutputValue += value;
|
|
||||||
|
|
||||||
// get output address
|
|
||||||
final address = output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
|
||||||
output["scriptPubKey"]?["address"] as String?;
|
|
||||||
if (address != null) {
|
|
||||||
outputAddresses.add(address);
|
|
||||||
|
|
||||||
// if output was to my wallet, add value to amount received
|
|
||||||
if (receivingAddresses.contains(address)) {
|
|
||||||
amountReceivedInWallet += value;
|
|
||||||
} else if (changeAddresses.contains(address)) {
|
|
||||||
changeAmount += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final mySentFromAddresses = [
|
|
||||||
...receivingAddresses.intersection(inputAddresses),
|
|
||||||
...changeAddresses.intersection(inputAddresses)
|
|
||||||
];
|
|
||||||
final myReceivedOnAddresses =
|
|
||||||
receivingAddresses.intersection(outputAddresses);
|
|
||||||
final myChangeReceivedOnAddresses =
|
|
||||||
changeAddresses.intersection(outputAddresses);
|
|
||||||
|
|
||||||
final fee = totalInputValue - totalOutputValue;
|
|
||||||
|
|
||||||
// this is the address initially used to fetch the txid
|
|
||||||
Address transactionAddress = txData["address"] as Address;
|
|
||||||
|
|
||||||
TransactionType type;
|
|
||||||
Amount amount;
|
|
||||||
if (mySentFromAddresses.isNotEmpty && myReceivedOnAddresses.isNotEmpty) {
|
|
||||||
// tx is sent to self
|
|
||||||
type = TransactionType.sentToSelf;
|
|
||||||
|
|
||||||
// should be 0
|
|
||||||
amount =
|
|
||||||
amountSentFromWallet - amountReceivedInWallet - fee - changeAmount;
|
|
||||||
} else if (mySentFromAddresses.isNotEmpty) {
|
|
||||||
// outgoing tx
|
|
||||||
type = TransactionType.outgoing;
|
|
||||||
amount = amountSentFromWallet - changeAmount - fee;
|
|
||||||
|
|
||||||
// non wallet addresses found in tx outputs
|
|
||||||
final nonWalletOutAddresses = outputAddresses.difference(
|
|
||||||
myChangeReceivedOnAddresses,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (nonWalletOutAddresses.isNotEmpty) {
|
|
||||||
final possible = nonWalletOutAddresses.first;
|
|
||||||
|
|
||||||
if (transactionAddress.value != possible) {
|
|
||||||
transactionAddress = Address(
|
|
||||||
walletId: myAddresses.first.walletId,
|
|
||||||
value: possible,
|
|
||||||
derivationIndex: -1,
|
|
||||||
derivationPath: null,
|
|
||||||
subType: AddressSubType.nonWallet,
|
|
||||||
type: AddressType.nonWallet,
|
|
||||||
publicKey: [],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// some other type of tx where the receiving address is
|
|
||||||
// one of my change addresses
|
|
||||||
|
|
||||||
type = TransactionType.sentToSelf;
|
|
||||||
amount = changeAmount;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// incoming tx
|
|
||||||
type = TransactionType.incoming;
|
|
||||||
amount = amountReceivedInWallet;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Output> outs = [];
|
|
||||||
List<Input> ins = [];
|
|
||||||
|
|
||||||
for (final json in txData["vin"] as List) {
|
|
||||||
bool isCoinBase = json['coinbase'] != null;
|
|
||||||
String? witness;
|
|
||||||
if (json['witness'] != null && json['witness'] is String) {
|
|
||||||
witness = json['witness'] as String;
|
|
||||||
} else if (json['txinwitness'] != null) {
|
|
||||||
if (json['txinwitness'] is List) {
|
|
||||||
witness = jsonEncode(json['txinwitness']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final input = Input(
|
|
||||||
txid: json['txid'] as String,
|
|
||||||
vout: json['vout'] as int? ?? -1,
|
|
||||||
scriptSig: json['scriptSig']?['hex'] as String?,
|
|
||||||
scriptSigAsm: json['scriptSig']?['asm'] as String?,
|
|
||||||
isCoinbase: isCoinBase ? isCoinBase : json['is_coinbase'] as bool?,
|
|
||||||
sequence: json['sequence'] as int?,
|
|
||||||
innerRedeemScriptAsm: json['innerRedeemscriptAsm'] as String?,
|
|
||||||
witness: witness,
|
|
||||||
);
|
|
||||||
ins.add(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final json in txData["vout"] as List) {
|
|
||||||
final output = Output(
|
|
||||||
scriptPubKey: json['scriptPubKey']?['hex'] as String?,
|
|
||||||
scriptPubKeyAsm: json['scriptPubKey']?['asm'] as String?,
|
|
||||||
scriptPubKeyType: json['scriptPubKey']?['type'] as String?,
|
|
||||||
scriptPubKeyAddress:
|
|
||||||
json["scriptPubKey"]?["addresses"]?[0] as String? ??
|
|
||||||
json['scriptPubKey']?['type'] as String? ??
|
|
||||||
"",
|
|
||||||
value: Amount.fromDecimal(
|
|
||||||
Decimal.parse(json["value"].toString()),
|
|
||||||
fractionDigits: cryptoCurrency.coin.decimals,
|
|
||||||
).raw.toInt(),
|
|
||||||
);
|
|
||||||
outs.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
TransactionSubType txSubType = TransactionSubType.none;
|
|
||||||
if (this is PaynymInterface && outs.length > 1 && ins.isNotEmpty) {
|
|
||||||
for (int i = 0; i < outs.length; i++) {
|
|
||||||
List<String>? scriptChunks = outs[i].scriptPubKeyAsm?.split(" ");
|
|
||||||
if (scriptChunks?.length == 2 && scriptChunks?[0] == "OP_RETURN") {
|
|
||||||
final blindedPaymentCode = scriptChunks![1];
|
|
||||||
final bytes = blindedPaymentCode.fromHex;
|
|
||||||
|
|
||||||
// https://en.bitcoin.it/wiki/BIP_0047#Sending
|
|
||||||
if (bytes.length == 80 && bytes.first == 1) {
|
|
||||||
txSubType = TransactionSubType.bip47Notification;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final tx = Transaction(
|
|
||||||
walletId: myAddresses.first.walletId,
|
|
||||||
txid: txData["txid"] as String,
|
|
||||||
timestamp: txData["blocktime"] as int? ??
|
|
||||||
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
|
||||||
type: type,
|
|
||||||
subType: txSubType,
|
|
||||||
// amount may overflow. Deprecated. Use amountString
|
|
||||||
amount: amount.raw.toInt(),
|
|
||||||
amountString: amount.toJsonString(),
|
|
||||||
fee: fee.raw.toInt(),
|
|
||||||
height: txData["height"] as int?,
|
|
||||||
isCancelled: false,
|
|
||||||
isLelantus: false,
|
|
||||||
slateId: null,
|
|
||||||
otherData: null,
|
|
||||||
nonce: null,
|
|
||||||
inputs: ins,
|
|
||||||
outputs: outs,
|
|
||||||
numberOfMessages: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (transaction: tx, address: transactionAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -1993,12 +1691,15 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> checkSaveInitialReceivingAddress() async {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
try {
|
try {
|
||||||
final features = await electrumXClient
|
final features = await electrumXClient
|
||||||
.getServerFeatures()
|
.getServerFeatures()
|
||||||
.timeout(const Duration(seconds: 2));
|
.timeout(const Duration(seconds: 4));
|
||||||
|
|
||||||
Logging.instance.log("features: $features", level: LogLevel.Info);
|
Logging.instance.log("features: $features", level: LogLevel.Info);
|
||||||
|
|
||||||
|
|
|
@ -315,22 +315,20 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> init() async {
|
Future<void> checkSaveInitialReceivingAddress() async {
|
||||||
try {
|
try {
|
||||||
_cachedAddress = await getCurrentReceivingAddress();
|
_cachedAddress = await getCurrentReceivingAddress();
|
||||||
if (_cachedAddress == null) {
|
if (_cachedAddress == null) {
|
||||||
_cachedAddress = await _getAddressFromMnemonic();
|
_cachedAddress = await _getAddressFromMnemonic();
|
||||||
await mainDB.putAddress(_cachedAddress!);
|
await mainDB.updateOrPutAddresses([_cachedAddress!]);
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
// do nothing, still allow user into wallet
|
// do nothing, still allow user into wallet
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"$runtimeType init() failed: $e\n$s",
|
"$runtimeType checkSaveInitialReceivingAddress() failed: $e\n$s",
|
||||||
level: LogLevel.Error,
|
level: LogLevel.Error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -93,17 +93,20 @@ class SimpleWalletCard extends ConsumerWidget {
|
||||||
final nav = Navigator.of(context);
|
final nav = Navigator.of(context);
|
||||||
|
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
await wallet.init();
|
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
|
final Future<void> loadFuture;
|
||||||
if (wallet is CwBasedInterface) {
|
if (wallet is CwBasedInterface) {
|
||||||
|
loadFuture = wallet.init().then((value) async => await (wallet).open());
|
||||||
|
} else {
|
||||||
|
loadFuture = wallet.init();
|
||||||
|
}
|
||||||
await showLoading(
|
await showLoading(
|
||||||
whileFuture: wallet.open(),
|
whileFuture: loadFuture,
|
||||||
context: context,
|
context: context,
|
||||||
message: 'Opening ${wallet.info.name}',
|
message: 'Opening ${wallet.info.name}',
|
||||||
isDesktop: Util.isDesktop,
|
isDesktop: Util.isDesktop,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
if (popPrevious) nav.pop();
|
if (popPrevious) nav.pop();
|
||||||
|
|
||||||
if (desktopNavigatorState != null) {
|
if (desktopNavigatorState != null) {
|
||||||
|
|
Loading…
Reference in a new issue