diff --git a/lib/models/isar/models/blockchain_data/v2/transaction_v2.dart b/lib/models/isar/models/blockchain_data/v2/transaction_v2.dart index e00d8952a..fe46f9a82 100644 --- a/lib/models/isar/models/blockchain_data/v2/transaction_v2.dart +++ b/lib/models/isar/models/blockchain_data/v2/transaction_v2.dart @@ -155,6 +155,10 @@ class TransactionV2 { } if (isEpiccashTransaction) { + if (slateId == null) { + return "Restored Funds"; + } + if (isCancelled) { return "Cancelled"; } else if (type == TransactionType.incoming) { diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index 74b1e517d..f2eb0d779 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -10,6 +10,7 @@ import 'dart:async'; import 'dart:collection'; +import 'dart:convert'; import 'dart:io'; import 'dart:math'; @@ -50,6 +51,8 @@ import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/wallets/isar/models/wallet_info.dart'; +import 'package:stackwallet/wallets/wallet/impl/epiccash_wallet.dart'; +import 'package:stackwallet/wallets/wallet/supporting/epiccash_wallet_info_extension.dart'; import 'package:stackwallet/wallets/wallet/wallet.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; @@ -202,6 +205,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> { mnemonic = mnemonic.trim(); int height = 0; + String? otherDataJsonString; if (widget.coin == Coin.monero) { height = monero.getHeigthByDate(date: widget.restoreFromDate); @@ -228,6 +232,22 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> { if (height < 0) { height = 0; } + + otherDataJsonString = jsonEncode( + { + WalletInfoKeys.epiccashData: jsonEncode( + ExtraEpiccashWalletInfo( + receivingIndex: 0, + changeIndex: 0, + slatesToAddresses: {}, + slatesToCommits: {}, + lastScannedBlock: height, + restoreHeight: height, + creationHeight: height, + ).toMap(), + ), + }, + ); } // TODO: do actual check to make sure it is a valid mnemonic for monero @@ -244,6 +264,8 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> { final info = WalletInfo.createNew( coin: widget.coin, name: widget.walletName, + restoreHeight: height, + otherDataJsonString: otherDataJsonString, ); bool isRestoring = true; @@ -292,7 +314,12 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> { mnemonic: mnemonic, ); - await wallet.init(); + if (wallet is EpiccashWallet) { + await wallet.init(isRestore: true); + } else { + await wallet.init(); + } + await wallet.recover(isRescan: false); // check if state is still active before continuing diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart index 58ab3f713..74b016893 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart @@ -110,7 +110,25 @@ class _TransactionV2DetailsViewState unit = coin.ticker; - if (_transaction.subType == TransactionSubType.cashFusion) { + if (_transaction.isEpiccashTransaction) { + switch (_transaction.type) { + case TransactionType.outgoing: + case TransactionType.unknown: + amount = _transaction.getAmountSentFromThisWallet(coin: coin); + break; + + case TransactionType.incoming: + case TransactionType.sentToSelf: + amount = _transaction.getAmountReceivedInThisWallet(coin: coin); + break; + } + data = _transaction.outputs + .map((e) => ( + addresses: e.addresses, + amount: Amount(rawValue: e.value, fractionDigits: coin.decimals) + )) + .toList(); + } else if (_transaction.subType == TransactionSubType.cashFusion) { amount = _transaction.getAmountReceivedInThisWallet(coin: coin); data = _transaction.outputs .where((e) => e.walletOwns) diff --git a/lib/services/mixins/epic_cash_hive.dart b/lib/services/mixins/epic_cash_hive.dart deleted file mode 100644 index 014b9e02d..000000000 --- a/lib/services/mixins/epic_cash_hive.dart +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of Stack Wallet. - * - * Copyright (c) 2023 Cypher Stack - * All Rights Reserved. - * The code is distributed under GPLv3 license, see LICENSE file for details. - * Generated by Cypher Stack on 2023-05-26 - * - */ - -import 'package:stackwallet/db/hive/db.dart'; - -mixin EpicCashHive { - late final String _walletId; - - void initEpicCashHive(String walletId) { - _walletId = walletId; - } - - // receiving index - int? epicGetReceivingIndex() { - return DB.instance.get<dynamic>(boxName: _walletId, key: "receivingIndex") - as int?; - } - - Future<void> epicUpdateReceivingIndex(int index) async { - await DB.instance.put<dynamic>( - boxName: _walletId, - key: "receivingIndex", - value: index, - ); - } - - // change index - int? epicGetChangeIndex() { - return DB.instance.get<dynamic>(boxName: _walletId, key: "changeIndex") - as int?; - } - - Future<void> epicUpdateChangeIndex(int index) async { - await DB.instance.put<dynamic>( - boxName: _walletId, - key: "changeIndex", - value: index, - ); - } - - // slateToAddresses - Map epicGetSlatesToAddresses() { - return DB.instance.get<dynamic>( - boxName: _walletId, - key: "slate_to_address", - ) as Map? ?? - {}; - } - - Future<void> epicUpdateSlatesToAddresses(Map map) async { - await DB.instance.put<dynamic>( - boxName: _walletId, - key: "slate_to_address", - value: map, - ); - } - - // slatesToCommits - Map? epicGetSlatesToCommits() { - return DB.instance.get<dynamic>( - boxName: _walletId, - key: "slatesToCommits", - ) as Map?; - } - - Future<void> epicUpdateSlatesToCommits(Map map) async { - await DB.instance.put<dynamic>( - boxName: _walletId, - key: "slatesToCommits", - value: map, - ); - } - - // last scanned block - int? epicGetLastScannedBlock() { - return DB.instance.get<dynamic>(boxName: _walletId, key: "lastScannedBlock") - as int?; - } - - Future<void> epicUpdateLastScannedBlock(int blockHeight) async { - await DB.instance.put<dynamic>( - boxName: _walletId, - key: "lastScannedBlock", - value: blockHeight, - ); - } - - // epic restore height - int? epicGetRestoreHeight() { - return DB.instance.get<dynamic>(boxName: _walletId, key: "restoreHeight") - as int?; - } - - Future<void> epicUpdateRestoreHeight(int height) async { - await DB.instance.put<dynamic>( - boxName: _walletId, - key: "restoreHeight", - value: height, - ); - } - - // epic creation height - int? epicGetCreationHeight() { - return DB.instance.get<dynamic>(boxName: _walletId, key: "creationHeight") - as int?; - } - - Future<void> epicUpdateCreationHeight(int height) async { - await DB.instance.put<dynamic>( - boxName: _walletId, - key: "creationHeight", - value: height, - ); - } -} diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index b8b74a5d3..db0543044 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -259,7 +259,6 @@ abstract class Constants { case Coin.litecoinTestNet: case Coin.firo: case Coin.firoTestNet: - case Coin.epicCash: case Coin.namecoin: case Coin.particl: case Coin.ethereum: @@ -270,6 +269,7 @@ abstract class Constants { case Coin.nano: case Coin.banano: + case Coin.epicCash: case Coin.stellar: case Coin.stellarTestnet: case Coin.tezos: diff --git a/lib/wallets/isar/models/wallet_info.dart b/lib/wallets/isar/models/wallet_info.dart index 405728d4c..e18cba80d 100644 --- a/lib/wallets/isar/models/wallet_info.dart +++ b/lib/wallets/isar/models/wallet_info.dart @@ -353,7 +353,6 @@ class WalletInfo implements IsarId { int favouriteOrderIndex = -1, int cachedChainHeight = 0, int restoreHeight = 0, - bool isMnemonicVerified = false, String? cachedBalanceString, String? cachedBalanceSecondaryString, String? cachedBalanceTertiaryString, diff --git a/lib/wallets/wallet/impl/epiccash_wallet.dart b/lib/wallets/wallet/impl/epiccash_wallet.dart index e532b6195..ef3cd1966 100644 --- a/lib/wallets/wallet/impl/epiccash_wallet.dart +++ b/lib/wallets/wallet/impl/epiccash_wallet.dart @@ -501,79 +501,87 @@ class EpiccashWallet extends Bip39Wallet { FilterGroup.and(standardReceivingAddressFilters); @override - Future<void> init() async { - String? encodedWallet = - await secureStorageInterface.read(key: "${walletId}_wallet"); + Future<void> init({bool? isRestore}) async { + if (isRestore != true) { + String? encodedWallet = + await secureStorageInterface.read(key: "${walletId}_wallet"); - // check if should create a new wallet - if (encodedWallet == null) { - await updateNode(); - final mnemonicString = await getMnemonic(); + // check if should create a new wallet + if (encodedWallet == null) { + await updateNode(); + final mnemonicString = await getMnemonic(); - final String password = generatePassword(); - final String stringConfig = await _getConfig(); - final EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig(); + final String password = generatePassword(); + final String stringConfig = await _getConfig(); + final EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig(); - await secureStorageInterface.write( - key: '${walletId}_config', value: stringConfig); - await secureStorageInterface.write( - key: '${walletId}_password', value: password); - await secureStorageInterface.write( - key: '${walletId}_epicboxConfig', value: epicboxConfig.toString()); + await secureStorageInterface.write( + key: '${walletId}_config', value: stringConfig); + await secureStorageInterface.write( + key: '${walletId}_password', value: password); + await secureStorageInterface.write( + key: '${walletId}_epicboxConfig', value: epicboxConfig.toString()); - String name = walletId; + String name = walletId; - await epiccash.LibEpiccash.initializeNewWallet( - config: stringConfig, - mnemonic: mnemonicString, - password: password, - name: name, - ); + await epiccash.LibEpiccash.initializeNewWallet( + config: stringConfig, + mnemonic: mnemonicString, + password: password, + name: name, + ); - //Open wallet - encodedWallet = await epiccash.LibEpiccash.openWallet( - config: stringConfig, password: password); - await secureStorageInterface.write( - key: '${walletId}_wallet', value: encodedWallet); + //Open wallet + encodedWallet = await epiccash.LibEpiccash.openWallet( + config: stringConfig, + password: password, + ); + await secureStorageInterface.write( + key: '${walletId}_wallet', + value: encodedWallet, + ); - //Store Epic box address info - await _generateAndStoreReceivingAddressForIndex(0); + //Store Epic box address info + await _generateAndStoreReceivingAddressForIndex(0); - // subtract a couple days to ensure we have a buffer for SWB - final bufferedCreateHeight = _calculateRestoreHeightFrom( - date: DateTime.now().subtract(const Duration(days: 2))); + // subtract a couple days to ensure we have a buffer for SWB + final bufferedCreateHeight = _calculateRestoreHeightFrom( + date: DateTime.now().subtract(const Duration(days: 2))); - final epicData = ExtraEpiccashWalletInfo( - receivingIndex: 0, - changeIndex: 0, - slatesToAddresses: {}, - slatesToCommits: {}, - lastScannedBlock: bufferedCreateHeight, - restoreHeight: bufferedCreateHeight, - creationHeight: bufferedCreateHeight, - ); + final epicData = ExtraEpiccashWalletInfo( + receivingIndex: 0, + changeIndex: 0, + slatesToAddresses: {}, + slatesToCommits: {}, + lastScannedBlock: bufferedCreateHeight, + restoreHeight: bufferedCreateHeight, + creationHeight: bufferedCreateHeight, + ); - await info.updateExtraEpiccashWalletInfo( - epicData: epicData, - isar: mainDB.isar, - ); - } else { - Logging.instance.log( - "initializeExisting() ${cryptoCurrency.coin.prettyName} wallet", - level: LogLevel.Info); + await info.updateExtraEpiccashWalletInfo( + epicData: epicData, + isar: mainDB.isar, + ); + } else { + Logging.instance.log( + "initializeExisting() ${cryptoCurrency.coin.prettyName} wallet", + level: LogLevel.Info); - final config = await _getRealConfig(); - final password = - await secureStorageInterface.read(key: '${walletId}_password'); + final config = await _getRealConfig(); + final password = + await secureStorageInterface.read(key: '${walletId}_password'); - final walletOpen = await epiccash.LibEpiccash.openWallet( - config: config, password: password!); - await secureStorageInterface.write( - key: '${walletId}_wallet', value: walletOpen); + final walletOpen = await epiccash.LibEpiccash.openWallet( + config: config, + password: password!, + ); + await secureStorageInterface.write( + key: '${walletId}_wallet', value: walletOpen); - await updateNode(); - await updateBalance(); - // TODO: is there anything else that should be set up here whenever this wallet is first loaded again? + await updateNode(); + await updateBalance(); + // TODO: is there anything else that should be set up here whenever this wallet is first loaded again? + } } return await super.init(); @@ -695,7 +703,7 @@ class EpiccashWallet extends Bip39Wallet { isar: mainDB.isar, ); - await _startScans(); + unawaited(_startScans()); } else { await updateNode(); final String password = generatePassword(); @@ -754,7 +762,7 @@ class EpiccashWallet extends Bip39Wallet { } }); - await refresh(); + unawaited(refresh()); } catch (e, s) { Logging.instance.log( "Exception rethrown from electrumx_mixin recover(): $e\n$s", @@ -945,10 +953,9 @@ class EpiccashWallet extends Bip39Wallet { for (final tx in transactions) { // Logging.instance.log("tx: $tx", level: LogLevel.Info); - // unsure if needed - // final isIncoming = - // tx.txType == epic_models.TransactionType.TxReceived || - // tx.txType == epic_models.TransactionType.TxReceivedCancelled; + final isIncoming = + tx.txType == epic_models.TransactionType.TxReceived || + tx.txType == epic_models.TransactionType.TxReceivedCancelled; final slateId = tx.txSlateId; final commitId = slatesToCommits[slateId]?['commitId'] as String?; final numberOfMessages = tx.messages?.messages.length; @@ -956,43 +963,57 @@ class EpiccashWallet extends Bip39Wallet { final addressFrom = slatesToCommits[slateId]?["from"] as String?; final addressTo = slatesToCommits[slateId]?["to"] as String?; + final credit = int.parse(tx.amountCredited); + final debit = int.parse(tx.amountDebited); + final fee = int.tryParse(tx.fee ?? "0") ?? 0; + // hack epic tx data into inputs and outputs final List<OutputV2> outputs = []; final List<InputV2> inputs = []; - // TODO: [prio=high] should addressFrom and addressTo be swapped?? final addressFromIsMine = myAddressesSet.contains(addressFrom); final addressToIsMine = myAddressesSet.contains(addressTo); - outputs.add( - OutputV2.isarCantDoRequiredInDefaultConstructor( - scriptPubKeyHex: "00", - valueStringSats: tx.amountCredited, - addresses: [if (addressFrom != null) addressFrom], - walletOwns: addressFromIsMine, - ), + + OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor( + scriptPubKeyHex: "00", + valueStringSats: credit.toString(), + addresses: [ + if (addressFrom != null) addressFrom, + ], + walletOwns: true, ); - inputs.add( - InputV2.isarCantDoRequiredInDefaultConstructor( - scriptSigHex: null, - sequence: null, - outpoint: null, - addresses: [if (addressTo != null) addressTo], - valueStringSats: tx.amountDebited, - witness: null, - innerRedeemScriptAsm: null, - coinbase: null, - walletOwns: addressToIsMine, - ), + InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( + scriptSigHex: null, + sequence: null, + outpoint: null, + addresses: [if (addressTo != null) addressTo], + valueStringSats: debit.toString(), + witness: null, + innerRedeemScriptAsm: null, + coinbase: null, + walletOwns: true, ); final TransactionType txType; - if (addressFromIsMine && addressToIsMine) { - txType = TransactionType.sentToSelf; - } else if (addressFromIsMine) { - txType = TransactionType.incoming; + if (isIncoming) { + if (addressToIsMine && addressFromIsMine) { + txType = TransactionType.sentToSelf; + } else { + txType = TransactionType.incoming; + } + output = output.copyWith( + addresses: [ + myAddressesSet + .first, // Must be changed if we ever do more than a single wallet address!!! + ], + walletOwns: true, + ); } else { txType = TransactionType.outgoing; } + outputs.add(output); + inputs.add(input); + final otherData = { "isEpiccashTransaction": true, "numberOfMessages": numberOfMessages, @@ -1001,6 +1022,10 @@ class EpiccashWallet extends Bip39Wallet { "isCancelled": tx.txType == epic_models.TransactionType.TxSentCancelled || tx.txType == epic_models.TransactionType.TxReceivedCancelled, + "anonFees": Amount( + rawValue: BigInt.from(fee), + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), }; final txn = TransactionV2(