Merge pull request #714 from cypherstack/wallets_refactor

Wallets refactor
This commit is contained in:
Diego Salazar 2024-01-15 09:33:56 -07:00 committed by GitHub
commit 783c42b63b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
415 changed files with 61623 additions and 96802 deletions

2
.gitignore vendored
View file

@ -56,4 +56,6 @@ libcw_wownero.dll
libepic_cash_wallet.dll
libmobileliblelantus.dll
libtor_ffi.dll
flutter_libsparkmobile.dll
secp256k1.dll
/libisar.so

1
assets/svg/spark.svg Normal file
View file

@ -0,0 +1 @@
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"><path d="m24.5 31.9-4.9 16.2h12.5l-4.2 13.9 16.5-20.2h-11.9l2.9-9.9z" fill="#ffce31" transform="matrix(1.25 0 0 2 -6 -62)"/></svg>

After

Width:  |  Height:  |  Size: 190 B

@ -1 +1 @@
Subproject commit f677dec0b34d3f9fe8fce2bc8ff5c508c3f3bb9a
Subproject commit 8ce7c3ab9b602c1f7f4e856380023b86002857ce

View file

@ -12,7 +12,8 @@ import 'package:hive/hive.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/db/migrate_wallets_to_isar.dart';
import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
@ -54,7 +55,7 @@ class DbVersionMigrator with WalletDB {
final walletInfoList = await walletsService.walletNames;
await prefs.init();
ElectrumX? client;
ElectrumXClient? client;
int? latestSetId;
// only instantiate client if there are firo wallets
@ -76,7 +77,7 @@ class DbVersionMigrator with WalletDB {
)
.toList();
client = ElectrumX.from(
client = ElectrumXClient.from(
node: ElectrumXNode(
address: node.host,
port: node.port,
@ -88,7 +89,7 @@ class DbVersionMigrator with WalletDB {
);
try {
latestSetId = await client.getLatestCoinId();
latestSetId = await client.getLelantusLatestCoinId();
} catch (e) {
// default to 2 for now
latestSetId = 2;
@ -353,74 +354,23 @@ class DbVersionMigrator with WalletDB {
// try to continue migrating
return await migrate(11, secureStore: secureStore);
case 11:
// migrate
await _v11(secureStore);
// update version
await DB.instance.put<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 12);
// try to continue migrating
return await migrate(12, secureStore: secureStore);
default:
// finally return
return;
}
}
Future<void> _v10(SecureStorageInterface secureStore) async {
await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
await Hive.openBox<dynamic>(DB.boxNamePrefs);
final walletsService = WalletsService(secureStorageInterface: secureStore);
final prefs = Prefs.instance;
final walletInfoList = await walletsService.walletNames;
await prefs.init();
await MainDB.instance.initMainDB();
for (final walletId in walletInfoList.keys) {
final info = walletInfoList[walletId]!;
assert(info.walletId == walletId);
if (info.coin == Coin.firo &&
MainDB.instance.isar.lelantusCoins
.where()
.walletIdEqualTo(walletId)
.countSync() ==
0) {
final walletBox = await Hive.openBox<dynamic>(walletId);
final hiveLCoins = DB.instance.get<dynamic>(
boxName: walletId,
key: "_lelantus_coins",
) as List? ??
[];
final jindexes = (DB.instance
.get<dynamic>(boxName: walletId, key: "jindex") as List? ??
[])
.cast<int>();
final List<isar_models.LelantusCoin> coins = [];
for (final e in hiveLCoins) {
final map = e as Map;
final lcoin = map.values.first as LelantusCoin;
final isJMint = jindexes.contains(lcoin.index);
final coin = isar_models.LelantusCoin(
walletId: walletId,
txid: lcoin.txId,
value: lcoin.value.toString(),
mintIndex: lcoin.index,
anonymitySetId: lcoin.anonymitySetId,
isUsed: lcoin.isUsed,
isJMint: isJMint,
otherData: null,
);
coins.add(coin);
}
if (coins.isNotEmpty) {
await MainDB.instance.isar.writeTxn(() async {
await MainDB.instance.isar.lelantusCoins.putAll(coins);
});
}
}
}
}
Future<void> _v4(SecureStorageInterface secureStore) async {
await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
await Hive.openBox<dynamic>(DB.boxNamePrefs);
@ -619,4 +569,70 @@ class DbVersionMigrator with WalletDB {
await addressBookBox.deleteFromDisk();
}
Future<void> _v10(SecureStorageInterface secureStore) async {
await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
await Hive.openBox<dynamic>(DB.boxNamePrefs);
final walletsService = WalletsService(secureStorageInterface: secureStore);
final prefs = Prefs.instance;
final walletInfoList = await walletsService.walletNames;
await prefs.init();
await MainDB.instance.initMainDB();
for (final walletId in walletInfoList.keys) {
final info = walletInfoList[walletId]!;
assert(info.walletId == walletId);
if (info.coin == Coin.firo &&
MainDB.instance.isar.lelantusCoins
.where()
.walletIdEqualTo(walletId)
.countSync() ==
0) {
final walletBox = await Hive.openBox<dynamic>(walletId);
final hiveLCoins = DB.instance.get<dynamic>(
boxName: walletId,
key: "_lelantus_coins",
) as List? ??
[];
final jindexes = (DB.instance
.get<dynamic>(boxName: walletId, key: "jindex") as List? ??
[])
.cast<int>();
final List<isar_models.LelantusCoin> coins = [];
for (final e in hiveLCoins) {
final map = e as Map;
final lcoin = map.values.first as LelantusCoin;
final isJMint = jindexes.contains(lcoin.index);
final coin = isar_models.LelantusCoin(
walletId: walletId,
txid: lcoin.txId,
value: lcoin.value.toString(),
mintIndex: lcoin.index,
anonymitySetId: lcoin.anonymitySetId,
isUsed: lcoin.isUsed,
isJMint: isJMint,
otherData: null,
);
coins.add(coin);
}
if (coins.isNotEmpty) {
await MainDB.instance.isar.writeTxn(() async {
await MainDB.instance.isar.lelantusCoins.putAll(coins);
});
}
}
}
}
Future<void> _v11(SecureStorageInterface secureStore) async {
await migrateWalletsToIsar(secureStore: secureStore);
}
}

View file

@ -26,12 +26,13 @@ class DB {
@Deprecated("Left over for migration from old versions of Stack Wallet")
static const String boxNameAddressBook = "addressBook";
static const String boxNameTrades = "exchangeTransactionsBox";
static const String boxNameAllWalletsData = "wallets";
static const String boxNameFavoriteWallets = "favoriteWallets";
// in use
// TODO: migrate
static const String boxNameNodeModels = "nodeModels";
static const String boxNamePrimaryNodes = "primaryNodes";
static const String boxNameAllWalletsData = "wallets";
static const String boxNameNotifications = "notificationModels";
static const String boxNameWatchedTransactions =
"watchedTxNotificationModels";
@ -39,7 +40,6 @@ class DB {
static const String boxNameTradesV2 = "exchangeTradesBox";
static const String boxNameTradeNotes = "tradeNotesBox";
static const String boxNameTradeLookup = "tradeToTxidLookUpBox";
static const String boxNameFavoriteWallets = "favoriteWallets";
static const String boxNameWalletsToDeleteOnStart = "walletsToDeleteOnStart";
static const String boxNamePriceCache = "priceAPIPrice24hCache";
@ -53,8 +53,12 @@ class DB {
// firo only
String _boxNameSetCache({required Coin coin}) =>
"${coin.name}_anonymitySetCache";
String _boxNameSetSparkCache({required Coin coin}) =>
"${coin.name}_anonymitySetSparkCache";
String _boxNameUsedSerialsCache({required Coin coin}) =>
"${coin.name}_usedSerialsCache";
String _boxNameSparkUsedCoinsTagsCache({required Coin coin}) =>
"${coin.name}_sparkUsedCoinsTagsCache";
Box<NodeModel>? _boxNodeModels;
Box<NodeModel>? _boxPrimaryNodes;
@ -75,7 +79,9 @@ class DB {
final Map<Coin, Box<dynamic>> _txCacheBoxes = {};
final Map<Coin, Box<dynamic>> _setCacheBoxes = {};
final Map<Coin, Box<dynamic>> _setSparkCacheBoxes = {};
final Map<Coin, Box<dynamic>> _usedSerialsCacheBoxes = {};
final Map<Coin, Box<dynamic>> _getSparkUsedCoinsTagsCacheBoxes = {};
// exposed for monero
Box<xmr.WalletInfo> get moneroWalletInfoBox => _walletInfoSource!;
@ -197,6 +203,15 @@ class DB {
await Hive.openBox<dynamic>(_boxNameSetCache(coin: coin));
}
Future<Box<dynamic>> getSparkAnonymitySetCacheBox(
{required Coin coin}) async {
if (_setSparkCacheBoxes[coin]?.isOpen != true) {
_setSparkCacheBoxes.remove(coin);
}
return _setSparkCacheBoxes[coin] ??=
await Hive.openBox<dynamic>(_boxNameSetSparkCache(coin: coin));
}
Future<void> closeAnonymitySetCacheBox({required Coin coin}) async {
await _setCacheBoxes[coin]?.close();
}
@ -209,6 +224,16 @@ class DB {
await Hive.openBox<dynamic>(_boxNameUsedSerialsCache(coin: coin));
}
Future<Box<dynamic>> getSparkUsedCoinsTagsCacheBox(
{required Coin coin}) async {
if (_getSparkUsedCoinsTagsCacheBoxes[coin]?.isOpen != true) {
_getSparkUsedCoinsTagsCacheBoxes.remove(coin);
}
return _getSparkUsedCoinsTagsCacheBoxes[coin] ??=
await Hive.openBox<dynamic>(
_boxNameSparkUsedCoinsTagsCache(coin: coin));
}
Future<void> closeUsedSerialsCacheBox({required Coin coin}) async {
await _usedSerialsCacheBoxes[coin]?.close();
}
@ -216,9 +241,12 @@ class DB {
/// Clear all cached transactions for the specified coin
Future<void> clearSharedTransactionCache({required Coin coin}) async {
await deleteAll<dynamic>(boxName: _boxNameTxCache(coin: coin));
if (coin == Coin.firo) {
if (coin == Coin.firo || coin == Coin.firoTestNet) {
await deleteAll<dynamic>(boxName: _boxNameSetCache(coin: coin));
await deleteAll<dynamic>(boxName: _boxNameSetSparkCache(coin: coin));
await deleteAll<dynamic>(boxName: _boxNameUsedSerialsCache(coin: coin));
await deleteAll<dynamic>(
boxName: _boxNameSparkUsedCoinsTagsCache(coin: coin));
}
}
@ -322,5 +350,4 @@ abstract class DBKeys {
static const String isFavorite = "isFavorite";
static const String id = "id";
static const String storedChainHeight = "storedChainHeight";
static const String ethTokenContracts = "ethTokenContracts";
}

View file

@ -21,6 +21,10 @@ import 'package:stackwallet/models/isar/stack_theme.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:stackwallet/wallets/isar/models/spark_coin.dart';
import 'package:stackwallet/wallets/isar/models/token_wallet_info.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info_meta.dart';
import 'package:tuple/tuple.dart';
part '../queries/queries.dart';
@ -58,7 +62,11 @@ class MainDB {
ContactEntrySchema,
OrdinalSchema,
LelantusCoinSchema,
WalletInfoSchema,
TransactionV2Schema,
SparkCoinSchema,
WalletInfoMetaSchema,
TokenWalletInfoSchema,
],
directory: (await StackFileSystem.applicationIsarDirectory()).path,
// inspector: kDebugMode,
@ -69,6 +77,36 @@ class MainDB {
return true;
}
Future<void> putWalletInfo(WalletInfo walletInfo) async {
try {
await isar.writeTxn(() async {
await isar.walletInfo.put(walletInfo);
});
} catch (e) {
throw MainDBException("failed putWalletInfo()", e);
}
}
Future<void> updateWalletInfo(WalletInfo walletInfo) async {
try {
await isar.writeTxn(() async {
final info = await isar.walletInfo
.where()
.walletIdEqualTo(walletInfo.walletId)
.findFirst();
if (info == null) {
throw Exception("updateWalletInfo() called with new WalletInfo."
" Use putWalletInfo()");
}
await isar.walletInfo.deleteByWalletId(walletInfo.walletId);
await isar.walletInfo.put(walletInfo);
});
} catch (e) {
throw MainDBException("failed updateWalletInfo()", e);
}
}
// contact entries
List<ContactEntry> getContactEntries() {
return isar.contactEntrys.where().sortByName().findAllSync();
@ -390,8 +428,8 @@ class MainDB {
await isar.transactionV2s.where().walletIdEqualTo(walletId).count();
final addressCount = await getAddresses(walletId).count();
final utxoCount = await getUTXOs(walletId).count();
final lelantusCoinCount =
await isar.lelantusCoins.where().walletIdEqualTo(walletId).count();
// final lelantusCoinCount =
// await isar.lelantusCoins.where().walletIdEqualTo(walletId).count();
await isar.writeTxn(() async {
const paginateLimit = 50;
@ -439,16 +477,23 @@ class MainDB {
}
// lelantusCoins
for (int i = 0; i < lelantusCoinCount; i += paginateLimit) {
final lelantusCoinIds = await isar.lelantusCoins
await isar.lelantusCoins.where().walletIdEqualTo(walletId).deleteAll();
// for (int i = 0; i < lelantusCoinCount; i += paginateLimit) {
// final lelantusCoinIds = await isar.lelantusCoins
// .where()
// .walletIdEqualTo(walletId)
// .offset(i)
// .limit(paginateLimit)
// .idProperty()
// .findAll();
// await isar.lelantusCoins.deleteAll(lelantusCoinIds);
// }
// spark coins
await isar.sparkCoins
.where()
.walletIdEqualTo(walletId)
.offset(i)
.limit(paginateLimit)
.idProperty()
.findAll();
await isar.lelantusCoins.deleteAll(lelantusCoinIds);
}
.walletIdEqualToAnyLTagHash(walletId)
.deleteAll();
});
}

View file

@ -0,0 +1,214 @@
import 'dart:convert';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/wallets/isar/models/token_wallet_info.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info_meta.dart';
import 'package:stackwallet/wallets/wallet/supporting/epiccash_wallet_info_extension.dart';
Future<void> migrateWalletsToIsar({
required SecureStorageInterface secureStore,
}) async {
await MainDB.instance.initMainDB();
// ensure fresh
await MainDB.instance.isar
.writeTxn(() async => await MainDB.instance.isar.transactionV2s.clear());
final allWalletsBox = await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
final names = DB.instance
.get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
if (names == null) {
// no wallets to migrate
return;
}
//
// Parse the old data from the Hive map into a nice list
//
final List<
({
Coin coin,
String name,
String walletId,
})> oldInfo = Map<String, dynamic>.from(names).values.map((e) {
final map = e as Map;
return (
coin: Coin.values.byName(map["coin"] as String),
walletId: map["id"] as String,
name: map["name"] as String,
);
}).toList();
//
// Get current ordered list of favourite wallet Ids
//
final List<String> favourites =
(await Hive.openBox<String>(DB.boxNameFavoriteWallets)).values.toList();
final List<(WalletInfo, WalletInfoMeta)> newInfo = [];
final List<TokenWalletInfo> tokenInfo = [];
final List<TransactionNote> migratedNotes = [];
//
// Convert each old info into the new Isar WalletInfo
//
for (final old in oldInfo) {
final walletBox = await Hive.openBox<dynamic>(old.walletId);
//
// First handle transaction notes
//
final newNoteCount = await MainDB.instance.isar.transactionNotes
.where()
.walletIdEqualTo(old.walletId)
.count();
if (newNoteCount == 0) {
final map = walletBox.get('notes') as Map?;
if (map != null) {
final notes = Map<String, String>.from(map);
for (final txid in notes.keys) {
final note = notes[txid];
if (note != null && note.isNotEmpty) {
final newNote = TransactionNote(
walletId: old.walletId,
txid: txid,
value: note,
);
migratedNotes.add(newNote);
}
}
}
}
// reset stellar + tezos address type
if (old.coin == Coin.stellar ||
old.coin == Coin.stellarTestnet ||
old.coin == Coin.tezos) {
await MainDB.instance.deleteWalletBlockchainData(old.walletId);
}
//
// Set other data values
//
Map<String, dynamic> otherData = {};
final List<String>? tokenContractAddresses = walletBox.get(
"ethTokenContracts",
) as List<String>?;
if (tokenContractAddresses?.isNotEmpty == true) {
otherData[WalletInfoKeys.tokenContractAddresses] = tokenContractAddresses;
for (final address in tokenContractAddresses!) {
final contract = await MainDB.instance.isar.ethContracts
.where()
.addressEqualTo(address)
.findFirst();
if (contract != null) {
tokenInfo.add(
TokenWalletInfo(
walletId: old.walletId,
tokenAddress: address,
tokenFractionDigits: contract.decimals,
),
);
}
}
}
// epiccash specifics
if (old.coin == Coin.epicCash) {
final epicWalletInfo = ExtraEpiccashWalletInfo.fromMap({
"receivingIndex": walletBox.get("receivingIndex") as int? ?? 0,
"changeIndex": walletBox.get("changeIndex") as int? ?? 0,
"slatesToAddresses": walletBox.get("slate_to_address") as Map? ?? {},
"slatesToCommits": walletBox.get("slatesToCommits") as Map? ?? {},
"lastScannedBlock": walletBox.get("lastScannedBlock") as int? ?? 0,
"restoreHeight": walletBox.get("restoreHeight") as int? ?? 0,
"creationHeight": walletBox.get("creationHeight") as int? ?? 0,
});
otherData[WalletInfoKeys.epiccashData] = jsonEncode(
epicWalletInfo.toMap(),
);
} else if (old.coin == Coin.firo || old.coin == Coin.firoTestNet) {
otherData[WalletInfoKeys.lelantusCoinIsarRescanRequired] = walletBox
.get(WalletInfoKeys.lelantusCoinIsarRescanRequired) as bool? ??
true;
}
//
// Clear out any keys with null values as they are not needed
//
otherData.removeWhere((key, value) => value == null);
final infoMeta = WalletInfoMeta(
walletId: old.walletId,
isMnemonicVerified: allWalletsBox
.get("${old.walletId}_mnemonicHasBeenVerified") as bool? ??
false,
);
final info = WalletInfo(
coinName: old.coin.name,
walletId: old.walletId,
name: old.name,
mainAddressType: old.coin.primaryAddressType,
favouriteOrderIndex: favourites.indexOf(old.walletId),
cachedChainHeight: walletBox.get(
DBKeys.storedChainHeight,
) as int? ??
0,
cachedBalanceString: walletBox.get(
DBKeys.cachedBalance,
) as String?,
cachedBalanceSecondaryString: walletBox.get(
DBKeys.cachedBalanceSecondary,
) as String?,
otherDataJsonString: jsonEncode(otherData),
);
newInfo.add((info, infoMeta));
}
if (migratedNotes.isNotEmpty) {
await MainDB.instance.isar.writeTxn(() async {
await MainDB.instance.isar.transactionNotes.putAll(migratedNotes);
});
}
if (newInfo.isNotEmpty) {
await MainDB.instance.isar.writeTxn(() async {
await MainDB.instance.isar.walletInfo
.putAll(newInfo.map((e) => e.$1).toList());
await MainDB.instance.isar.walletInfoMeta
.putAll(newInfo.map((e) => e.$2).toList());
if (tokenInfo.isNotEmpty) {
await MainDB.instance.isar.tokenWalletInfo.putAll(tokenInfo);
}
});
}
await _cleanupOnSuccess(
walletIds: newInfo.map((e) => e.$1.walletId).toList());
}
Future<void> _cleanupOnSuccess({required List<String> walletIds}) async {
await Hive.deleteBoxFromDisk(DB.boxNameFavoriteWallets);
await Hive.deleteBoxFromDisk(DB.boxNameAllWalletsData);
for (final walletId in walletIds) {
await Hive.deleteBoxFromDisk(walletId);
}
}

View file

@ -12,24 +12,24 @@ import 'dart:convert';
import 'dart:math';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:string_validator/string_validator.dart';
class CachedElectrumX {
final ElectrumX electrumXClient;
class CachedElectrumXClient {
final ElectrumXClient electrumXClient;
static const minCacheConfirms = 30;
const CachedElectrumX({
const CachedElectrumXClient({
required this.electrumXClient,
});
factory CachedElectrumX.from({
required ElectrumX electrumXClient,
factory CachedElectrumXClient.from({
required ElectrumXClient electrumXClient,
}) =>
CachedElectrumX(
CachedElectrumXClient(
electrumXClient: electrumXClient,
);
@ -56,7 +56,7 @@ class CachedElectrumX {
set = Map<String, dynamic>.from(cachedSet);
}
final newSet = await electrumXClient.getAnonymitySet(
final newSet = await electrumXClient.getLelantusAnonymitySet(
groupId: groupId,
blockhash: set["blockHash"] as String,
);
@ -107,6 +107,63 @@ class CachedElectrumX {
}
}
Future<Map<String, dynamic>> getSparkAnonymitySet({
required String groupId,
String blockhash = "",
required Coin coin,
}) async {
try {
final box = await DB.instance.getSparkAnonymitySetCacheBox(coin: coin);
final cachedSet = box.get(groupId) as Map?;
Map<String, dynamic> set;
// null check to see if there is a cached set
if (cachedSet == null) {
set = {
"coinGroupID": int.parse(groupId),
"blockHash": blockhash,
"setHash": "",
"coins": <dynamic>[],
};
} else {
set = Map<String, dynamic>.from(cachedSet);
}
final newSet = await electrumXClient.getSparkAnonymitySet(
coinGroupId: groupId,
startBlockHash: set["blockHash"] as String,
);
// update set with new data
if (newSet["setHash"] != "" && set["setHash"] != newSet["setHash"]) {
set["setHash"] = newSet["setHash"];
set["blockHash"] = newSet["blockHash"];
for (int i = (newSet["coins"] as List).length - 1; i >= 0; i--) {
// TODO verify this is correct (or append?)
if ((set["coins"] as List)
.where((e) => e[0] == newSet["coins"][i][0])
.isEmpty) {
set["coins"].insert(0, newSet["coins"][i]);
}
}
// save set to db
await box.put(groupId, set);
Logging.instance.log(
"Updated current anonymity set for ${coin.name} with group ID $groupId",
level: LogLevel.Info,
);
}
return set;
} catch (e, s) {
Logging.instance.log(
"Failed to process CachedElectrumX.getSparkAnonymitySet(): $e\n$s",
level: LogLevel.Error);
rethrow;
}
}
String base64ToHex(String source) =>
base64Decode(LineSplitter.split(source).join())
.map((e) => e.toRadixString(16).padLeft(2, '0'))
@ -136,6 +193,7 @@ class CachedElectrumX {
result.remove("hex");
result.remove("lelantusData");
result.remove("sparkData");
if (result["confirmations"] != null &&
result["confirmations"] as int > minCacheConfirms) {
@ -173,18 +231,19 @@ class CachedElectrumX {
cachedSerials.length - 100, // 100 being some arbitrary buffer
);
final serials = await electrumXClient.getUsedCoinSerials(
final serials = await electrumXClient.getLelantusUsedCoinSerials(
startNumber: startNumber,
);
Set<String> newSerials = {};
for (final element in (serials["serials"] as List)) {
if (!isHexadecimal(element as String)) {
newSerials.add(base64ToHex(element));
} else {
newSerials.add(element);
}
final newSerials = List<String>.from(serials["serials"] as List)
.map((e) => !isHexadecimal(e) ? base64ToHex(e) : e)
.toSet();
// ensure we are getting some overlap so we know we are not missing any
if (cachedSerials.isNotEmpty && newSerials.isNotEmpty) {
assert(cachedSerials.intersection(newSerials).isNotEmpty);
}
cachedSerials.addAll(newSerials);
final resultingList = cachedSerials.toList();
@ -197,14 +256,62 @@ class CachedElectrumX {
return resultingList;
} catch (e, s) {
Logging.instance.log(
"Failed to process CachedElectrumX.getTransaction(): $e\n$s",
level: LogLevel.Error);
"Failed to process CachedElectrumX.getUsedCoinSerials(): $e\n$s",
level: LogLevel.Error,
);
rethrow;
}
}
Future<Set<String>> getSparkUsedCoinsTags({
required Coin coin,
}) async {
try {
final box = await DB.instance.getSparkUsedCoinsTagsCacheBox(coin: coin);
final _list = box.get("tags") as List?;
Set<String> cachedTags =
_list == null ? {} : List<String>.from(_list).toSet();
final startNumber = max(
0,
cachedTags.length - 100, // 100 being some arbitrary buffer
);
final tags = await electrumXClient.getSparkUsedCoinsTags(
startNumber: startNumber,
);
// final newSerials = List<String>.from(serials["serials"] as List)
// .map((e) => !isHexadecimal(e) ? base64ToHex(e) : e)
// .toSet();
// ensure we are getting some overlap so we know we are not missing any
if (cachedTags.isNotEmpty && tags.isNotEmpty) {
assert(cachedTags.intersection(tags).isNotEmpty);
}
cachedTags.addAll(tags);
await box.put(
"tags",
cachedTags.toList(),
);
return cachedTags;
} catch (e, s) {
Logging.instance.log(
"Failed to process CachedElectrumX.getSparkUsedCoinsTags(): $e\n$s",
level: LogLevel.Error,
);
rethrow;
}
}
/// Clear all cached transactions for the specified coin
Future<void> clearSharedTransactionCache({required Coin coin}) async {
await DB.instance.clearSharedTransactionCache(coin: coin);
await DB.instance.closeAnonymitySetCacheBox(coin: coin);
}
}

View file

@ -15,6 +15,8 @@ import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:decimal/decimal.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart';
import 'package:mutex/mutex.dart';
import 'package:stackwallet/electrumx_rpc/rpc.dart';
import 'package:stackwallet/exceptions/electrumx/no_such_transaction.dart';
@ -58,7 +60,7 @@ class ElectrumXNode {
}
}
class ElectrumX {
class ElectrumXClient {
String get host => _host;
late String _host;
@ -81,7 +83,7 @@ class ElectrumX {
// add finalizer to cancel stream subscription when all references to an
// instance of ElectrumX becomes inaccessible
static final Finalizer<ElectrumX> _finalizer = Finalizer(
static final Finalizer<ElectrumXClient> _finalizer = Finalizer(
(p0) {
p0._torPreferenceListener?.cancel();
p0._torStatusListener?.cancel();
@ -93,7 +95,7 @@ class ElectrumX {
final Mutex _torConnectingLock = Mutex();
bool _requireMutex = false;
ElectrumX({
ElectrumXClient({
required String host,
required int port,
required bool useSSL,
@ -158,14 +160,14 @@ class ElectrumX {
);
}
factory ElectrumX.from({
factory ElectrumXClient.from({
required ElectrumXNode node,
required Prefs prefs,
required List<ElectrumXNode> failovers,
TorService? torService,
EventBus? globalEventBusForTesting,
}) {
return ElectrumX(
return ElectrumXClient(
host: node.address,
port: node.port,
useSSL: node.useSSL,
@ -467,9 +469,9 @@ class ElectrumX {
/// and the binary header as a hexadecimal string.
/// Ex:
/// {
// "height": 520481,
// "hex": "00000020890208a0ae3a3892aa047c5468725846577cfcd9b512b50000000000000000005dc2b02f2d297a9064ee103036c14d678f9afc7e3d9409cf53fd58b82e938e8ecbeca05a2d2103188ce804c4"
// }
/// "height": 520481,
/// "hex": "00000020890208a0ae3a3892aa047c5468725846577cfcd9b512b50000000000000000005dc2b02f2d297a9064ee103036c14d678f9afc7e3d9409cf53fd58b82e938e8ecbeca05a2d2103188ce804c4"
/// }
Future<Map<String, dynamic>> getBlockHeadTip({String? requestID}) async {
try {
final response = await request(
@ -493,15 +495,15 @@ class ElectrumX {
///
/// Returns a map with server information
/// Ex:
// {
// "genesis_hash": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
// "hosts": {"14.3.140.101": {"tcp_port": 51001, "ssl_port": 51002}},
// "protocol_max": "1.0",
// "protocol_min": "1.0",
// "pruning": null,
// "server_version": "ElectrumX 1.0.17",
// "hash_function": "sha256"
// }
/// {
/// "genesis_hash": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
/// "hosts": {"14.3.140.101": {"tcp_port": 51001, "ssl_port": 51002}},
/// "protocol_max": "1.0",
/// "protocol_min": "1.0",
/// "pruning": null,
/// "server_version": "ElectrumX 1.0.17",
/// "hash_function": "sha256"
/// }
Future<Map<String, dynamic>> getServerFeatures({String? requestID}) async {
try {
final response = await request(
@ -567,20 +569,24 @@ class ElectrumX {
/// Returns a list of maps that contain the tx_hash and height of the tx.
/// Ex:
/// [
// {
// "height": 200004,
// "tx_hash": "acc3758bd2a26f869fcc67d48ff30b96464d476bca82c1cd6656e7d506816412"
// },
// {
// "height": 215008,
// "tx_hash": "f3e1bf48975b8d6060a9de8884296abb80be618dc00ae3cb2f6cee3085e09403"
// }
// ]
/// {
/// "height": 200004,
/// "tx_hash": "acc3758bd2a26f869fcc67d48ff30b96464d476bca82c1cd6656e7d506816412"
/// },
/// {
/// "height": 215008,
/// "tx_hash": "f3e1bf48975b8d6060a9de8884296abb80be618dc00ae3cb2f6cee3085e09403"
/// }
/// ]
Future<List<Map<String, dynamic>>> getHistory({
required String scripthash,
String? requestID,
}) async {
try {
int retryCount = 3;
dynamic result;
while (retryCount > 0 && result is! List) {
final response = await request(
requestID: requestID,
command: 'blockchain.scripthash.get_history',
@ -589,7 +595,12 @@ class ElectrumX {
scripthash,
],
);
return List<Map<String, dynamic>>.from(response["result"] as List);
result = response["result"];
retryCount--;
}
return List<Map<String, dynamic>>.from(result as List);
} catch (e) {
rethrow;
}
@ -618,19 +629,19 @@ class ElectrumX {
/// Returns a list of maps.
/// Ex:
/// [
// {
// "tx_pos": 0,
// "value": 45318048,
// "tx_hash": "9f2c45a12db0144909b5db269415f7319179105982ac70ed80d76ea79d923ebf",
// "height": 437146
// },
// {
// "tx_pos": 0,
// "value": 919195,
// "tx_hash": "3d2290c93436a3e964cfc2f0950174d8847b1fbe3946432c4784e168da0f019f",
// "height": 441696
// }
// ]
/// {
/// "tx_pos": 0,
/// "value": 45318048,
/// "tx_hash": "9f2c45a12db0144909b5db269415f7319179105982ac70ed80d76ea79d923ebf",
/// "height": 437146
/// },
/// {
/// "tx_pos": 0,
/// "value": 919195,
/// "tx_hash": "3d2290c93436a3e964cfc2f0950174d8847b1fbe3946432c4784e168da0f019f",
/// "height": 441696
/// }
/// ]
Future<List<Map<String, dynamic>>> getUTXOs({
required String scripthash,
String? requestID,
@ -751,7 +762,7 @@ class ElectrumX {
}
}
/// Returns the whole anonymity set for denomination in the groupId.
/// Returns the whole Lelantus anonymity set for denomination in the groupId.
///
/// ex:
/// {
@ -765,7 +776,7 @@ class ElectrumX {
/// [dynamic list of length 4],
/// ]
/// }
Future<Map<String, dynamic>> getAnonymitySet({
Future<Map<String, dynamic>> getLelantusAnonymitySet({
String groupId = "1",
String blockhash = "",
String? requestID,
@ -792,8 +803,11 @@ class ElectrumX {
//TODO add example to docs
///
///
/// Returns the block height and groupId of pubcoin.
Future<dynamic> getMintData({dynamic mints, String? requestID}) async {
/// Returns the block height and groupId of a Lelantus pubcoin.
Future<dynamic> getLelantusMintData({
dynamic mints,
String? requestID,
}) async {
try {
final response = await request(
requestID: requestID,
@ -809,12 +823,16 @@ class ElectrumX {
}
//TODO add example to docs
/// Returns the whole set of the used coin serials.
Future<Map<String, dynamic>> getUsedCoinSerials({
/// Returns the whole set of the used Lelantus coin serials.
Future<Map<String, dynamic>> getLelantusUsedCoinSerials({
String? requestID,
required int startNumber,
}) async {
try {
int retryCount = 3;
dynamic result;
while (retryCount > 0 && result is! List) {
final response = await request(
requestID: requestID,
command: 'lelantus.getusedcoinserials',
@ -823,17 +841,22 @@ class ElectrumX {
],
requestTimeout: const Duration(minutes: 2),
);
return Map<String, dynamic>.from(response["result"] as Map);
result = response["result"];
retryCount--;
}
return Map<String, dynamic>.from(result as Map);
} catch (e) {
Logging.instance.log(e, level: LogLevel.Error);
rethrow;
}
}
/// Returns the latest set id
/// Returns the latest Lelantus set id
///
/// ex: 1
Future<int> getLatestCoinId({String? requestID}) async {
Future<int> getLelantusLatestCoinId({String? requestID}) async {
try {
final response = await request(
requestID: requestID,
@ -846,31 +869,127 @@ class ElectrumX {
}
}
// /// Returns about 13 megabytes of json data as of march 2, 2022
// Future<Map<String, dynamic>> getCoinsForRecovery(
// {dynamic setId, String requestID}) async {
// try {
// final response = await request(
// requestID: requestID,
// command: 'lelantus.getcoinsforrecovery',
// args: [
// setId ?? 1,
// ],
// );
// return response["result"];
// } catch (e) {
// Logging.instance.log(e);
// throw e;
// }
// }
// ============== Spark ======================================================
// New Spark ElectrumX methods:
// > Functions provided by ElectrumX servers
// > // >
/// Returns the whole Spark anonymity set for denomination in the groupId.
///
/// Takes [coinGroupId] and [startBlockHash], if the last is empty it returns full set,
/// otherwise returns mint after that block, we need to call this to keep our
/// anonymity set data up to date.
///
/// Returns blockHash (last block hash),
/// setHash (hash of current set)
/// and coins (the list of pairs serialized coin and tx hash)
Future<Map<String, dynamic>> getSparkAnonymitySet({
String coinGroupId = "1",
String startBlockHash = "",
String? requestID,
}) async {
try {
Logging.instance.log("attempting to fetch spark.getsparkanonymityset...",
level: LogLevel.Info);
final response = await request(
requestID: requestID,
command: 'spark.getsparkanonymityset',
args: [
coinGroupId,
startBlockHash,
],
);
Logging.instance.log("Fetching spark.getsparkanonymityset finished",
level: LogLevel.Info);
return Map<String, dynamic>.from(response["result"] as Map);
} catch (e) {
rethrow;
}
}
/// Takes [startNumber], if it is 0, we get the full set,
/// otherwise the used tags after that number
Future<Set<String>> getSparkUsedCoinsTags({
String? requestID,
required int startNumber,
}) async {
try {
final response = await request(
requestID: requestID,
command: 'spark.getusedcoinstags',
args: [
"$startNumber",
],
requestTimeout: const Duration(minutes: 2),
);
final map = Map<String, dynamic>.from(response["result"] as Map);
final set = Set<String>.from(map["tags"] as List);
return await compute(_ffiHashTagsComputeWrapper, set);
} catch (e) {
Logging.instance.log(e, level: LogLevel.Error);
rethrow;
}
}
/// Takes a list of [sparkCoinHashes] and returns the set id and block height
/// for each coin
///
/// arg:
/// {
/// "coinHashes": [
/// "b476ed2b374bb081ea51d111f68f0136252521214e213d119b8dc67b92f5a390",
/// "b476ed2b374bb081ea51d111f68f0136252521214e213d119b8dc67b92f5a390",
/// ]
/// }
Future<List<Map<String, dynamic>>> getSparkMintMetaData({
String? requestID,
required List<String> sparkCoinHashes,
}) async {
try {
final response = await request(
requestID: requestID,
command: 'spark.getsparkmintmetadata',
args: [
{
"coinHashes": sparkCoinHashes,
},
],
);
return List<Map<String, dynamic>>.from(response["result"] as List);
} catch (e) {
Logging.instance.log(e, level: LogLevel.Error);
rethrow;
}
}
/// Returns the latest Spark set id
///
/// ex: 1
Future<int> getSparkLatestCoinId({
String? requestID,
}) async {
try {
final response = await request(
requestID: requestID,
command: 'spark.getsparklatestcoinid',
);
return response["result"] as int;
} catch (e) {
Logging.instance.log(e, level: LogLevel.Error);
rethrow;
}
}
// ===========================================================================
/// Get the current fee rate.
///
/// Returns a map with the kay "rate" that corresponds to the free rate in satoshis
/// Ex:
/// {
// "rate": 1000,
// }
/// "rate": 1000,
/// }
Future<Map<String, dynamic>> getFeeRate({String? requestID}) async {
try {
final response = await request(
@ -920,3 +1039,7 @@ class ElectrumX {
}
}
}
Set<String> _ffiHashTagsComputeWrapper(Set<String> base64Tags) {
return LibSpark.hashTags(base64Tags: base64Tags);
}

View file

@ -109,7 +109,9 @@ class JsonRPC {
"JsonRPC request: opening socket $host:$port",
level: LogLevel.Info,
);
await connect();
await connect().timeout(requestTimeout, onTimeout: () {
throw Exception("Request timeout: $jsonRpcRequest");
});
}
} else {
if (_socksSocket == null) {
@ -117,7 +119,9 @@ class JsonRPC {
"JsonRPC request: opening SOCKS socket to $host:$port",
level: LogLevel.Info,
);
await connect();
await connect().timeout(requestTimeout, onTimeout: () {
throw Exception("Request timeout: $jsonRpcRequest");
});
}
}
});

View file

@ -12,6 +12,7 @@ import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:coinlib_flutter/coinlib_flutter.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_info.dart';
@ -27,6 +28,7 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:isar/isar.dart';
import 'package:keyboard_dismisser/keyboard_dismisser.dart';
import 'package:path_provider/path_provider.dart';
import 'package:stackwallet/db/db_version_migration.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart';
@ -44,6 +46,7 @@ import 'package:stackwallet/pages/pinpad_views/create_pin_view.dart';
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_encrypted_string_view.dart';
import 'package:stackwallet/pages_desktop_specific/password/desktop_login_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/global/base_currencies_provider.dart';
@ -63,13 +66,13 @@ import 'package:stackwallet/services/trade_service.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/themes/theme_service.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/db_version_migration.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/all_wallets_info_provider.dart';
import 'package:stackwallet/widgets/crypto_notifications.dart';
import 'package:window_size/window_size.dart';
@ -79,8 +82,15 @@ final openedFromSWBFileStringStateProvider =
// main() is the entry point to the app. It initializes Hive (local database),
// runs the MyApp widget and checks for new users, caching the value in the
// miscellaneous box for later use
void main() async {
void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized();
if (Util.isDesktop && args.length == 2 && args.first == "-d") {
StackFileSystem.overrideDir = args.last;
}
final loadCoinlibFuture = loadCoinlib();
GoogleFonts.config.allowRuntimeFetching = false;
if (Platform.isIOS) {
Util.libraryPath = await getLibraryDirectory();
@ -213,6 +223,8 @@ void main() async {
// overlays: [SystemUiOverlay.bottom]);
await NotificationApi.init();
await loadCoinlibFuture;
await MainDB.instance.initMainDB();
ThemeService.instance.init(MainDB.instance);
@ -344,9 +356,10 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
prefs: ref.read(prefsChangeNotifierProvider),
);
ref.read(priceAnd24hChangeNotifierProvider).start(true);
await ref
.read(walletsChangeNotifierProvider)
.load(ref.read(prefsChangeNotifierProvider));
await ref.read(pWallets).load(
ref.read(prefsChangeNotifierProvider),
ref.read(mainDBProvider),
);
loadingCompleter.complete();
// TODO: this should probably run unawaited. Keep commented out for now as proper community nodes ui hasn't been implemented yet
// unawaited(_nodeService.updateCommunityNodes());
@ -735,7 +748,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// FlutterNativeSplash.remove();
if (ref.read(walletsChangeNotifierProvider).hasWallets ||
if (ref.read(pAllWalletsInfo).isNotEmpty ||
ref.read(prefsChangeNotifierProvider).hasPin) {
// return HomeView();

View file

@ -11,6 +11,7 @@
import 'dart:convert';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
class Balance {
final Amount total;
@ -25,6 +26,20 @@ class Balance {
required this.pendingSpendable,
});
factory Balance.zeroForCoin({required Coin coin}) {
final amount = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
return Balance(
total: amount,
spendable: amount,
blockedTotal: amount,
pendingSpendable: amount,
);
}
String toJsonIgnoreCoin() => jsonEncode({
"total": total.toJsonString(),
"spendable": spendable.toJsonString(),

View file

@ -148,7 +148,7 @@ class Address extends CryptoCurrencyAddress {
}
}
// do not modify
// do not modify unless you know what the consequences are
enum AddressType {
p2pkh,
p2sh,
@ -159,7 +159,11 @@ enum AddressType {
nonWallet,
ethereum,
nano,
banano;
banano,
spark,
stellar,
tezos,
;
String get readableName {
switch (this) {
@ -183,6 +187,12 @@ enum AddressType {
return "Nano";
case AddressType.banano:
return "Banano";
case AddressType.spark:
return "Spark";
case AddressType.stellar:
return "Stellar";
case AddressType.tezos:
return "Tezos";
}
}
}

View file

@ -263,6 +263,9 @@ const _AddresstypeEnumValueMap = {
'ethereum': 7,
'nano': 8,
'banano': 9,
'spark': 10,
'stellar': 11,
'tezos': 12,
};
const _AddresstypeValueEnumMap = {
0: AddressType.p2pkh,
@ -275,6 +278,9 @@ const _AddresstypeValueEnumMap = {
7: AddressType.ethereum,
8: AddressType.nano,
9: AddressType.banano,
10: AddressType.spark,
11: AddressType.stellar,
12: AddressType.tezos,
};
Id _addressGetId(Address object) {

View file

@ -252,5 +252,8 @@ enum TransactionSubType {
mint, // firo specific
join, // firo specific
ethToken, // eth token
cashFusion;
cashFusion,
sparkMint, // firo specific
sparkSpend, // firo specific
ordinal;
}

View file

@ -365,6 +365,9 @@ const _TransactionsubTypeEnumValueMap = {
'join': 3,
'ethToken': 4,
'cashFusion': 5,
'sparkMint': 6,
'sparkSpend': 7,
'ordinal': 8,
};
const _TransactionsubTypeValueEnumMap = {
0: TransactionSubType.none,
@ -373,6 +376,9 @@ const _TransactionsubTypeValueEnumMap = {
3: TransactionSubType.join,
4: TransactionSubType.ethToken,
5: TransactionSubType.cashFusion,
6: TransactionSubType.sparkMint,
7: TransactionSubType.sparkSpend,
8: TransactionSubType.ordinal,
};
const _TransactiontypeEnumValueMap = {
'outgoing': 0,

View file

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:isar/isar.dart';
part 'input_v2.g.dart';
@ -44,6 +46,7 @@ class OutpointV2 {
@Embedded()
class InputV2 {
late final String? scriptSigHex;
late final String? scriptSigAsm;
late final int? sequence;
late final OutpointV2? outpoint;
late final List<String> addresses;
@ -63,6 +66,7 @@ class InputV2 {
static InputV2 isarCantDoRequiredInDefaultConstructor({
required String? scriptSigHex,
required String? scriptSigAsm,
required int? sequence,
required OutpointV2? outpoint,
required List<String> addresses,
@ -74,6 +78,7 @@ class InputV2 {
}) =>
InputV2()
..scriptSigHex = scriptSigHex
..scriptSigAsm = scriptSigAsm
..sequence = sequence
..outpoint = outpoint
..addresses = List.unmodifiable(addresses)
@ -83,8 +88,41 @@ class InputV2 {
..coinbase = coinbase
..walletOwns = walletOwns;
static InputV2 fromElectrumxJson({
required Map<String, dynamic> json,
required OutpointV2? outpoint,
required List<String> addresses,
required String valueStringSats,
required String? coinbase,
required bool walletOwns,
}) {
final dynamicWitness = json["witness"] ?? json["txinwitness"];
final String? witness;
if (dynamicWitness is Map || dynamicWitness is List) {
witness = jsonEncode(dynamicWitness);
} else if (dynamicWitness is String) {
witness = dynamicWitness;
} else {
witness = null;
}
return InputV2()
..scriptSigHex = json["scriptSig"]?["hex"] as String?
..scriptSigAsm = json["scriptSig"]?["asm"] as String?
..sequence = json["sequence"] as int?
..outpoint = outpoint
..addresses = List.unmodifiable(addresses)
..valueStringSats = valueStringSats
..witness = witness
..innerRedeemScriptAsm = json["innerRedeemscriptAsm"] as String?
..coinbase = coinbase
..walletOwns = walletOwns;
}
InputV2 copyWith({
String? scriptSigHex,
String? scriptSigAsm,
int? sequence,
OutpointV2? outpoint,
List<String>? addresses,
@ -96,6 +134,7 @@ class InputV2 {
}) {
return InputV2.isarCantDoRequiredInDefaultConstructor(
scriptSigHex: scriptSigHex ?? this.scriptSigHex,
scriptSigAsm: scriptSigAsm ?? this.scriptSigAsm,
sequence: sequence ?? this.sequence,
outpoint: outpoint ?? this.outpoint,
addresses: addresses ?? this.addresses,
@ -111,6 +150,7 @@ class InputV2 {
String toString() {
return 'InputV2(\n'
' scriptSigHex: $scriptSigHex,\n'
' scriptSigAsm: $scriptSigAsm,\n'
' sequence: $sequence,\n'
' outpoint: $outpoint,\n'
' addresses: $addresses,\n'

View file

@ -357,28 +357,33 @@ const InputV2Schema = Schema(
type: IsarType.object,
target: r'OutpointV2',
),
r'scriptSigHex': PropertySchema(
r'scriptSigAsm': PropertySchema(
id: 4,
name: r'scriptSigAsm',
type: IsarType.string,
),
r'scriptSigHex': PropertySchema(
id: 5,
name: r'scriptSigHex',
type: IsarType.string,
),
r'sequence': PropertySchema(
id: 5,
id: 6,
name: r'sequence',
type: IsarType.long,
),
r'valueStringSats': PropertySchema(
id: 6,
id: 7,
name: r'valueStringSats',
type: IsarType.string,
),
r'walletOwns': PropertySchema(
id: 7,
id: 8,
name: r'walletOwns',
type: IsarType.bool,
),
r'witness': PropertySchema(
id: 8,
id: 9,
name: r'witness',
type: IsarType.string,
)
@ -422,6 +427,12 @@ int _inputV2EstimateSize(
value, allOffsets[OutpointV2]!, allOffsets);
}
}
{
final value = object.scriptSigAsm;
if (value != null) {
bytesCount += 3 + value.length * 3;
}
}
{
final value = object.scriptSigHex;
if (value != null) {
@ -453,11 +464,12 @@ void _inputV2Serialize(
OutpointV2Schema.serialize,
object.outpoint,
);
writer.writeString(offsets[4], object.scriptSigHex);
writer.writeLong(offsets[5], object.sequence);
writer.writeString(offsets[6], object.valueStringSats);
writer.writeBool(offsets[7], object.walletOwns);
writer.writeString(offsets[8], object.witness);
writer.writeString(offsets[4], object.scriptSigAsm);
writer.writeString(offsets[5], object.scriptSigHex);
writer.writeLong(offsets[6], object.sequence);
writer.writeString(offsets[7], object.valueStringSats);
writer.writeBool(offsets[8], object.walletOwns);
writer.writeString(offsets[9], object.witness);
}
InputV2 _inputV2Deserialize(
@ -475,11 +487,12 @@ InputV2 _inputV2Deserialize(
OutpointV2Schema.deserialize,
allOffsets,
);
object.scriptSigHex = reader.readStringOrNull(offsets[4]);
object.sequence = reader.readLongOrNull(offsets[5]);
object.valueStringSats = reader.readString(offsets[6]);
object.walletOwns = reader.readBool(offsets[7]);
object.witness = reader.readStringOrNull(offsets[8]);
object.scriptSigAsm = reader.readStringOrNull(offsets[4]);
object.scriptSigHex = reader.readStringOrNull(offsets[5]);
object.sequence = reader.readLongOrNull(offsets[6]);
object.valueStringSats = reader.readString(offsets[7]);
object.walletOwns = reader.readBool(offsets[8]);
object.witness = reader.readStringOrNull(offsets[9]);
return object;
}
@ -505,12 +518,14 @@ P _inputV2DeserializeProp<P>(
case 4:
return (reader.readStringOrNull(offset)) as P;
case 5:
return (reader.readLongOrNull(offset)) as P;
return (reader.readStringOrNull(offset)) as P;
case 6:
return (reader.readString(offset)) as P;
return (reader.readLongOrNull(offset)) as P;
case 7:
return (reader.readBool(offset)) as P;
return (reader.readString(offset)) as P;
case 8:
return (reader.readBool(offset)) as P;
case 9:
return (reader.readStringOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
@ -1055,6 +1070,154 @@ extension InputV2QueryFilter
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigAsmIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'scriptSigAsm',
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition>
scriptSigAsmIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'scriptSigAsm',
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigAsmEqualTo(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'scriptSigAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigAsmGreaterThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'scriptSigAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigAsmLessThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'scriptSigAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigAsmBetween(
String? lower,
String? upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'scriptSigAsm',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigAsmStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'scriptSigAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigAsmEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'scriptSigAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigAsmContains(
String value,
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'scriptSigAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigAsmMatches(
String pattern,
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'scriptSigAsm',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigAsmIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'scriptSigAsm',
value: '',
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition>
scriptSigAsmIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'scriptSigAsm',
value: '',
));
});
}
QueryBuilder<InputV2, InputV2, QAfterFilterCondition> scriptSigHexIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(

View file

@ -6,6 +6,7 @@ part 'output_v2.g.dart';
@Embedded()
class OutputV2 {
late final String scriptPubKeyHex;
late final String? scriptPubKeyAsm;
late final String valueStringSats;
late final List<String> addresses;
@ -18,24 +19,28 @@ class OutputV2 {
static OutputV2 isarCantDoRequiredInDefaultConstructor({
required String scriptPubKeyHex,
String? scriptPubKeyAsm,
required String valueStringSats,
required List<String> addresses,
required bool walletOwns,
}) =>
OutputV2()
..scriptPubKeyHex = scriptPubKeyHex
..scriptPubKeyAsm = scriptPubKeyAsm
..valueStringSats = valueStringSats
..walletOwns = walletOwns
..addresses = List.unmodifiable(addresses);
OutputV2 copyWith({
String? scriptPubKeyHex,
String? scriptPubKeyAsm,
String? valueStringSats,
List<String>? addresses,
bool? walletOwns,
}) {
return OutputV2.isarCantDoRequiredInDefaultConstructor(
scriptPubKeyHex: scriptPubKeyHex ?? this.scriptPubKeyHex,
scriptPubKeyAsm: scriptPubKeyAsm ?? this.scriptPubKeyAsm,
valueStringSats: valueStringSats ?? this.valueStringSats,
addresses: addresses ?? this.addresses,
walletOwns: walletOwns ?? this.walletOwns,
@ -46,6 +51,7 @@ class OutputV2 {
Map<String, dynamic> json, {
required bool walletOwns,
required int decimalPlaces,
bool isFullAmountNotSats = false,
}) {
try {
List<String> addresses = [];
@ -60,9 +66,11 @@ class OutputV2 {
return OutputV2.isarCantDoRequiredInDefaultConstructor(
scriptPubKeyHex: json["scriptPubKey"]["hex"] as String,
scriptPubKeyAsm: json["scriptPubKey"]["asm"] as String?,
valueStringSats: parseOutputAmountString(
json["value"].toString(),
decimalPlaces: decimalPlaces,
isFullAmountNotSats: isFullAmountNotSats,
),
addresses: addresses,
walletOwns: walletOwns,
@ -75,6 +83,7 @@ class OutputV2 {
static String parseOutputAmountString(
String amount, {
required int decimalPlaces,
bool isFullAmountNotSats = false,
}) {
final temp = Decimal.parse(amount);
if (temp < Decimal.zero) {
@ -82,7 +91,9 @@ class OutputV2 {
}
final String valueStringSats;
if (temp.isInteger) {
if (isFullAmountNotSats) {
valueStringSats = temp.shift(decimalPlaces).toBigInt().toString();
} else if (temp.isInteger) {
valueStringSats = temp.toString();
} else {
valueStringSats = temp.shift(decimalPlaces).toBigInt().toString();
@ -95,27 +106,10 @@ class OutputV2 {
String toString() {
return 'OutputV2(\n'
' scriptPubKeyHex: $scriptPubKeyHex,\n'
' scriptPubKeyAsm: $scriptPubKeyAsm,\n'
' value: $value,\n'
' walletOwns: $walletOwns,\n'
' addresses: $addresses,\n'
')';
}
}
bool _listEquals<T, U>(List<T> a, List<U> b) {
if (T != U) {
return false;
}
if (a.length != b.length) {
return false;
}
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}

View file

@ -18,18 +18,23 @@ const OutputV2Schema = Schema(
name: r'addresses',
type: IsarType.stringList,
),
r'scriptPubKeyHex': PropertySchema(
r'scriptPubKeyAsm': PropertySchema(
id: 1,
name: r'scriptPubKeyAsm',
type: IsarType.string,
),
r'scriptPubKeyHex': PropertySchema(
id: 2,
name: r'scriptPubKeyHex',
type: IsarType.string,
),
r'valueStringSats': PropertySchema(
id: 2,
id: 3,
name: r'valueStringSats',
type: IsarType.string,
),
r'walletOwns': PropertySchema(
id: 3,
id: 4,
name: r'walletOwns',
type: IsarType.bool,
)
@ -53,6 +58,12 @@ int _outputV2EstimateSize(
bytesCount += value.length * 3;
}
}
{
final value = object.scriptPubKeyAsm;
if (value != null) {
bytesCount += 3 + value.length * 3;
}
}
bytesCount += 3 + object.scriptPubKeyHex.length * 3;
bytesCount += 3 + object.valueStringSats.length * 3;
return bytesCount;
@ -65,9 +76,10 @@ void _outputV2Serialize(
Map<Type, List<int>> allOffsets,
) {
writer.writeStringList(offsets[0], object.addresses);
writer.writeString(offsets[1], object.scriptPubKeyHex);
writer.writeString(offsets[2], object.valueStringSats);
writer.writeBool(offsets[3], object.walletOwns);
writer.writeString(offsets[1], object.scriptPubKeyAsm);
writer.writeString(offsets[2], object.scriptPubKeyHex);
writer.writeString(offsets[3], object.valueStringSats);
writer.writeBool(offsets[4], object.walletOwns);
}
OutputV2 _outputV2Deserialize(
@ -78,9 +90,10 @@ OutputV2 _outputV2Deserialize(
) {
final object = OutputV2();
object.addresses = reader.readStringList(offsets[0]) ?? [];
object.scriptPubKeyHex = reader.readString(offsets[1]);
object.valueStringSats = reader.readString(offsets[2]);
object.walletOwns = reader.readBool(offsets[3]);
object.scriptPubKeyAsm = reader.readStringOrNull(offsets[1]);
object.scriptPubKeyHex = reader.readString(offsets[2]);
object.valueStringSats = reader.readString(offsets[3]);
object.walletOwns = reader.readBool(offsets[4]);
return object;
}
@ -94,10 +107,12 @@ P _outputV2DeserializeProp<P>(
case 0:
return (reader.readStringList(offset) ?? []) as P;
case 1:
return (reader.readString(offset)) as P;
return (reader.readStringOrNull(offset)) as P;
case 2:
return (reader.readString(offset)) as P;
case 3:
return (reader.readString(offset)) as P;
case 4:
return (reader.readBool(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
@ -330,6 +345,160 @@ extension OutputV2QueryFilter
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'scriptPubKeyAsm',
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'scriptPubKeyAsm',
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmEqualTo(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'scriptPubKeyAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmGreaterThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'scriptPubKeyAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmLessThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'scriptPubKeyAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmBetween(
String? lower,
String? upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'scriptPubKeyAsm',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'scriptPubKeyAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'scriptPubKeyAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'scriptPubKeyAsm',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'scriptPubKeyAsm',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'scriptPubKeyAsm',
value: '',
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyAsmIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'scriptPubKeyAsm',
value: '',
));
});
}
QueryBuilder<OutputV2, OutputV2, QAfterFilterCondition>
scriptPubKeyHexEqualTo(
String value, {

View file

@ -1,3 +1,4 @@
import 'dart:convert';
import 'dart:math';
import 'package:isar/isar.dart';
@ -5,7 +6,8 @@ import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'
import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/extensions/extensions.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
part 'transaction_v2.g.dart';
@ -37,6 +39,8 @@ class TransactionV2 {
@enumerated
final TransactionSubType subType;
final String? otherData;
TransactionV2({
required this.walletId,
required this.blockHash,
@ -49,8 +53,21 @@ class TransactionV2 {
required this.version,
required this.type,
required this.subType,
required this.otherData,
});
bool get isEpiccashTransaction =>
_getFromOtherData(key: "isEpiccashTransaction") == true;
int? get numberOfMessages =>
_getFromOtherData(key: "numberOfMessages") as int?;
String? get slateId => _getFromOtherData(key: "slateId") as String?;
String? get onChainNote => _getFromOtherData(key: "onChainNote") as String?;
bool get isCancelled => _getFromOtherData(key: "isCancelled") == true;
String? get contractAddress =>
_getFromOtherData(key: "contractAddress") as String?;
int? get nonce => _getFromOtherData(key: "nonce") as int?;
int getConfirmations(int currentChainHeight) {
if (height == null || height! <= 0) return 0;
return max(0, currentChainHeight - (height! - 1));
@ -61,37 +78,63 @@ class TransactionV2 {
return confirmations >= minimumConfirms;
}
Amount getFee({required Coin coin}) {
Amount getFee({required int fractionDigits}) {
// check for override fee
final fee = _getOverrideFee();
if (fee != null) {
return fee;
}
final inSum =
inputs.map((e) => e.value).reduce((value, element) => value += element);
final outSum = outputs
.map((e) => e.value)
.reduce((value, element) => value += element);
return Amount(rawValue: inSum - outSum, fractionDigits: coin.decimals);
return Amount(rawValue: inSum - outSum, fractionDigits: fractionDigits);
}
Amount getAmountReceivedThisWallet({required Coin coin}) {
Amount getAmountReceivedInThisWallet({required int fractionDigits}) {
final outSum = outputs
.where((e) => e.walletOwns)
.fold(BigInt.zero, (p, e) => p + e.value);
return Amount(rawValue: outSum, fractionDigits: coin.decimals);
return Amount(rawValue: outSum, fractionDigits: fractionDigits);
}
Amount getAmountSentFromThisWallet({required Coin coin}) {
Amount getAmountSparkSelfMinted({required int fractionDigits}) {
final outSum = outputs.where((e) {
final op = e.scriptPubKeyHex.substring(0, 2).toUint8ListFromHex.first;
return e.walletOwns && (op == OP_SPARKMINT);
}).fold(BigInt.zero, (p, e) => p + e.value);
return Amount(rawValue: outSum, fractionDigits: fractionDigits);
}
Amount getAmountSentFromThisWallet({required int fractionDigits}) {
final inSum = inputs
.where((e) => e.walletOwns)
.fold(BigInt.zero, (p, e) => p + e.value);
return Amount(
Amount amount = Amount(
rawValue: inSum,
fractionDigits: coin.decimals,
fractionDigits: fractionDigits,
) -
getAmountReceivedThisWallet(
coin: coin,
) -
getFee(coin: coin);
getAmountReceivedInThisWallet(
fractionDigits: fractionDigits,
);
if (subType != TransactionSubType.ethToken) {
amount = amount - getFee(fractionDigits: fractionDigits);
}
// negative amounts are likely an error or can happen with coins such as eth
// that don't use the btc style inputs/outputs
if (amount.raw < BigInt.zero) {
return Amount.zeroWith(fractionDigits: fractionDigits);
}
return amount;
}
Set<String> associatedAddresses() => {
@ -99,6 +142,95 @@ class TransactionV2 {
...outputs.map((e) => e.addresses).expand((e) => e),
};
Amount? _getOverrideFee() {
try {
return Amount.fromSerializedJsonString(
_getFromOtherData(key: "overrideFee") as String,
);
} catch (_) {
return null;
}
}
String statusLabel({
required int currentChainHeight,
required int minConfirms,
}) {
if (subType == TransactionSubType.cashFusion ||
subType == TransactionSubType.mint ||
(subType == TransactionSubType.sparkMint &&
type == TransactionType.sentToSelf)) {
if (isConfirmed(currentChainHeight, minConfirms)) {
return "Anonymized";
} else {
return "Anonymizing";
}
}
if (isEpiccashTransaction) {
if (slateId == null) {
return "Restored Funds";
}
if (isCancelled) {
return "Cancelled";
} else if (type == TransactionType.incoming) {
if (isConfirmed(currentChainHeight, minConfirms)) {
return "Received";
} else {
if (numberOfMessages == 1) {
return "Receiving (waiting for sender)";
} else if ((numberOfMessages ?? 0) > 1) {
return "Receiving (waiting for confirmations)"; // TODO test if the sender still has to open again after the receiver has 2 messages present, ie. sender->receiver->sender->node (yes) vs. sender->receiver->node (no)
} else {
return "Receiving";
}
}
} else if (type == TransactionType.outgoing) {
if (isConfirmed(currentChainHeight, minConfirms)) {
return "Sent (confirmed)";
} else {
if (numberOfMessages == 1) {
return "Sending (waiting for receiver)";
} else if ((numberOfMessages ?? 0) > 1) {
return "Sending (waiting for confirmations)";
} else {
return "Sending";
}
}
}
}
if (type == TransactionType.incoming) {
// if (_transaction.isMinting) {
// return "Minting";
// } else
if (isConfirmed(currentChainHeight, minConfirms)) {
return "Received";
} else {
return "Receiving";
}
} else if (type == TransactionType.outgoing) {
if (isConfirmed(currentChainHeight, minConfirms)) {
return "Sent";
} else {
return "Sending";
}
} else if (type == TransactionType.sentToSelf) {
return "Sent to self";
} else {
return type.name;
}
}
dynamic _getFromOtherData({required dynamic key}) {
if (otherData == null) {
return null;
}
final map = jsonDecode(otherData!);
return map[key];
}
@override
String toString() {
return 'TransactionV2(\n'
@ -113,15 +245,7 @@ class TransactionV2 {
' version: $version,\n'
' inputs: $inputs,\n'
' outputs: $outputs,\n'
' otherData: $otherData,\n'
')';
}
}
enum TxDirection {
outgoing,
incoming;
}
enum TxType {
normal,
}

File diff suppressed because it is too large Load diff

View file

@ -25,8 +25,22 @@ class TransactionNote {
@Index()
late String walletId;
@Index(unique: true, composite: [CompositeIndex("walletId")])
@Index(
unique: true,
replace: true,
composite: [CompositeIndex("walletId")],
)
late String txid;
late String value;
TransactionNote copyWith({
String? value,
}) {
return TransactionNote(
walletId: walletId,
txid: txid,
value: value ?? this.value,
);
}
}

View file

@ -56,7 +56,7 @@ const TransactionNoteSchema = CollectionSchema(
id: -2771771174176035985,
name: r'txid_walletId',
unique: true,
replace: false,
replace: true,
properties: [
IndexPropertySchema(
name: r'txid',

View file

@ -11,8 +11,8 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/wallet_restore_state.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/enums/stack_restoring_status.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart';
class StackRestoringUIState extends ChangeNotifier {
bool _walletsWasSet = false;
@ -93,14 +93,14 @@ class StackRestoringUIState extends ChangeNotifier {
notifyListeners();
}
List<Manager> get managers {
List<Manager> _managers = [];
List<Wallet> get wallets {
List<Wallet> _wallets = [];
for (final item in _walletStates.values) {
if (item.manager != null) {
_managers.add(item.manager!);
if (item.wallet != null) {
_wallets.add(item.wallet!);
}
}
return _managers;
return _wallets;
}
Map<String, WalletRestoreState> _walletStates = {};
@ -132,15 +132,14 @@ class StackRestoringUIState extends ChangeNotifier {
void update({
required String walletId,
required StackRestoringStatus restoringStatus,
Manager? manager,
Wallet? wallet,
String? address,
String? mnemonic,
String? mnemonicPassphrase,
int? height,
}) {
_walletStates[walletId]!.restoringState = restoringStatus;
_walletStates[walletId]!.manager =
manager ?? _walletStates[walletId]!.manager;
_walletStates[walletId]!.wallet = wallet ?? _walletStates[walletId]!.wallet;
_walletStates[walletId]!.address =
address ?? _walletStates[walletId]!.address;
_walletStates[walletId]!.mnemonic =

View file

@ -10,6 +10,7 @@
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/wallets/models/tx_recipient.dart';
// TODO use something like this instead of Map<String, dynamic> transactionObject
@ -43,13 +44,3 @@ class TxInfo {
recipients: recipients ?? this.recipients,
);
}
class TxRecipient {
final String address;
final Amount amount;
TxRecipient({
required this.address,
required this.amount,
});
}

View file

@ -8,17 +8,17 @@
*
*/
import 'package:flutter/cupertino.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:flutter/material.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/stack_restoring_status.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart';
class WalletRestoreState extends ChangeNotifier {
final String walletId;
final String walletName;
final Coin coin;
late StackRestoringStatus _restoringStatus;
Manager? manager;
Wallet? wallet;
String? address;
String? mnemonic;
String? mnemonicPassphrase;
@ -35,7 +35,7 @@ class WalletRestoreState extends ChangeNotifier {
required this.walletName,
required this.coin,
required StackRestoringStatus restoringStatus,
this.manager,
this.wallet,
this.address,
this.mnemonic,
this.mnemonicPassphrase,
@ -54,7 +54,7 @@ class WalletRestoreState extends ChangeNotifier {
walletName: walletName,
coin: coin,
restoringStatus: restoringStatus ?? _restoringStatus,
manager: manager,
wallet: wallet,
address: this.address,
mnemonic: mnemonic,
mnemonicPassphrase: mnemonicPassphrase,

View file

@ -24,13 +24,14 @@ import 'package:stackwallet/pages/add_wallet_views/add_token_view/sub_widgets/ad
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/default_eth_tokens.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -98,10 +99,8 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
.map((e) => e.token.address)
.toList();
final ethWallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as EthereumWallet;
final ethWallet =
ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet;
await ethWallet.updateTokenContracts(selectedTokens);
if (mounted) {
@ -122,7 +121,7 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "${ethWallet.walletName} tokens saved",
message: "${ethWallet.info.name} tokens saved",
context: context,
),
);
@ -181,11 +180,7 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
tokenEntities.addAll(contracts.map((e) => AddTokenListElementData(e)));
final walletContracts = (ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as EthereumWallet)
.getWalletTokenContractAddresses();
final walletContracts = ref.read(pWalletTokenAddresses(widget.walletId));
final shouldMarkAsSelectedContracts = [
...walletContracts,
@ -209,8 +204,7 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final walletName = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).walletName));
final walletName = ref.watch(pWalletName(widget.walletId));
if (isDesktop) {
return ConditionalParent(

View file

@ -126,7 +126,7 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
void initState() {
_searchFieldController = TextEditingController();
_searchFocusNode = FocusNode();
_coinsTestnet.remove(Coin.firoTestNet);
// _coinsTestnet.remove(Coin.firoTestNet);
if (Platform.isWindows) {
_coins.remove(Coin.monero);
_coins.remove(Coin.wownero);

View file

@ -12,22 +12,22 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/coin_image.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart';
import 'package:stackwallet/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/global/wallets_service_provider.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/ui/verify_recovery_phrase/mnemonic_word_count_state_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/add_wallet_type_enum.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/name_generator.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/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
@ -81,10 +81,15 @@ class _NameYourWalletViewState extends ConsumerState<NameYourWalletView> {
void initState() {
isDesktop = Util.isDesktop;
ref.read(walletsServiceChangeNotifierProvider).walletNames.then(
(value) => namesToExclude.addAll(
value.values.map((e) => e.name),
),
ref
.read(mainDBProvider)
.isar
.walletInfo
.where()
.nameProperty()
.findAll()
.then(
(values) => namesToExclude.addAll(values),
);
generator = NameGenerator();
addWalletType = widget.addWalletType;
@ -333,22 +338,9 @@ class _NameYourWalletViewState extends ConsumerState<NameYourWalletView> {
child: TextButton(
onPressed: _nextEnabled
? () async {
final walletsService =
ref.read(walletsServiceChangeNotifierProvider);
final name = textEditingController.text;
final hasDuplicateName =
await walletsService.checkForDuplicate(name);
if (mounted) {
if (hasDuplicateName) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Wallet name already in use.",
iconAsset: Assets.svg.circleAlert,
context: context,
));
} else {
// hide keyboard if has focus
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
@ -357,9 +349,7 @@ class _NameYourWalletViewState extends ConsumerState<NameYourWalletView> {
}
if (mounted) {
ref
.read(mnemonicWordCountStateProvider.state)
.state =
ref.read(mnemonicWordCountStateProvider.state).state =
Constants.possibleLengthsForCoin(coin).last;
ref.read(pNewWalletOptions.notifier).state = null;
@ -394,7 +384,6 @@ class _NameYourWalletViewState extends ConsumerState<NameYourWalletView> {
}
}
}
}
: null,
style: _nextEnabled
? Theme.of(context)

View file

@ -21,14 +21,16 @@ import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_wa
import 'package:stackwallet/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.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';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
@ -37,14 +39,14 @@ import 'package:tuple/tuple.dart';
class NewWalletRecoveryPhraseView extends ConsumerStatefulWidget {
const NewWalletRecoveryPhraseView({
Key? key,
required this.manager,
required this.wallet,
required this.mnemonic,
this.clipboardInterface = const ClipboardWrapper(),
}) : super(key: key);
static const routeName = "/newWalletRecoveryPhrase";
final Manager manager;
final Wallet wallet;
final List<String> mnemonic;
final ClipboardInterface clipboardInterface;
@ -58,14 +60,14 @@ class _NewWalletRecoveryPhraseViewState
extends ConsumerState<NewWalletRecoveryPhraseView>
// with WidgetsBindingObserver
{
late Manager _manager;
late Wallet _wallet;
late List<String> _mnemonic;
late ClipboardInterface _clipboardInterface;
late final bool isDesktop;
@override
void initState() {
_manager = widget.manager;
_wallet = widget.wallet;
_mnemonic = widget.mnemonic;
_clipboardInterface = widget.clipboardInterface;
isDesktop = Util.isDesktop;
@ -78,14 +80,14 @@ class _NewWalletRecoveryPhraseViewState
}
Future<void> delete() async {
await _wallet.exit();
await ref
.read(walletsServiceChangeNotifierProvider)
.deleteWallet(_manager.walletName, false);
await _manager.exitCurrentWallet();
.read(pWallets)
.deleteWallet(_wallet.info, ref.read(secureStoreProvider));
}
Future<void> _copy() async {
final words = await _manager.mnemonic;
final words = _mnemonic;
await _clipboardInterface.setData(ClipboardData(text: words.join(" ")));
unawaited(showFloatingFlushBar(
type: FlushBarType.info,
@ -191,7 +193,7 @@ class _NewWalletRecoveryPhraseViewState
),
if (!isDesktop)
Text(
_manager.walletName,
ref.watch(pWalletName(_wallet.walletId)),
textAlign: TextAlign.center,
style: STextStyles.label(context).copyWith(
fontSize: 12,
@ -305,7 +307,7 @@ class _NewWalletRecoveryPhraseViewState
unawaited(Navigator.of(context).pushNamed(
VerifyRecoveryPhraseView.routeName,
arguments: Tuple2(_manager, _mnemonic),
arguments: Tuple2(_wallet, _mnemonic),
));
},
style: Theme.of(context)

View file

@ -9,7 +9,9 @@
*/
import 'dart:async';
import 'dart:convert';
import 'package:bip39/bip39.dart' as bip39;
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -17,10 +19,9 @@ import 'package:stackwallet/pages/add_wallet_views/new_wallet_options/new_wallet
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/recovery_phrase_explanation_dialog.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -30,6 +31,10 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/crypto_currency/coins/tezos.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
@ -453,14 +458,17 @@ class _NewWalletRecoveryPhraseWarningViewState
},
));
final walletsService = ref.read(
walletsServiceChangeNotifierProvider);
final walletId =
await walletsService.addNewWallet(
name: walletName,
coin: coin,
shouldNotifyListeners: false,
final info = WalletInfo.createNew(
coin: widget.coin,
name: widget.walletName,
otherDataJsonString: coin == Coin.tezos
? jsonEncode({
WalletInfoKeys
.tezosDerivationPath:
Tezos.standardDerivationPath
.value,
})
: null,
);
var node = ref
@ -480,44 +488,73 @@ class _NewWalletRecoveryPhraseWarningViewState
final txTracker =
TransactionNotificationTracker(
walletId: walletId!);
final failovers = ref
.read(nodeServiceChangeNotifierProvider)
.failoverNodesFor(coin: widget.coin);
final wallet = CoinServiceAPI.from(
coin,
walletId,
walletName,
ref.read(secureStoreProvider),
node,
txTracker,
ref.read(prefsChangeNotifierProvider),
failovers,
walletId: info.walletId,
);
final manager = Manager(wallet);
int? wordCount;
String? mnemonicPassphrase;
String? mnemonic;
String? privateKey;
if (coin.hasMnemonicPassphraseSupport &&
ref
wordCount =
Constants.defaultSeedPhraseLengthFor(
coin: info.coin,
);
if (coin == Coin.monero ||
coin == Coin.wownero) {
// currently a special case due to the
// xmr/wow libraries handling their
// own mnemonic generation
} else if (wordCount > 0) {
if (ref
.read(pNewWalletOptions.state)
.state !=
null) {
await manager.initializeNew((
mnemonicPassphrase: ref
if (coin.hasMnemonicPassphraseSupport) {
mnemonicPassphrase = ref
.read(pNewWalletOptions.state)
.state!
.mnemonicPassphrase,
wordCount: ref
.mnemonicPassphrase;
} else {}
wordCount = ref
.read(pNewWalletOptions.state)
.state!
.mnemonicWordsCount,
));
.mnemonicWordsCount;
} else {
await manager.initializeNew(null);
mnemonicPassphrase = "";
}
if (wordCount < 12 ||
24 < wordCount ||
wordCount % 3 != 0) {
throw Exception("Invalid word count");
}
final strength = (wordCount ~/ 3) * 32;
mnemonic = bip39.generateMnemonic(
strength: strength,
);
}
final wallet = await Wallet.create(
walletInfo: info,
mainDB: ref.read(mainDBProvider),
secureStorageInterface:
ref.read(secureStoreProvider),
nodeService: ref.read(
nodeServiceChangeNotifierProvider),
prefs:
ref.read(prefsChangeNotifierProvider),
mnemonicPassphrase: mnemonicPassphrase,
mnemonic: mnemonic,
privateKey: privateKey,
);
await wallet.init();
// pop progress dialog
if (mounted) {
Navigator.pop(context);
@ -531,8 +568,9 @@ class _NewWalletRecoveryPhraseWarningViewState
unawaited(Navigator.of(context).pushNamed(
NewWalletRecoveryPhraseView.routeName,
arguments: Tuple2(
manager,
await manager.mnemonic,
wallet,
await (wallet as MnemonicInterface)
.getMnemonicAsWords(),
),
));
}

View file

@ -10,6 +10,7 @@
import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
@ -32,10 +33,9 @@ import 'package:stackwallet/pages/add_wallet_views/verify_recovery_phrase_view/v
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/address_utils.dart';
@ -50,6 +50,10 @@ import 'package:stackwallet/utilities/enums/form_input_status_enum.dart';
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';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
@ -201,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);
@ -227,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
@ -239,13 +260,14 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
));
} else {
if (!Platform.isLinux) await Wakelock.enable();
final walletsService = ref.read(walletsServiceChangeNotifierProvider);
final walletId = await walletsService.addNewWallet(
name: widget.walletName,
final info = WalletInfo.createNew(
coin: widget.coin,
shouldNotifyListeners: false,
name: widget.walletName,
restoreHeight: height,
otherDataJsonString: otherDataJsonString,
);
bool isRestoring = true;
// show restoring in progress
unawaited(showDialog<dynamic>(
@ -256,13 +278,10 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
return RestoringDialog(
onCancel: () async {
isRestoring = false;
ref
.read(walletsChangeNotifierProvider.notifier)
.removeWallet(walletId: walletId!);
await walletsService.deleteWallet(
widget.walletName,
false,
await ref.read(pWallets).deleteWallet(
info,
ref.read(secureStoreProvider),
);
},
);
@ -281,49 +300,35 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
);
}
final txTracker = TransactionNotificationTracker(walletId: walletId!);
final failovers = ref
.read(nodeServiceChangeNotifierProvider)
.failoverNodesFor(coin: widget.coin);
final wallet = CoinServiceAPI.from(
widget.coin,
walletId,
widget.walletName,
ref.read(secureStoreProvider),
node,
txTracker,
ref.read(prefsChangeNotifierProvider),
failovers,
);
final manager = Manager(wallet);
final txTracker =
TransactionNotificationTracker(walletId: info.walletId);
try {
// TODO GUI option to set maxUnusedAddressGap?
// default is 20 but it may miss some transactions if
// the previous wallet software generated many addresses
// without using them
await manager.recoverFromMnemonic(
mnemonic: mnemonic,
final wallet = await Wallet.create(
walletInfo: info,
mainDB: ref.read(mainDBProvider),
secureStorageInterface: ref.read(secureStoreProvider),
nodeService: ref.read(nodeServiceChangeNotifierProvider),
prefs: ref.read(prefsChangeNotifierProvider),
mnemonicPassphrase: widget.mnemonicPassphrase,
maxUnusedAddressGap: widget.coin == Coin.firo ? 50 : 20,
maxNumberOfIndexesToCheck: 1000,
height: height,
mnemonic: mnemonic,
);
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
if (mounted) {
await ref
.read(walletsServiceChangeNotifierProvider)
.setMnemonicVerified(
walletId: manager.walletId,
await wallet.info.setMnemonicVerified(
isar: ref.read(mainDBProvider).isar,
);
ref
.read(walletsChangeNotifierProvider.notifier)
.addWallet(walletId: manager.walletId, manager: manager);
ref.read(pWallets).addWallet(wallet);
final isCreateSpecialEthWallet =
ref.read(createSpecialEthWalletRoutingFlag);
@ -360,11 +365,11 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
(route) => false,
),
);
if (manager.coin == Coin.ethereum) {
if (info.coin == Coin.ethereum) {
unawaited(
Navigator.of(context).pushNamed(
EditWalletTokensView.routeName,
arguments: manager.walletId,
arguments: wallet.walletId,
),
);
}
@ -410,8 +415,8 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
builder: (context) {
return RestoreFailedDialog(
errorMessage: e.toString(),
walletId: wallet.walletId,
walletName: wallet.walletName,
walletId: info.walletId,
walletName: info.name,
);
},
);

View file

@ -10,9 +10,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class RestoreFailedDialog extends ConsumerStatefulWidget {
@ -63,14 +65,11 @@ class _RestoreFailedDialogState extends ConsumerState<RestoreFailedDialog> {
style: STextStyles.itemSubtitle12(context),
),
onPressed: () async {
ref
.read(walletsChangeNotifierProvider.notifier)
.removeWallet(walletId: walletId);
await ref.read(walletsServiceChangeNotifierProvider).deleteWallet(
walletName,
false,
await ref.read(pWallets).deleteWallet(
ref.read(pWalletInfo(walletId)),
ref.read(secureStoreProvider),
);
if (mounted) {
Navigator.of(context).pop();
}

View file

@ -10,17 +10,16 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/coin_entity.dart';
import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart';
import 'package:stackwallet/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart';
import 'package:stackwallet/providers/global/wallets_service_provider.dart';
import 'package:stackwallet/services/wallets_service.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/all_wallets_info_provider.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -54,8 +53,6 @@ class SelectWalletForTokenView extends ConsumerStatefulWidget {
class _SelectWalletForTokenViewState
extends ConsumerState<SelectWalletForTokenView> {
final isDesktop = Util.isDesktop;
late final List<String> ethWalletIds;
bool _hasEthWallets = false;
String? _selectedWalletId;
@ -77,49 +74,23 @@ class _SelectWalletForTokenViewState
);
}
late int _cachedWalletCount;
@override
Widget build(BuildContext context) {
final ethWalletInfos = ref
.watch(pAllWalletsInfo)
.where((e) => e.coin == widget.entity.coin)
.toList();
void _updateWalletsList(Map<String, WalletInfo> walletsData) {
_cachedWalletCount = walletsData.length;
final _hasEthWallets = ethWalletInfos.isNotEmpty;
walletsData.removeWhere((key, value) => value.coin != widget.entity.coin);
ethWalletIds.clear();
final List<String> ethWalletIds = [];
_hasEthWallets = walletsData.isNotEmpty;
// TODO: proper wallet data class instead of this Hive silliness
for (final walletId in walletsData.values.map((e) => e.walletId).toList()) {
final walletContracts = DB.instance.get<dynamic>(
boxName: walletId,
key: DBKeys.ethTokenContracts,
) as List<String>? ??
[];
for (final walletId in ethWalletInfos.map((e) => e.walletId).toList()) {
final walletContracts = ref.read(pWalletTokenAddresses(walletId));
if (!walletContracts.contains(widget.entity.token.address)) {
ethWalletIds.add(walletId);
}
}
}
@override
void initState() {
ethWalletIds = [];
final walletsData =
ref.read(walletsServiceChangeNotifierProvider).fetchWalletsData();
_updateWalletsList(walletsData);
super.initState();
}
@override
Widget build(BuildContext context) {
// dumb hack
ref.watch(newEthWalletTriggerTempUntilHiveCompletelyDeleted);
final walletsData =
ref.read(walletsServiceChangeNotifierProvider).fetchWalletsData();
if (walletsData.length != _cachedWalletCount) {
_updateWalletsList(walletsData);
}
return WillPopScope(
onWillPop: () async {

View file

@ -24,14 +24,17 @@ import 'package:stackwallet/pages/add_wallet_views/verify_recovery_phrase_view/v
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.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';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
@ -42,13 +45,13 @@ final createSpecialEthWalletRoutingFlag = StateProvider((ref) => false);
class VerifyRecoveryPhraseView extends ConsumerStatefulWidget {
const VerifyRecoveryPhraseView({
Key? key,
required this.manager,
required this.wallet,
required this.mnemonic,
}) : super(key: key);
static const routeName = "/verifyRecoveryPhrase";
final Manager manager;
final Wallet wallet;
final List<String> mnemonic;
@override
@ -60,13 +63,13 @@ class _VerifyRecoveryPhraseViewState
extends ConsumerState<VerifyRecoveryPhraseView>
// with WidgetsBindingObserver
{
late Manager _manager;
late Wallet _wallet;
late List<String> _mnemonic;
late final bool isDesktop;
@override
void initState() {
_manager = widget.manager;
_wallet = widget.wallet;
_mnemonic = widget.mnemonic;
isDesktop = Util.isDesktop;
// WidgetsBinding.instance?.addObserver(this);
@ -119,13 +122,11 @@ class _VerifyRecoveryPhraseViewState
}
}
await ref.read(walletsServiceChangeNotifierProvider).setMnemonicVerified(
walletId: _manager.walletId,
await ref.read(pWalletInfo(_wallet.walletId)).setMnemonicVerified(
isar: ref.read(mainDBProvider).isar,
);
ref
.read(walletsChangeNotifierProvider.notifier)
.addWallet(walletId: _manager.walletId, manager: _manager);
ref.read(pWallets).addWallet(_wallet);
final isCreateSpecialEthWallet =
ref.read(createSpecialEthWalletRoutingFlag);
@ -153,11 +154,11 @@ class _VerifyRecoveryPhraseViewState
DesktopHomeView.routeName,
),
);
if (widget.manager.coin == Coin.ethereum) {
if (widget.wallet.info.coin == Coin.ethereum) {
unawaited(
Navigator.of(context).pushNamed(
EditWalletTokensView.routeName,
arguments: widget.manager.walletId,
arguments: widget.wallet.walletId,
),
);
}
@ -176,11 +177,11 @@ class _VerifyRecoveryPhraseViewState
(route) => false,
),
);
if (widget.manager.coin == Coin.ethereum) {
if (widget.wallet.info.coin == Coin.ethereum) {
unawaited(
Navigator.of(context).pushNamed(
EditWalletTokensView.routeName,
arguments: widget.manager.walletId,
arguments: widget.wallet.walletId,
),
);
}
@ -260,10 +261,10 @@ class _VerifyRecoveryPhraseViewState
}
Future<void> delete() async {
await ref
.read(walletsServiceChangeNotifierProvider)
.deleteWallet(_manager.walletName, false);
await _manager.exitCurrentWallet();
await ref.read(pWallets).deleteWallet(
_wallet.info,
ref.read(secureStoreProvider),
);
}
@override

View file

@ -79,14 +79,14 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
WidgetsBinding.instance.addPostFrameCallback((_) async {
List<ContactAddressEntry> addresses = [];
final managers = ref.read(walletsChangeNotifierProvider).managers;
for (final manager in managers) {
final wallets = ref.read(pWallets).wallets;
for (final wallet in wallets) {
addresses.add(
ContactAddressEntry()
..coinName = manager.coin.name
..address = await manager.currentReceivingAddress
..coinName = wallet.info.coin.name
..address = (await wallet.getCurrentReceivingAddress())!.value
..label = "Current Receiving"
..other = manager.walletName,
..other = wallet.info.name,
);
}
final self = ContactEntry(

View file

@ -15,16 +15,14 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/address_book_views/subviews/add_new_contact_address_view.dart';
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart';
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -63,29 +61,33 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
List<Tuple2<String, Transaction>> _cachedTransactions = [];
Future<List<Tuple2<String, Transaction>>> _filteredTransactionsByContact(
List<Manager> managers,
) async {
Future<List<Tuple2<String, Transaction>>>
_filteredTransactionsByContact() async {
final contact =
ref.read(addressBookServiceProvider).getContactById(_contactId);
// TODO: optimise
List<Tuple2<String, Transaction>> result = [];
for (final manager in managers) {
final transactions = await MainDB.instance
.getTransactions(manager.walletId)
final transactions = await ref
.read(mainDBProvider)
.isar
.transactions
.where()
.filter()
.anyOf(contact.addresses.map((e) => e.address),
(q, String e) => q.address((q) => q.valueEqualTo(e)))
.sortByTimestampDesc()
.findAll();
List<Tuple2<String, Transaction>> result = [];
for (final tx in transactions) {
result.add(Tuple2(manager.walletId, tx));
}
result.add(Tuple2(tx.walletId, tx));
}
// sort by date
result.sort((a, b) => b.item2.timestamp - a.item2.timestamp);
return result;
}
@ -461,8 +463,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
height: 12,
),
FutureBuilder(
future: _filteredTransactionsByContact(
ref.watch(walletsChangeNotifierProvider).managers),
future: _filteredTransactionsByContact(),
builder: (_,
AsyncSnapshot<List<Tuple2<String, Transaction>>>
snapshot) {

View file

@ -19,6 +19,7 @@ import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/address_book_views/subviews/contact_details_view.dart';
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_2_view.dart';
import 'package:stackwallet/pages/send_view/send_view.dart';
import 'package:stackwallet/providers/global/active_wallet_provider.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
@ -28,6 +29,7 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:tuple/tuple.dart';
@ -51,21 +53,15 @@ class ContactPopUp extends ConsumerWidget {
final contact = ref.watch(addressBookServiceProvider
.select((value) => value.getContactById(contactId)));
final active = ref
.read(walletsChangeNotifierProvider)
.managers
.where((e) => e.isActiveWallet)
.toList(growable: false);
final active = ref.read(currentWalletIdProvider);
assert(active.isEmpty || active.length == 1);
bool hasActiveWallet = active.length == 1;
bool hasActiveWallet = active != null;
bool isExchangeFlow =
ref.watch(exchangeFlowIsActiveStateProvider.state).state;
final addresses = contact.addressesSorted.where((e) {
if (hasActiveWallet && !isExchangeFlow) {
return e.coin == active[0].coin;
return e.coin == ref.watch(pWalletCoin(active));
} else {
return true;
}
@ -201,7 +197,7 @@ class ContactPopUp extends ConsumerWidget {
child: RoundedWhiteContainer(
child: Center(
child: Text(
"No ${active[0].coin.prettyName} addresses found",
"No ${ref.watch(pWalletCoin(active!)).prettyName} addresses found",
style:
STextStyles.itemSubtitle(context),
),
@ -372,8 +368,9 @@ class ContactPopUp extends ConsumerWidget {
.pushNamed(
SendView.routeName,
arguments: Tuple3(
active[0].walletId,
active[0].coin,
active,
ref.read(
pWalletCoin(active)),
SendViewAutoFillData(
address: address,
contactLabel:

View file

@ -1160,15 +1160,14 @@ class _BuyFormState extends ConsumerState<BuyForm> {
)
.then((value) async {
if (value is String) {
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(value);
final wallet = ref.read(pWallets).getWallet(value);
// _toController.text = manager.walletName;
// model.recipientAddress =
// await manager.currentReceivingAddress;
_receiveAddressController.text =
await manager.currentReceivingAddress;
(await wallet.getCurrentReceivingAddress())!
.value;
setState(() {
_addressToggleFlag =

View file

@ -20,11 +20,13 @@ import 'package:stackwallet/pages/cashfusion/fusion_rounds_selection_sheet.dart'
import 'package:stackwallet/providers/cash_fusion/fusion_progress_ui_state_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/mixins/fusion_wallet_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
@ -54,6 +56,7 @@ class _CashFusionViewState extends ConsumerState<CashFusionView> {
late final FocusNode portFocusNode;
late final TextEditingController fusionRoundController;
late final FocusNode fusionRoundFocusNode;
late final Coin coin;
bool _enableSSLCheckbox = false;
bool _enableStartButton = false;
@ -61,11 +64,8 @@ class _CashFusionViewState extends ConsumerState<CashFusionView> {
FusionOption _option = FusionOption.continuous;
Future<void> _startFusion() async {
final wallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet;
final fusionWallet = wallet as FusionWalletInterface;
final fusionWallet =
ref.read(pWallets).getWallet(widget.walletId) as CashFusionInterface;
try {
fusionWallet.uiState = ref.read(
@ -90,9 +90,7 @@ class _CashFusionViewState extends ConsumerState<CashFusionView> {
);
// update user prefs (persistent)
ref
.read(prefsChangeNotifierProvider)
.setFusionServerInfo(wallet.coin, newInfo);
ref.read(prefsChangeNotifierProvider).setFusionServerInfo(coin, newInfo);
unawaited(
fusionWallet.fuse(
@ -116,11 +114,11 @@ class _CashFusionViewState extends ConsumerState<CashFusionView> {
portFocusNode = FocusNode();
fusionRoundFocusNode = FocusNode();
final info = ref.read(prefsChangeNotifierProvider).getFusionServerInfo(ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet
.coin);
coin = ref.read(pWalletCoin(widget.walletId));
final info =
ref.read(prefsChangeNotifierProvider).getFusionServerInfo(coin);
serverController.text = info.host;
portController.text = info.port.toString();
_enableSSLCheckbox = info.ssl;
@ -221,11 +219,7 @@ class _CashFusionViewState extends ConsumerState<CashFusionView> {
CustomTextButton(
text: "Default",
onTap: () {
final def = kFusionServerInfoDefaults[ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet
.coin]!;
final def = kFusionServerInfoDefaults[coin]!;
serverController.text = def.host;
portController.text = def.port.toString();
fusionRoundController.text =

View file

@ -16,12 +16,13 @@ import 'package:stackwallet/pages_desktop_specific/cashfusion/sub_widgets/fusion
import 'package:stackwallet/providers/cash_fusion/fusion_progress_ui_state_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/mixins/fusion_wallet_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
@ -38,7 +39,6 @@ class FusionProgressView extends ConsumerStatefulWidget {
static const routeName = "/cashFusionProgressView";
final String walletId;
@override
ConsumerState<FusionProgressView> createState() => _FusionProgressViewState();
}
@ -70,10 +70,8 @@ class _FusionProgressViewState extends ConsumerState<FusionProgressView> {
);
if (shouldCancel == true && mounted) {
final fusionWallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as FusionWalletInterface;
final fusionWallet =
ref.read(pWallets).getWallet(widget.walletId) as CashFusionInterface;
await showLoading(
whileFuture: Future.wait([
@ -93,11 +91,7 @@ class _FusionProgressViewState extends ConsumerState<FusionProgressView> {
@override
void initState() {
coin = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet
.coin;
coin = ref.read(pWalletCoin(widget.walletId));
super.initState();
}
@ -238,10 +232,8 @@ class _FusionProgressViewState extends ConsumerState<FusionProgressView> {
/// Fuse again.
void _fuseAgain() async {
final fusionWallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as FusionWalletInterface;
final fusionWallet =
ref.read(pWallets).getWallet(widget.walletId) as CashFusionInterface;
final fusionInfo =
ref.read(prefsChangeNotifierProvider).getFusionServerInfo(coin);

View file

@ -19,7 +19,6 @@ import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/coin_control/utxo_card.dart';
import 'package:stackwallet/pages/coin_control/utxo_details_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/mixins/coin_control_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
@ -27,6 +26,8 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart';
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
import 'package:stackwallet/widgets/app_bar_field.dart';
import 'package:stackwallet/widgets/background.dart';
@ -82,11 +83,9 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
final Set<UTXO> _selectedBlocked = {};
Future<void> _refreshBalance() async {
final coinControlInterface = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as CoinControlInterface;
await coinControlInterface.refreshBalance(notify: true);
final coinControlInterface =
ref.read(pWallets).getWallet(widget.walletId) as CoinControlInterface;
await coinControlInterface.updateBalance();
}
@override
@ -113,25 +112,14 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final coin = ref.watch(
walletsChangeNotifierProvider.select(
(value) => value
.getManager(
widget.walletId,
)
.coin,
),
);
final minConfirms = ref
.watch(pWallets)
.getWallet(widget.walletId)
.cryptoCurrency
.minConfirms;
final currentChainHeight = ref.watch(
walletsChangeNotifierProvider.select(
(value) => value
.getManager(
widget.walletId,
)
.currentHeight,
),
);
final coin = ref.watch(pWalletCoin(widget.walletId));
final currentHeight = ref.watch(pWalletChainHeight(widget.walletId));
if (_sort == CCSortDescriptor.address && !_isSearching) {
_list = null;
@ -357,8 +345,8 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
(widget.type == CoinControlViewType.use &&
!utxo.isBlocked &&
utxo.isConfirmed(
currentChainHeight,
coin.requiredConfirmations,
currentHeight,
minConfirms,
)),
initialSelectedState: isSelected,
onSelectedChanged: (value) {
@ -420,8 +408,8 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
CoinControlViewType.use &&
!_showBlocked &&
utxo.isConfirmed(
currentChainHeight,
coin.requiredConfirmations,
currentHeight,
minConfirms,
)),
initialSelectedState: isSelected,
onSelectedChanged: (value) {
@ -562,8 +550,8 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
.use &&
!utxo.isBlocked &&
utxo.isConfirmed(
currentChainHeight,
coin.requiredConfirmations,
currentHeight,
minConfirms,
)),
initialSelectedState: isSelected,
onSelectedChanged: (value) {

View file

@ -19,6 +19,7 @@ import 'package:stackwallet/utilities/amount/amount_formatter.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/icon_widgets/utxo_status_icon.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
@ -64,11 +65,8 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).coin));
final currentChainHeight = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).currentHeight));
final coin = ref.watch(pWalletCoin(widget.walletId));
final currentHeight = ref.watch(pWalletChainHeight(widget.walletId));
return ConditionalParent(
condition: widget.onPressed != null,
@ -113,8 +111,12 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
child: UTXOStatusIcon(
blocked: utxo.isBlocked,
status: utxo.isConfirmed(
currentChainHeight,
coin.requiredConfirmations,
currentHeight,
ref
.watch(pWallets)
.getWallet(widget.walletId)
.cryptoCurrency
.minConfirms,
)
? UTXOStatusIconStatus.confirmed
: UTXOStatusIconStatus.unconfirmed,

View file

@ -23,6 +23,7 @@ import 'package:stackwallet/utilities/amount/amount_formatter.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -91,21 +92,12 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> {
@override
Widget build(BuildContext context) {
final coin = ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(widget.walletId).coin,
),
);
final currentHeight = ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(widget.walletId).currentHeight,
),
);
final coin = ref.watch(pWalletCoin(widget.walletId));
final currentHeight = ref.watch(pWalletChainHeight(widget.walletId));
final confirmed = utxo!.isConfirmed(
currentHeight,
coin.requiredConfirmations,
ref.watch(pWallets).getWallet(widget.walletId).cryptoCurrency.minConfirms,
);
return ConditionalParent(

View file

@ -15,6 +15,7 @@ import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -47,8 +48,12 @@ class _ChooseFromStackViewState extends ConsumerState<ChooseFromStackView> {
@override
Widget build(BuildContext context) {
final walletIds = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getWalletIdsFor(coin: coin)));
final walletIds = ref
.watch(pWallets)
.wallets
.where((e) => e.info.coin == coin)
.map((e) => e.walletId)
.toList();
return Background(
child: Scaffold(
@ -78,8 +83,7 @@ class _ChooseFromStackViewState extends ConsumerState<ChooseFromStackView> {
: ListView.builder(
itemCount: walletIds.length,
itemBuilder: (context, index) {
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletIds[index])));
final walletId = walletIds[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
@ -98,7 +102,7 @@ class _ChooseFromStackViewState extends ConsumerState<ChooseFromStackView> {
elevation: 0,
onPressed: () async {
if (mounted) {
Navigator.of(context).pop(manager.walletId);
Navigator.of(context).pop(walletId);
}
},
child: RoundedWhiteContainer(
@ -115,7 +119,7 @@ class _ChooseFromStackViewState extends ConsumerState<ChooseFromStackView> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
manager.walletName,
ref.watch(pWalletName(walletId)),
style: STextStyles.titleBold12(context),
overflow: TextOverflow.ellipsis,
),

View file

@ -13,14 +13,15 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/trade_wallet_lookup.dart';
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
@ -29,6 +30,9 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -44,7 +48,7 @@ import 'package:uuid/uuid.dart';
class ConfirmChangeNowSendView extends ConsumerStatefulWidget {
const ConfirmChangeNowSendView({
Key? key,
required this.transactionInfo,
required this.txData,
required this.walletId,
this.routeOnSuccessName = WalletView.routeName,
required this.trade,
@ -54,7 +58,7 @@ class ConfirmChangeNowSendView extends ConsumerStatefulWidget {
static const String routeName = "/confirmChangeNowSend";
final Map<String, dynamic> transactionInfo;
final TxData txData;
final String walletId;
final String routeOnSuccessName;
final Trade trade;
@ -68,7 +72,6 @@ class ConfirmChangeNowSendView extends ConsumerStatefulWidget {
class _ConfirmChangeNowSendViewState
extends ConsumerState<ConfirmChangeNowSendView> {
late final Map<String, dynamic> transactionInfo;
late final String walletId;
late final String routeOnSuccessName;
late final Trade trade;
@ -76,8 +79,8 @@ class _ConfirmChangeNowSendViewState
final isDesktop = Util.isDesktop;
Future<void> _attemptSend(BuildContext context) async {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
final wallet = ref.read(pWallets).getWallet(walletId);
final coin = wallet.info.coin;
final sendProgressController = ProgressAndSuccessController();
@ -88,7 +91,7 @@ class _ConfirmChangeNowSendViewState
barrierDismissible: false,
builder: (context) {
return SendingTransactionDialog(
coin: manager.coin,
coin: coin,
controller: sendProgressController,
);
},
@ -102,19 +105,18 @@ class _ConfirmChangeNowSendViewState
);
late String txid;
Future<String> txidFuture;
Future<TxData> txidFuture;
final String note = transactionInfo["note"] as String? ?? "";
final String note = widget.txData.note ?? "";
try {
if (widget.shouldSendPublicFiroFunds == true) {
txidFuture = (manager.wallet as FiroWallet)
.confirmSendPublic(txData: transactionInfo);
if (wallet is FiroWallet && widget.shouldSendPublicFiroFunds == false) {
txidFuture = wallet.confirmSendLelantus(txData: widget.txData);
} else {
txidFuture = manager.confirmSend(txData: transactionInfo);
txidFuture = wallet.confirmSend(txData: widget.txData);
}
unawaited(manager.refresh());
unawaited(wallet.refresh());
final results = await Future.wait([
txidFuture,
@ -124,12 +126,16 @@ class _ConfirmChangeNowSendViewState
sendProgressController.triggerSuccess?.call();
await Future<void>.delayed(const Duration(seconds: 5));
txid = results.first as String;
txid = (results.first as TxData).txid!;
// save note
await ref
.read(notesServiceChangeNotifierProvider(walletId))
.editOrAddNote(txid: txid, note: note);
await ref.read(mainDBProvider).putTransactionNote(
TransactionNote(
walletId: walletId,
txid: txid,
value: note,
),
);
await ref.read(tradeSentFromStackLookupProvider).save(
tradeWalletLookup: TradeWalletLookup(
@ -199,8 +205,7 @@ class _ConfirmChangeNowSendViewState
Future<void> _confirmSend() async {
final dynamic unlocked;
final coin =
ref.read(walletsChangeNotifierProvider).getManager(walletId).coin;
final coin = ref.read(pWalletCoin(walletId));
if (Util.isDesktop) {
unlocked = await showDialog<bool?>(
@ -257,7 +262,6 @@ class _ConfirmChangeNowSendViewState
@override
void initState() {
transactionInfo = widget.transactionInfo;
walletId = widget.walletId;
routeOnSuccessName = widget.routeOnSuccessName;
trade = widget.trade;
@ -266,9 +270,6 @@ class _ConfirmChangeNowSendViewState
@override
Widget build(BuildContext context) {
final managerProvider = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManagerProvider(walletId)));
return ConditionalParent(
condition: !isDesktop,
builder: (child) {
@ -340,7 +341,7 @@ class _ConfirmChangeNowSendViewState
width: 12,
),
Text(
"Confirm ${ref.watch(managerProvider.select((value) => value.coin)).ticker} transaction",
"Confirm ${ref.watch(pWalletCoin(walletId)).ticker} transaction",
style: STextStyles.desktopH3(context),
)
],
@ -384,18 +385,9 @@ class _ConfirmChangeNowSendViewState
children: [
Text(
ref
.watch(pAmountFormatter(ref.watch(
managerProvider.select((value) => value.coin),
)))
.format(transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int)
.toAmountAsRaw(
fractionDigits: ref.watch(
managerProvider.select(
(value) => value.coin.decimals),
),
)),
.watch(pAmountFormatter(
ref.watch(pWalletCoin(walletId))))
.format(widget.txData.fee!),
style:
STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
@ -427,17 +419,9 @@ class _ConfirmChangeNowSendViewState
),
Builder(
builder: (context) {
final coin = ref.watch(
managerProvider.select((value) => value.coin),
);
final fee = transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int)
.toAmountAsRaw(
fractionDigits: coin.decimals,
);
final amount =
transactionInfo["recipientAmt"] as Amount;
final coin = ref.read(pWalletCoin(walletId));
final fee = widget.txData.fee!;
final amount = widget.txData.amountWithoutChange!;
final total = amount + fee;
return Text(
@ -509,7 +493,7 @@ class _ConfirmChangeNowSendViewState
),
),
child: Text(
"Send ${ref.watch(managerProvider.select((value) => value.coin)).ticker}",
"Send ${ref.watch(pWalletCoin(walletId)).ticker}",
style: isDesktop
? STextStyles.desktopTextMedium(context)
: STextStyles.pageTitleH1(context),
@ -536,10 +520,7 @@ class _ConfirmChangeNowSendViewState
height: 4,
),
Text(
ref
.watch(walletsChangeNotifierProvider)
.getManager(walletId)
.walletName,
ref.watch(pWalletName(walletId)),
style: STextStyles.itemSubtitle12(context),
),
],
@ -566,7 +547,7 @@ class _ConfirmChangeNowSendViewState
height: 4,
),
Text(
"${transactionInfo["address"] ?? "ERROR"}",
widget.txData.recipients!.first.address,
style: STextStyles.itemSubtitle12(context),
),
],
@ -595,15 +576,14 @@ class _ConfirmChangeNowSendViewState
children: [
child,
Builder(builder: (context) {
final coin = ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(walletId).coin));
final coin = ref.watch(pWalletCoin(walletId));
final price = ref.watch(
priceAnd24hChangeNotifierProvider
.select((value) => value.getPrice(coin)));
final amount =
transactionInfo["recipientAmt"] as Amount;
final value = (price.item1 * amount.decimal)
final amountWithoutChange =
widget.txData.amountWithoutChange!;
final value =
(price.item1 * amountWithoutChange.decimal)
.toAmount(fractionDigits: 2);
final currency = ref.watch(prefsChangeNotifierProvider
.select((value) => value.currency));
@ -628,10 +608,9 @@ class _ConfirmChangeNowSendViewState
),
child: Text(
ref
.watch(pAmountFormatter(ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(walletId).coin))))
.format((transactionInfo["recipientAmt"] as Amount)),
.watch(pAmountFormatter(
ref.watch(pWalletCoin(walletId))))
.format((widget.txData.amountWithoutChange!)),
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
@ -658,18 +637,10 @@ class _ConfirmChangeNowSendViewState
),
Text(
ref
.watch(pAmountFormatter(ref.watch(
managerProvider.select((value) => value.coin),
)))
.watch(
pAmountFormatter(ref.read(pWalletCoin(walletId))))
.format(
(transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int).toAmountAsRaw(
fractionDigits: ref.watch(
managerProvider.select(
(value) => value.coin.decimals,
),
))),
widget.txData.fee!,
),
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
@ -698,7 +669,7 @@ class _ConfirmChangeNowSendViewState
height: 4,
),
Text(
transactionInfo["note"] as String? ?? "",
widget.txData.note ?? "",
style: STextStyles.itemSubtitle12(context),
),
],
@ -751,16 +722,9 @@ class _ConfirmChangeNowSendViewState
),
Builder(
builder: (context) {
final coin = ref.watch(
managerProvider.select((value) => value.coin),
);
final fee = transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int).toAmountAsRaw(
fractionDigits: coin.decimals,
);
final amount =
transactionInfo["recipientAmt"] as Amount;
final coin = ref.watch(pWalletCoin(walletId));
final fee = widget.txData.fee!;
final amount = widget.txData.amountWithoutChange!;
final total = amount + fee;
return Text(

View file

@ -96,22 +96,22 @@ class _Step2ViewState extends ConsumerState<Step2View> {
if (model.receiveTicker.toLowerCase() ==
tuple.item2.ticker.toLowerCase()) {
ref
.read(walletsChangeNotifierProvider)
.getManager(tuple.item1)
.currentReceivingAddress
.read(pWallets)
.getWallet(tuple.item1)
.getCurrentReceivingAddress()
.then((value) {
_toController.text = value;
_toController.text = value!.value;
model.recipientAddress = _toController.text;
});
} else {
if (model.sendTicker.toUpperCase() ==
tuple.item2.ticker.toUpperCase()) {
ref
.read(walletsChangeNotifierProvider)
.getManager(tuple.item1)
.currentReceivingAddress
.read(pWallets)
.getWallet(tuple.item1)
.getCurrentReceivingAddress()
.then((value) {
_refundController.text = value;
_refundController.text = value!.value;
model.refundAddress = _refundController.text;
});
}
@ -218,15 +218,14 @@ class _Step2ViewState extends ConsumerState<Step2View> {
)
.then((value) async {
if (value is String) {
final manager = ref
.read(
walletsChangeNotifierProvider)
.getManager(value);
final wallet = ref
.read(pWallets)
.getWallet(value);
_toController.text =
manager.walletName;
model.recipientAddress = await manager
.currentReceivingAddress;
_toController.text = wallet.info.name;
model.recipientAddress = (await wallet
.getCurrentReceivingAddress())!
.value;
setState(() {
enableNext =
@ -490,15 +489,15 @@ class _Step2ViewState extends ConsumerState<Step2View> {
)
.then((value) async {
if (value is String) {
final manager = ref
.read(
walletsChangeNotifierProvider)
.getManager(value);
final wallet = ref
.read(pWallets)
.getWallet(value);
_refundController.text =
manager.walletName;
model.refundAddress = await manager
.currentReceivingAddress;
wallet.info.name;
model.refundAddress = (await wallet
.getCurrentReceivingAddress())!
.value;
}
setState(() {
enableNext =

View file

@ -25,7 +25,6 @@ import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dia
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
@ -35,6 +34,9 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
@ -71,9 +73,9 @@ class _Step4ViewState extends ConsumerState<Step4View> {
try {
final coin = coinFromTickerCaseInsensitive(ticker);
return ref
.read(walletsChangeNotifierProvider)
.managers
.where((element) => element.coin == coin)
.read(pWallets)
.wallets
.where((e) => e.info.coin == coin)
.isNotEmpty;
} catch (_) {
return false;
@ -134,11 +136,9 @@ class _Step4ViewState extends ConsumerState<Step4View> {
}
Future<bool?> _showSendFromFiroBalanceSelectSheet(String walletId) async {
final firoWallet = ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.wallet as FiroWallet;
final locale = ref.read(localeServiceChangeNotifierProvider).locale;
final coin = ref.read(pWalletCoin(walletId));
final balancePublic = ref.read(pWalletBalance(walletId));
final balancePrivate = ref.read(pWalletBalanceSecondary(walletId));
return await showModalBottomSheet<bool?>(
context: context,
@ -172,7 +172,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
),
SecondaryButton(
label:
"${ref.watch(pAmountFormatter(firoWallet.coin)).format(firoWallet.balancePrivate.spendable)} (private)",
"${ref.watch(pAmountFormatter(coin)).format(balancePrivate.spendable)} (private)",
onPressed: () => Navigator.of(context).pop(false),
),
const SizedBox(
@ -180,7 +180,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
),
SecondaryButton(
label:
"${ref.watch(pAmountFormatter(firoWallet.coin)).format(firoWallet.balance.spendable)} (public)",
"${ref.watch(pAmountFormatter(coin)).format(balancePublic.spendable)} (public)",
onPressed: () => Navigator.of(context).pop(true),
),
const SizedBox(
@ -206,11 +206,10 @@ class _Step4ViewState extends ConsumerState<Step4View> {
firoPublicSend = false;
}
final manager =
ref.read(walletsChangeNotifierProvider).getManager(tuple.item1);
final wallet = ref.read(pWallets).getWallet(tuple.item1);
final Amount amount = model.sendAmount.toAmount(
fractionDigits: manager.coin.decimals,
fractionDigits: wallet.info.coin.decimals,
);
final address = model.trade!.payInAddress;
@ -225,7 +224,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
barrierDismissible: false,
builder: (context) {
return BuildingTransactionDialog(
coin: manager.coin,
coin: wallet.info.coin,
onCancel: () {
wasCancelled = true;
},
@ -240,32 +239,45 @@ class _Step4ViewState extends ConsumerState<Step4View> {
),
);
Future<Map<String, dynamic>> txDataFuture;
Future<TxData> txDataFuture;
if (firoPublicSend) {
txDataFuture = (manager.wallet as FiroWallet).prepareSendPublic(
// TODO: [prio=high] Firo spark
if (wallet is FiroWallet && !firoPublicSend) {
txDataFuture = wallet.prepareSendLelantus(
txData: TxData(
recipients: [
(
address: address,
amount: amount,
args: {
"feeRate": FeeRateType.average,
// ref.read(feeRateTypeStateProvider)
},
isChange: false,
)
],
note: "${model.trade!.payInCurrency.toUpperCase()}/"
"${model.trade!.payOutCurrency.toUpperCase()} exchange",
),
);
} else {
final memo =
manager.coin == Coin.stellar || manager.coin == Coin.stellarTestnet
final memo = wallet.info.coin == Coin.stellar ||
wallet.info.coin == Coin.stellarTestnet
? model.trade!.payInExtraId.isNotEmpty
? model.trade!.payInExtraId
: null
: null;
txDataFuture = manager.prepareSend(
txDataFuture = wallet.prepareSend(
txData: TxData(
recipients: [
(
address: address,
amount: amount,
args: {
"memo": memo,
"feeRate": FeeRateType.average,
// ref.read(feeRateTypeStateProvider)
},
isChange: false,
),
],
memo: memo,
feeRateType: FeeRateType.average,
note: "${model.trade!.payInCurrency.toUpperCase()}/"
"${model.trade!.payOutCurrency.toUpperCase()} exchange",
),
);
}
@ -274,7 +286,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
time,
]);
final txData = results.first as Map<String, dynamic>;
final txData = results.first as TxData;
if (!wasCancelled) {
// pop building dialog
@ -283,17 +295,13 @@ class _Step4ViewState extends ConsumerState<Step4View> {
Navigator.of(context).pop();
}
txData["note"] =
"${model.trade!.payInCurrency.toUpperCase()}/${model.trade!.payOutCurrency.toUpperCase()} exchange";
txData["address"] = address;
if (mounted) {
unawaited(
Navigator.of(context).push(
RouteGenerator.getRoute(
shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
builder: (_) => ConfirmChangeNowSendView(
transactionInfo: txData,
txData: txData,
walletId: tuple.item1,
routeOnSuccessName: HomeView.routeName,
trade: model.trade!,
@ -815,9 +823,10 @@ class _Step4ViewState extends ConsumerState<Step4View> {
model.sendTicker.toLowerCase() ==
tuple.item2.ticker.toLowerCase()) {
final walletName = ref
.read(walletsChangeNotifierProvider)
.getManager(tuple.item1)
.walletName;
.read(pWallets)
.getWallet(tuple.item1)
.info
.name;
buttonTitle = "Send from $walletName";
}

View file

@ -195,9 +195,9 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
if (txid != null &&
walletIds != null &&
walletIds.isNotEmpty) {
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(walletIds.first);
final wallet = ref
.read(pWallets)
.getWallet(walletIds.first);
//todo: check if print needed
// debugPrint("name: ${manager.walletName}");
@ -212,7 +212,7 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
unawaited(Navigator.of(context).pushNamed(
TradeDetailsView.routeName,
arguments: Tuple4(tradeId, tx,
walletIds.first, manager.walletName),
walletIds.first, wallet.info.name),
));
}
} else {

View file

@ -21,8 +21,6 @@ import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dia
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
@ -33,6 +31,9 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -85,8 +86,12 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final walletIds = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getWalletIdsFor(coin: coin)));
final walletIds = ref
.watch(pWallets)
.wallets
.where((e) => e.info.coin == coin)
.map((e) => e.walletId)
.toList();
final isDesktop = Util.isDesktop;
@ -224,7 +229,9 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
late final String address;
late final Trade trade;
Future<void> _send(Manager manager, {bool? shouldSendPublicFiroFunds}) async {
Future<void> _send({bool? shouldSendPublicFiroFunds}) async {
final coin = ref.read(pWalletCoin(walletId));
try {
bool wasCancelled = false;
@ -245,7 +252,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
),
),
child: BuildingTransactionDialog(
coin: manager.coin,
coin: coin,
onCancel: () {
wasCancelled = true;
@ -263,46 +270,56 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
),
);
Map<String, dynamic> txData;
Future<Map<String, dynamic>> txDataFuture;
TxData txData;
Future<TxData> txDataFuture;
final wallet = ref.read(pWallets).getWallet(walletId);
// if not firo then do normal send
if (shouldSendPublicFiroFunds == null) {
final memo =
manager.coin == Coin.stellar || manager.coin == Coin.stellarTestnet
final memo = coin == Coin.stellar || coin == Coin.stellarTestnet
? trade.payInExtraId.isNotEmpty
? trade.payInExtraId
: null
: null;
txDataFuture = manager.prepareSend(
txDataFuture = wallet.prepareSend(
txData: TxData(
recipients: [
(
address: address,
amount: amount,
args: {
"memo": memo,
"feeRate": FeeRateType.average,
// ref.read(feeRateTypeStateProvider)
},
amount: amount, isChange: false,
),
],
memo: memo,
feeRateType: FeeRateType.average,
),
);
} else {
final firoWallet = manager.wallet as FiroWallet;
final firoWallet = wallet as FiroWallet;
// otherwise do firo send based on balance selected
if (shouldSendPublicFiroFunds) {
txDataFuture = firoWallet.prepareSendPublic(
txDataFuture = wallet.prepareSend(
txData: TxData(
recipients: [
(
address: address,
amount: amount,
args: {
"feeRate": FeeRateType.average,
// ref.read(feeRateTypeStateProvider)
},
amount: amount, isChange: false,
),
],
feeRateType: FeeRateType.average,
),
);
} else {
txDataFuture = firoWallet.prepareSend(
txDataFuture = firoWallet.prepareSendLelantus(
txData: TxData(
recipients: [
(
address: address,
amount: amount,
args: {
"feeRate": FeeRateType.average,
// ref.read(feeRateTypeStateProvider)
},
amount: amount, isChange: false,
),
],
// feeRateType: FeeRateType.average,
),
);
}
}
@ -312,7 +329,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
time,
]);
txData = results.first as Map<String, dynamic>;
txData = results.first as TxData;
if (!wasCancelled) {
// pop building dialog
@ -324,16 +341,17 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
).pop();
}
txData["note"] =
"${trade.payInCurrency.toUpperCase()}/${trade.payOutCurrency.toUpperCase()} exchange";
txData["address"] = address;
txData = txData.copyWith(
note: "${trade.payInCurrency.toUpperCase()}/"
"${trade.payOutCurrency.toUpperCase()} exchange",
);
if (mounted) {
await Navigator.of(context).push(
RouteGenerator.getRoute(
shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
builder: (_) => ConfirmChangeNowSendView(
transactionInfo: txData,
txData: txData,
walletId: walletId,
routeOnSuccessName: Util.isDesktop
? DesktopExchangeView.routeName
@ -396,14 +414,12 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
@override
Widget build(BuildContext context) {
final manager = ref.watch(ref
.watch(walletsChangeNotifierProvider.notifier)
.getManagerProvider(walletId));
final wallet = ref.watch(pWallets).getWallet(walletId);
final locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale));
final coin = manager.coin;
final coin = ref.watch(pWalletCoin(walletId));
final isFiro = coin == Coin.firoTestNet || coin == Coin.firo;
@ -438,7 +454,6 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
if (mounted) {
unawaited(
_send(
manager,
shouldSendPublicFiroFunds: false,
),
);
@ -465,10 +480,9 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
style: STextStyles.itemSubtitle(context),
),
Text(
ref.watch(pAmountFormatter(coin)).format(
(manager.wallet as FiroWallet)
.availablePrivateBalance(),
),
ref.watch(pAmountFormatter(coin)).format(ref
.watch(pWalletBalance(walletId))
.spendable),
style: STextStyles.itemSubtitle(context),
),
],
@ -501,7 +515,6 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
if (mounted) {
unawaited(
_send(
manager,
shouldSendPublicFiroFunds: true,
),
);
@ -529,8 +542,11 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
),
Text(
ref.watch(pAmountFormatter(coin)).format(
(manager.wallet as FiroWallet)
.availablePublicBalance()),
ref
.watch(
pWalletBalanceSecondary(walletId))
.spendable,
),
style: STextStyles.itemSubtitle(context),
),
],
@ -569,7 +585,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
onPressed: () async {
if (mounted) {
unawaited(
_send(manager),
_send(),
);
}
},
@ -581,7 +597,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.colorForCoin(manager.coin)
.colorForCoin(coin)
.withOpacity(0.5),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
@ -609,7 +625,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
manager.walletName,
ref.watch(pWalletName(walletId)),
style: STextStyles.titleBold12(context),
),
if (!isFiro)
@ -618,9 +634,8 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
),
if (!isFiro)
Text(
ref
.watch(pAmountFormatter(coin))
.format(manager.balance.spendable),
ref.watch(pAmountFormatter(coin)).format(
ref.watch(pWalletBalance(walletId)).spendable),
style: STextStyles.itemSubtitle(context),
),
],

View file

@ -84,8 +84,6 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
late final Transaction? transactionIfSentFromStack;
late final String? walletId;
String _note = "";
bool isStackCoin(String ticker) {
try {
try {
@ -960,7 +958,9 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
maxHeight: 360,
child: EditTradeNoteView(
tradeId: tradeId,
note: _note,
note: ref
.read(tradeNoteServiceProvider)
.getNote(tradeId: tradeId),
),
);
},
@ -1046,7 +1046,6 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
txid:
transactionIfSentFromStack!.txid,
walletId: walletId!,
note: _note,
),
);
},
@ -1057,10 +1056,9 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
onTap: () {
Navigator.of(context).pushNamed(
EditNoteView.routeName,
arguments: Tuple3(
arguments: Tuple2(
transactionIfSentFromStack!.txid,
walletId!,
_note,
walletId,
),
);
},
@ -1089,22 +1087,19 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
const SizedBox(
height: 4,
),
FutureBuilder(
future: ref.watch(
notesServiceChangeNotifierProvider(walletId!).select(
(value) => value.getNoteFor(
txid: transactionIfSentFromStack!.txid))),
builder:
(builderContext, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
_note = snapshot.data ?? "";
}
return SelectableText(
_note,
SelectableText(
ref
.watch(
pTransactionNote(
(
txid: transactionIfSentFromStack!.txid,
walletId: walletId!,
),
),
)
?.value ??
"",
style: STextStyles.itemSubtitle12(context),
);
},
),
],
),

View file

@ -33,7 +33,6 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/onetime_popups/tor_has_been_add_dialog.dart';
import 'package:stackwallet/widgets/small_tor_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
@ -130,9 +129,9 @@ class _HomeViewState extends ConsumerState<HomeView> {
ref.read(notificationsProvider).startCheckingWatchedNotifications();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
showOneTimeTorHasBeenAddedDialogIfRequired(context);
});
// WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
// showOneTimeTorHasBeenAddedDialogIfRequired(context);
// });
super.initState();
}

View file

@ -10,11 +10,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/favourite_wallets_provider.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
@ -61,8 +62,8 @@ class ManageFavoritesView extends StatelessWidget {
body: isDesktop
? Consumer(
builder: (_, ref, __) {
final favorites = ref.watch(favoritesProvider);
final nonFavorites = ref.watch(nonFavoritesProvider);
final favorites = ref.watch(pFavouriteWalletInfos(true));
final nonFavorites = ref.watch(pFavouriteWalletInfos(false));
return Column(
children: [
@ -108,8 +109,7 @@ class ManageFavoritesView extends StatelessWidget {
key: key,
itemCount: favorites.length,
itemBuilder: (builderContext, index) {
final walletId =
ref.read(favorites[index]).walletId;
final walletId = favorites[index].walletId;
return Padding(
key: Key(
"manageFavoriteWalletsItem_$walletId",
@ -127,14 +127,43 @@ class ManageFavoritesView extends StatelessWidget {
);
},
onReorder: (oldIndex, newIndex) {
ref
.read(walletsServiceChangeNotifierProvider)
.moveFavorite(
fromIndex: oldIndex, toIndex: newIndex);
final isar = ref.read(mainDBProvider).isar;
ref
.read(favoritesProvider)
.reorder(oldIndex, newIndex, true);
final actualIndex =
favorites[oldIndex].favouriteOrderIndex;
if (oldIndex > newIndex) {
for (int i = oldIndex - 1; i >= newIndex; i--) {
final next = favorites[i];
next.updateIsFavourite(
true,
isar: isar,
customIndexOverride:
next.favouriteOrderIndex + 1,
);
}
favorites[oldIndex].updateIsFavourite(
true,
isar: isar,
customIndexOverride:
actualIndex - (oldIndex - newIndex),
);
} else {
for (int i = oldIndex + 1; i < newIndex; i++) {
final next = favorites[i];
next.updateIsFavourite(
true,
isar: isar,
customIndexOverride:
next.favouriteOrderIndex - 1,
);
}
favorites[oldIndex].updateIsFavourite(
true,
isar: isar,
customIndexOverride:
actualIndex + (newIndex - oldIndex),
);
}
},
proxyDecorator: (child, index, animation) {
return Material(
@ -176,8 +205,7 @@ class ManageFavoritesView extends StatelessWidget {
itemBuilder: (buildContext, index) {
// final walletId = ref.watch(
// nonFavorites[index].select((value) => value.walletId));
final walletId =
ref.read(nonFavorites[index]).walletId;
final walletId = nonFavorites[index].walletId;
return Padding(
key: Key(
"manageNonFavoriteWalletsItem_$walletId",
@ -236,13 +264,13 @@ class ManageFavoritesView extends StatelessWidget {
Expanded(
child: Consumer(
builder: (_, ref, __) {
final favorites = ref.watch(favoritesProvider);
final favorites =
ref.watch(pFavouriteWalletInfos(true));
return ReorderableListView.builder(
key: key,
itemCount: favorites.length,
itemBuilder: (builderContext, index) {
final walletId =
ref.read(favorites[index]).walletId;
final walletId = favorites[index].walletId;
return Padding(
key: Key(
"manageFavoriteWalletsItem_$walletId",
@ -254,14 +282,43 @@ class ManageFavoritesView extends StatelessWidget {
);
},
onReorder: (oldIndex, newIndex) {
ref
.read(walletsServiceChangeNotifierProvider)
.moveFavorite(
fromIndex: oldIndex, toIndex: newIndex);
final isar = ref.read(mainDBProvider).isar;
ref
.read(favoritesProvider)
.reorder(oldIndex, newIndex, true);
final actualIndex =
favorites[oldIndex].favouriteOrderIndex;
if (oldIndex > newIndex) {
for (int i = oldIndex - 1; i >= newIndex; i--) {
final next = favorites[i];
next.updateIsFavourite(
true,
isar: isar,
customIndexOverride:
next.favouriteOrderIndex + 1,
);
}
favorites[oldIndex].updateIsFavourite(
true,
isar: isar,
customIndexOverride:
actualIndex - (oldIndex - newIndex),
);
} else {
for (int i = oldIndex + 1; i < newIndex; i++) {
final next = favorites[i];
next.updateIsFavourite(
true,
isar: isar,
customIndexOverride:
next.favouriteOrderIndex - 1,
);
}
favorites[oldIndex].updateIsFavourite(
true,
isar: isar,
customIndexOverride:
actualIndex + (newIndex - oldIndex),
);
}
},
proxyDecorator: (child, index, animation) {
return Material(
@ -301,15 +358,15 @@ class ManageFavoritesView extends StatelessWidget {
Expanded(
child: Consumer(
builder: (_, ref, __) {
final nonFavorites = ref.watch(nonFavoritesProvider);
final nonFavorites =
ref.watch(pFavouriteWalletInfos(false));
return ListView.builder(
itemCount: nonFavorites.length,
itemBuilder: (buildContext, index) {
// final walletId = ref.watch(
// nonFavorites[index].select((value) => value.walletId));
final walletId =
ref.read(nonFavorites[index]).walletId;
final walletId = nonFavorites[index].walletId;
return Padding(
key: Key(
"manageNonFavoriteWalletsItem_$walletId",

View file

@ -10,7 +10,7 @@
// import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
// import 'package:stackwallet/providers/global/wallets_provider.dart';
// import 'package:stackwallet/services/coins/banano/banano_wallet.dart';
// import 'package:stackwallet/services/coins/manager.dart';
//
// import 'package:stackwallet/themes/stack_colors.dart';
// import 'package:stackwallet/utilities/assets.dart';
// import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -146,7 +146,7 @@
// WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
// final address = await ref
// .read(walletsChangeNotifierProvider)
// .getManager(walletId)
// .getWallet(walletId)
// .currentReceivingAddress;
// setState(() {
// receivingAddress = address;
@ -164,8 +164,8 @@
// @override
// Widget build(BuildContext context) {
// final Coin coin = ref.watch(managerProvider.select((value) => value.coin));
// final manager = ref.watch(walletsChangeNotifierProvider
// .select((value) => value.getManager(widget.walletId)));
// final wallet = ref.watch(walletsChangeNotifierProvider
// .select((value) => value.getWallet(widget.walletId)));
//
// List<int>? imageBytes;
// imageBytes = (manager.wallet as BananoWallet).getMonkeyImageBytes();

View file

@ -8,15 +8,15 @@ import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/banano/banano_wallet.dart';
import 'package:stackwallet/services/monkey_service.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/impl/banano_wallet.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -48,9 +48,7 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
List<int>? imageBytes;
Future<void> _updateWalletMonKey(Uint8List monKeyBytes) async {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
await (manager.wallet as BananoWallet)
await (ref.read(pWallets).getWallet(walletId) as BananoWallet)
.updateMonkeyImageBytes(monKeyBytes.toList());
}
@ -83,9 +81,9 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
}
final address = await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.currentReceivingAddress;
.read(pWallets)
.getWallet(walletId)
.getCurrentReceivingAddress();
final docPath = dir.path;
String filePath = "$docPath/monkey_$address";
@ -110,13 +108,12 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
@override
Widget build(BuildContext context) {
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId)));
final Coin coin = manager.coin;
final wallet = ref.watch(pWallets).getWallet(widget.walletId);
final coin = ref.watch(pWalletCoin(widget.walletId));
final bool isDesktop = Util.isDesktop;
imageBytes ??= (manager.wallet as BananoWallet).getMonkeyImageBytes();
imageBytes ??= (wallet as BananoWallet).getMonkeyImageBytes();
return Background(
child: ConditionalParent(
@ -344,7 +341,7 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
whileFuture: Future.wait([
_saveMonKeyToFile(
bytes: Uint8List.fromList(
(manager.wallet as BananoWallet)
(wallet as BananoWallet)
.getMonkeyImageBytes()!),
),
Future<void>.delayed(
@ -386,11 +383,11 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
bool didError = false;
await showLoading(
whileFuture: Future.wait([
manager.currentReceivingAddress.then(
wallet.getCurrentReceivingAddress().then(
(address) async => await ref
.read(pMonKeyService)
.fetchMonKey(
address: address,
address: address!.value,
png: true,
)
.then(
@ -489,10 +486,10 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
onPressed: () async {
await showLoading(
whileFuture: Future.wait([
manager.currentReceivingAddress.then(
wallet.getCurrentReceivingAddress().then(
(address) async => await ref
.read(pMonKeyService)
.fetchMonKey(address: address)
.fetchMonKey(address: address!.value)
.then(
(monKeyBytes) async =>
await _updateWalletMonKey(
@ -520,8 +517,8 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
},
);
imageBytes = (manager.wallet as BananoWallet)
.getMonkeyImageBytes();
imageBytes =
(wallet as BananoWallet).getMonkeyImageBytes();
if (imageBytes != null) {
setState(() {});

View file

@ -13,7 +13,6 @@ import 'package:stackwallet/networking/http.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/tor_service.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
@ -23,6 +22,7 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
@ -57,8 +57,7 @@ class _OrdinalDetailsViewState extends ConsumerState<OrdinalDetailsView> {
@override
Widget build(BuildContext context) {
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).coin));
final coin = ref.watch(pWalletCoin(widget.walletId));
return Background(
child: SafeArea(

View file

@ -13,11 +13,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/mixins/ordinals_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -89,10 +89,8 @@ class _OrdinalsViewState extends ConsumerState<OrdinalsView> {
await showLoading(
whileFuture: Future.wait<void>([
Future.delayed(const Duration(seconds: 2)),
(ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as OrdinalsInterface)
(ref.read(pWallets).getWallet(widget.walletId)
as OrdinalsInterface)
.refreshInscriptions()
]),
context: context,

View file

@ -26,12 +26,13 @@ import 'package:stackwallet/pages/send_view/send_view.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
import 'package:stackwallet/widgets/custom_buttons/paynym_follow_toggle_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
@ -59,13 +60,12 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
bool _showInsufficientFundsInfo = false;
Future<void> _onSend() async {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(widget.walletId);
final wallet = ref.read(pWallets).getWallet(widget.walletId);
await Navigator.of(context).pushNamed(
SendView.routeName,
arguments: Tuple3(
manager.walletId,
manager.coin,
wallet.walletId,
wallet.info.coin,
widget.accountLite,
),
);
@ -85,10 +85,9 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
),
);
final manager =
ref.read(walletsChangeNotifierProvider).getManager(widget.walletId);
final wallet = manager.wallet as PaynymWalletInterface;
final wallet =
ref.read(pWallets).getWallet(widget.walletId) as PaynymInterface;
final coin = ref.read(pWalletCoin(widget.walletId));
if (await wallet.hasConnected(widget.accountLite.code)) {
canPop = true;
@ -97,9 +96,9 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
return;
}
final rates = await manager.fees;
final rates = await ref.read(pWallets).getWallet(widget.walletId).fees;
Map<String, dynamic> preparedTx;
TxData preparedTx;
try {
preparedTx = await wallet.prepareNotificationTx(
@ -148,32 +147,22 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
nymName: widget.accountLite.nymName,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
onConfirmPressed: () {
//
print("CONFIRM NOTIF TX: $preparedTx");
Navigator.of(context).push(
RouteGenerator.getRoute(
builder: (_) => ConfirmTransactionView(
walletId: manager.walletId,
walletId: widget.walletId,
routeOnSuccessName: PaynymHomeView.routeName,
isPaynymNotificationTransaction: true,
transactionInfo: {
"hex": preparedTx["hex"],
"address": preparedTx["recipientPaynym"],
"recipientAmt": preparedTx["amount"],
"fee": preparedTx["fee"],
"vSize": preparedTx["vSize"],
"note": "PayNym connect"
txData: preparedTx,
onSuccess: () {
// do nothing extra
},
),
),
);
},
amount: (preparedTx["amount"] as Amount) +
(preparedTx["fee"] as int).toAmountAsRaw(
fractionDigits: manager.coin.decimals,
),
coin: manager.coin,
amount: preparedTx.amount! + preparedTx.fee!,
coin: coin,
),
);
}
@ -181,10 +170,8 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
@override
Widget build(BuildContext context) {
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId)));
final wallet = manager.wallet as PaynymWalletInterface;
final wallet =
ref.watch(pWallets).getWallet(widget.walletId) as PaynymInterface;
return DesktopDialog(
maxWidth: MediaQuery.of(context).size.width - 32,
@ -316,7 +303,7 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
"Adding a PayNym to your contacts requires a one-time "
"transaction fee for creating the record on the "
"blockchain. Please deposit more "
"${ref.read(walletsChangeNotifierProvider).getManager(widget.walletId).wallet.coin.ticker} "
"${ref.watch(pWalletCoin(widget.walletId)).ticker} "
"into your wallet and try again.",
style: STextStyles.infoSmall(context).copyWith(
color: Theme.of(context)

View file

@ -20,11 +20,11 @@ import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/providers/global/paynym_api_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
@ -47,11 +47,8 @@ class PaynymClaimView extends ConsumerStatefulWidget {
class _PaynymClaimViewState extends ConsumerState<PaynymClaimView> {
Future<bool> _addSegwitCode(PaynymAccount myAccount) async {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(widget.walletId);
// get wallet to access paynym calls
final wallet = manager.wallet as PaynymWalletInterface;
final wallet =
ref.read(pWallets).getWallet(widget.walletId) as PaynymInterface;
final token = await ref
.read(paynymAPIProvider)
@ -190,12 +187,8 @@ class _PaynymClaimViewState extends ConsumerState<PaynymClaimView> {
).then((value) => shouldCancel = value == true),
);
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId);
// get wallet to access paynym calls
final wallet = manager.wallet as PaynymWalletInterface;
final wallet = ref.read(pWallets).getWallet(widget.walletId)
as PaynymInterface;
if (shouldCancel) return;

View file

@ -24,12 +24,13 @@ import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/custom_buttons/paynym_follow_toggle_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
@ -70,10 +71,8 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
),
);
final manager =
ref.read(walletsChangeNotifierProvider).getManager(widget.walletId);
final wallet = manager.wallet as PaynymWalletInterface;
final wallet =
ref.read(pWallets).getWallet(widget.walletId) as PaynymInterface;
if (await wallet.hasConnected(widget.accountLite.code)) {
canPop = true;
@ -82,9 +81,9 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
return;
}
final rates = await manager.fees;
final rates = await ref.read(pWallets).getWallet(widget.walletId).fees;
Map<String, dynamic> preparedTx;
TxData preparedTx;
try {
preparedTx = await wallet.prepareNotificationTx(
@ -121,18 +120,14 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
showDialog(
context: context,
builder: (context) => DesktopDialog(
maxHeight: double.infinity,
maxHeight: MediaQuery.of(context).size.height - 64,
maxWidth: 580,
child: ConfirmTransactionView(
walletId: manager.walletId,
walletId: widget.walletId,
isPaynymNotificationTransaction: true,
transactionInfo: {
"hex": preparedTx["hex"],
"address": preparedTx["recipientPaynym"],
"recipientAmt": preparedTx["amount"],
"fee": preparedTx["fee"],
"vSize": preparedTx["vSize"],
"note": "PayNym connect"
txData: preparedTx,
onSuccess: () {
// do nothing extra
},
onSuccessInsteadOfRouteOnSuccess: () {
Navigator.of(context, rootNavigator: true).pop();
@ -152,11 +147,8 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
),
);
},
amount: (preparedTx["amount"] as Amount) +
(preparedTx["fee"] as int).toAmountAsRaw(
fractionDigits: manager.coin.decimals,
),
coin: manager.coin,
amount: preparedTx.amount! + preparedTx.fee!,
coin: ref.read(pWalletCoin(widget.walletId)),
),
);
}
@ -174,10 +166,9 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
@override
Widget build(BuildContext context) {
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId)));
final wallet = ref.watch(pWallets).getWallet(widget.walletId);
final wallet = manager.wallet as PaynymWalletInterface;
final paynymWallet = wallet as PaynymInterface;
return RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
@ -205,7 +196,8 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
style: STextStyles.desktopTextSmall(context),
),
FutureBuilder(
future: wallet.hasConnected(widget.accountLite.code),
future: paynymWallet
.hasConnected(widget.accountLite.code),
builder: (context, AsyncSnapshot<bool> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
@ -243,7 +235,8 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
children: [
Expanded(
child: FutureBuilder(
future: wallet.hasConnected(widget.accountLite.code),
future:
paynymWallet.hasConnected(widget.accountLite.code),
builder: (context, AsyncSnapshot<bool> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
@ -315,7 +308,7 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
"Adding a PayNym to your contacts requires a one-time "
"transaction fee for creating the record on the "
"blockchain. Please deposit more "
"${ref.read(walletsChangeNotifierProvider).getManager(widget.walletId).wallet.coin.ticker} "
"${ref.watch(pWalletCoin(widget.walletId)).ticker} "
"into your wallet and try again.",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(

View file

@ -16,12 +16,12 @@ import 'package:stackwallet/pages/paynym/subwidgets/paynym_card_button.dart';
import 'package:stackwallet/providers/global/paynym_api_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -75,12 +75,8 @@ class _PaynymFollowersListState extends ConsumerState<PaynymFollowersList> {
child: child,
onRefresh: () async {
try {
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId);
// get wallet to access paynym calls
final wallet = manager.wallet as PaynymWalletInterface;
final wallet = ref.read(pWallets).getWallet(widget.walletId)
as PaynymInterface;
// get payment code
final pCode = await wallet.getPaymentCode(

View file

@ -16,12 +16,12 @@ import 'package:stackwallet/pages/paynym/subwidgets/paynym_card_button.dart';
import 'package:stackwallet/providers/global/paynym_api_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -75,12 +75,8 @@ class _PaynymFollowingListState extends ConsumerState<PaynymFollowingList> {
child: child,
onRefresh: () async {
try {
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId);
// get wallet to access paynym calls
final wallet = manager.wallet as PaynymWalletInterface;
final wallet = ref.read(pWallets).getWallet(widget.walletId)
as PaynymInterface;
// get payment code
final pCode = await wallet.getPaymentCode(

View file

@ -32,7 +32,6 @@ 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_pin_put/custom_pin_put.dart';
import 'package:stackwallet/widgets/shake/shake.dart';
import 'package:tuple/tuple.dart';
class LockscreenView extends ConsumerStatefulWidget {
const LockscreenView({
@ -98,14 +97,13 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
if (loadIntoWallet) {
final walletId = widget.routeOnSuccessArguments as String;
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
if (manager.coin == Coin.monero) {
final wallet = ref.read(pWallets).getWallet(walletId);
if (wallet.info.coin == Coin.monero) {
await showLoading(
opaqueBG: true,
whileFuture: manager.initializeExisting(),
whileFuture: wallet.init(),
context: context,
message: "Loading ${manager.coin.prettyName} wallet...",
message: "Loading ${wallet.info.coin.prettyName} wallet...",
);
}
}
@ -124,12 +122,7 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
unawaited(
Navigator.of(context).pushNamed(
WalletView.routeName,
arguments: Tuple2(
walletId,
ref
.read(walletsChangeNotifierProvider)
.getManagerProvider(walletId),
),
arguments: walletId,
),
);
}

View file

@ -28,6 +28,7 @@ import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -95,9 +96,7 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
key: _qrKey,
child: QrImageView(
data: AddressUtils.buildUriString(
ref.watch(walletsChangeNotifierProvider.select(
(value) =>
value.getManager(widget.walletId).coin)),
ref.watch(pWalletCoin(widget.walletId)),
address.value,
{},
),
@ -151,8 +150,7 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
@override
Widget build(BuildContext context) {
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).coin));
final coin = ref.watch(pWalletCoin(widget.walletId));
return ConditionalParent(
condition: !isDesktop,
builder: (child) => Background(
@ -266,8 +264,11 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
borderColor: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
child: coin == Coin.bitcoincash ||
coin == Coin.bitcoincashTestnet
child: ref
.watch(pWallets)
.getWallet(widget.walletId)
.isarTransactionVersion ==
2
? _AddressDetailsTxV2List(
walletId: widget.walletId,
address: address,
@ -292,9 +293,7 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
key: _qrKey,
child: QrImageView(
data: AddressUtils.buildUriString(
ref.watch(walletsChangeNotifierProvider.select(
(value) =>
value.getManager(widget.walletId).coin)),
coin,
address.value,
{},
),
@ -357,6 +356,16 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
data: address.derivationPath!.value,
button: Container(),
),
if (address.type == AddressType.spark)
const _Div(
height: 12,
),
if (address.type == AddressType.spark)
_Item(
title: "Diversifier",
data: address.derivationIndex.toString(),
button: Container(),
),
const _Div(
height: 12,
),

View file

@ -16,11 +16,11 @@ import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/receive_view/addresses/address_card.dart';
import 'package:stackwallet/pages/receive_view/addresses/address_details_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -135,8 +135,8 @@ class _WalletAddressesViewState extends ConsumerState<WalletAddressesView> {
@override
Widget build(BuildContext context) {
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).coin));
final coin = ref.watch(pWalletCoin(widget.walletId));
return ConditionalParent(
condition: !isDesktop,
builder: (child) => Background(

View file

@ -10,15 +10,18 @@
import 'dart:async';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:isar/isar.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/receive_view/addresses/wallet_addresses_view.dart';
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/themes/stack_colors.dart';
@ -28,7 +31,11 @@ import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.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_loading_overlay.dart';
@ -56,8 +63,16 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
late final Coin coin;
late final String walletId;
late final ClipboardInterface clipboard;
late final bool supportsSpark;
String? _sparkAddress;
String? _qrcodeContent;
bool _showSparkAddress = true;
Future<void> generateNewAddress() async {
final wallet = ref.read(pWallets).getWallet(walletId);
if (wallet is MultiAddressInterface) {
bool shouldPop = false;
unawaited(
showDialog(
@ -80,10 +95,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
),
);
await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.generateNewAddress();
await wallet.generateNewReceivingAddress();
shouldPop = true;
@ -92,45 +104,108 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
.popUntil(ModalRoute.withName(ReceiveView.routeName));
}
}
}
String receivingAddress = "";
Future<void> generateNewSparkAddress() async {
final wallet = ref.read(pWallets).getWallet(walletId);
if (wallet is SparkInterface) {
bool shouldPop = false;
unawaited(
showDialog(
context: context,
builder: (_) {
return WillPopScope(
onWillPop: () async => shouldPop,
child: Container(
color: Theme.of(context)
.extension<StackColors>()!
.overlay
.withOpacity(0.5),
child: const CustomLoadingOverlay(
message: "Generating address",
eventBus: null,
),
),
);
},
),
);
final address = await wallet.generateNextSparkAddress();
await ref.read(mainDBProvider).isar.writeTxn(() async {
await ref.read(mainDBProvider).isar.addresses.put(address);
});
shouldPop = true;
if (mounted) {
Navigator.of(context, rootNavigator: true).pop();
if (_sparkAddress != address.value) {
setState(() {
_sparkAddress = address.value;
});
}
}
}
}
StreamSubscription<Address?>? _streamSub;
@override
void initState() {
walletId = widget.walletId;
coin = ref.read(walletsChangeNotifierProvider).getManager(walletId).coin;
coin = ref.read(pWalletCoin(walletId));
clipboard = widget.clipboard;
supportsSpark = ref.read(pWallets).getWallet(walletId) is SparkInterface;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final address = await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.currentReceivingAddress;
if (supportsSpark) {
_streamSub = ref
.read(mainDBProvider)
.isar
.addresses
.where()
.walletIdEqualTo(walletId)
.filter()
.typeEqualTo(AddressType.spark)
.sortByDerivationIndexDesc()
.findFirst()
.asStream()
.listen((event) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
receivingAddress = address;
_sparkAddress = event?.value;
});
}
});
});
}
super.initState();
}
@override
void dispose() {
_streamSub?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
ref.listen(
ref
.read(walletsChangeNotifierProvider)
.getManagerProvider(walletId)
.select((value) => value.currentReceivingAddress),
(previous, next) {
if (next is Future<String>) {
next.then((value) => setState(() => receivingAddress = value));
}
});
final ticker = widget.tokenContract?.symbol ?? coin.ticker;
if (supportsSpark) {
if (_showSparkAddress) {
_qrcodeContent = _sparkAddress;
} else {
_qrcodeContent = ref.watch(pWalletReceivingAddress(walletId));
}
} else {
_qrcodeContent = ref.watch(pWalletReceivingAddress(walletId));
}
return Background(
child: Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
@ -243,11 +318,166 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ConditionalParent(
condition: supportsSpark,
builder: (child) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
DropdownButtonHideUnderline(
child: DropdownButton2<bool>(
value: _showSparkAddress,
items: [
DropdownMenuItem(
value: true,
child: Text(
"Spark address",
style: STextStyles.desktopTextMedium(context),
),
),
DropdownMenuItem(
value: false,
child: Text(
"Transparent address",
style: STextStyles.desktopTextMedium(context),
),
),
],
onChanged: (value) {
if (value is bool && value != _showSparkAddress) {
setState(() {
_showSparkAddress = value;
});
}
},
isExpanded: true,
iconStyleData: IconStyleData(
icon: Padding(
padding: const EdgeInsets.only(right: 10),
child: SvgPicture.asset(
Assets.svg.chevronDown,
width: 12,
height: 6,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveSearchIconRight,
),
),
),
dropdownStyleData: DropdownStyleData(
offset: const Offset(0, -10),
elevation: 0,
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
menuItemStyleData: const MenuItemStyleData(
padding: EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
),
),
),
const SizedBox(
height: 12,
),
if (_showSparkAddress)
GestureDetector(
onTap: () {
clipboard.setData(
ClipboardData(text: _sparkAddress ?? "Error"),
);
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
);
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
width: 1,
),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: RoundedWhiteContainer(
child: Column(
children: [
Row(
children: [
Text(
"Your ${coin.ticker} SPARK address",
style:
STextStyles.itemSubtitle(context),
),
const Spacer(),
Row(
children: [
SvgPicture.asset(
Assets.svg.copy,
width: 15,
height: 15,
color: Theme.of(context)
.extension<StackColors>()!
.infoItemIcons,
),
const SizedBox(
width: 4,
),
Text(
"Copy",
style: STextStyles.link2(context),
),
],
),
],
),
const SizedBox(
height: 8,
),
Row(
children: [
Expanded(
child: Text(
_sparkAddress ?? "Error",
style: STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
),
],
),
],
),
),
),
),
if (!_showSparkAddress) child,
],
),
child: GestureDetector(
onTap: () {
HapticFeedback.lightImpact();
clipboard.setData(
ClipboardData(text: receivingAddress),
ClipboardData(
text:
ref.watch(pWalletReceivingAddress(walletId))),
);
showFloatingFlushBar(
type: FlushBarType.info,
@ -294,7 +524,8 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
children: [
Expanded(
child: Text(
receivingAddress,
ref.watch(
pWalletReceivingAddress(walletId)),
style: STextStyles.itemSubtitle12(context),
),
),
@ -304,25 +535,22 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
),
),
),
if (coin != Coin.epicCash &&
coin != Coin.ethereum &&
coin != Coin.banano &&
coin != Coin.nano &&
coin != Coin.stellar &&
coin != Coin.stellarTestnet &&
coin != Coin.tezos)
),
if (ref.watch(pWallets
.select((value) => value.getWallet(walletId)))
is MultiAddressInterface ||
supportsSpark)
const SizedBox(
height: 12,
),
if (coin != Coin.epicCash &&
coin != Coin.ethereum &&
coin != Coin.banano &&
coin != Coin.nano &&
coin != Coin.stellar &&
coin != Coin.stellarTestnet &&
coin != Coin.tezos)
if (ref.watch(pWallets
.select((value) => value.getWallet(walletId)))
is MultiAddressInterface ||
supportsSpark)
TextButton(
onPressed: generateNewAddress,
onPressed: supportsSpark && _showSparkAddress
? generateNewSparkAddress
: generateNewAddress,
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonStyle(context),
@ -346,7 +574,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
QrImageView(
data: AddressUtils.buildUriString(
coin,
receivingAddress,
_qrcodeContent ?? "",
{},
),
size: MediaQuery.of(context).size.width / 2,
@ -365,7 +593,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
RouteGenerator.useMaterialPageRoute,
builder: (_) => GenerateUriQrCodeView(
coin: coin,
receivingAddress: receivingAddress,
receivingAddress: _qrcodeContent ?? "",
),
settings: const RouteSettings(
name: GenerateUriQrCodeView.routeName,

View file

@ -13,22 +13,20 @@ import 'dart:io';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_libepiccash/lib.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/models/isar/models/transaction_note.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
import 'package:stackwallet/pages/token_view/token_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
@ -37,6 +35,11 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -53,8 +56,9 @@ import 'package:stackwallet/widgets/textfield_icon_button.dart';
class ConfirmTransactionView extends ConsumerStatefulWidget {
const ConfirmTransactionView({
Key? key,
required this.transactionInfo,
required this.txData,
required this.walletId,
required this.onSuccess,
this.routeOnSuccessName = WalletView.routeName,
this.isTradeTransaction = false,
this.isPaynymTransaction = false,
@ -65,7 +69,7 @@ class ConfirmTransactionView extends ConsumerStatefulWidget {
static const String routeName = "/confirmTransactionView";
final Map<String, dynamic> transactionInfo;
final TxData txData;
final String walletId;
final String routeOnSuccessName;
final bool isTradeTransaction;
@ -73,6 +77,7 @@ class ConfirmTransactionView extends ConsumerStatefulWidget {
final bool isPaynymNotificationTransaction;
final bool isTokenTx;
final VoidCallback? onSuccessInsteadOfRouteOnSuccess;
final VoidCallback onSuccess;
@override
ConsumerState<ConfirmTransactionView> createState() =>
@ -81,7 +86,6 @@ class ConfirmTransactionView extends ConsumerStatefulWidget {
class _ConfirmTransactionViewState
extends ConsumerState<ConfirmTransactionView> {
late final Map<String, dynamic> transactionInfo;
late final String walletId;
late final String routeOnSuccessName;
late final bool isDesktop;
@ -89,14 +93,12 @@ class _ConfirmTransactionViewState
late final FocusNode _noteFocusNode;
late final TextEditingController noteController;
late final FocusNode _onChainNoteFocusNode;
late final TextEditingController onChainNoteController;
Future<void> _attemptSend(BuildContext context) async {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
final wallet = ref.read(pWallets).getWallet(walletId);
final coin = wallet.info.coin;
final sendProgressController = ProgressAndSuccessController();
@ -107,7 +109,7 @@ class _ConfirmTransactionViewState
barrierDismissible: false,
builder: (context) {
return SendingTransactionDialog(
coin: manager.coin,
coin: coin,
controller: sendProgressController,
);
},
@ -120,58 +122,88 @@ class _ConfirmTransactionViewState
),
);
late String txid;
Future<String> txidFuture;
final List<String> txids = [];
Future<TxData> txDataFuture;
final note = noteController.text;
try {
if (widget.isTokenTx) {
txidFuture = ref
.read(tokenServiceProvider)!
.confirmSend(txData: transactionInfo);
txDataFuture =
ref.read(pCurrentTokenWallet)!.confirmSend(txData: widget.txData);
} else if (widget.isPaynymNotificationTransaction) {
txidFuture = (manager.wallet as PaynymWalletInterface)
.broadcastNotificationTx(preparedTx: transactionInfo);
txDataFuture = (wallet as PaynymInterface)
.broadcastNotificationTx(txData: widget.txData);
} else if (widget.isPaynymTransaction) {
txidFuture = manager.confirmSend(txData: transactionInfo);
txDataFuture = wallet.confirmSend(txData: widget.txData);
} else {
final coin = manager.coin;
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
txidFuture = (manager.wallet as FiroWallet)
.confirmSendPublic(txData: transactionInfo);
if (wallet is FiroWallet) {
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
if (widget.txData.sparkMints == null) {
txDataFuture = wallet.confirmSend(txData: widget.txData);
} else {
txDataFuture =
wallet.confirmSparkMintTransactions(txData: widget.txData);
}
break;
case FiroType.lelantus:
txDataFuture = wallet.confirmSendLelantus(txData: widget.txData);
break;
case FiroType.spark:
txDataFuture = wallet.confirmSendSpark(txData: widget.txData);
break;
}
} else {
if (coin == Coin.epicCash) {
transactionInfo["onChainNote"] = onChainNoteController.text;
txDataFuture = wallet.confirmSend(
txData: widget.txData.copyWith(
noteOnChain: onChainNoteController.text,
),
);
} else {
txDataFuture = wallet.confirmSend(txData: widget.txData);
}
txidFuture = manager.confirmSend(txData: transactionInfo);
}
}
final results = await Future.wait([
txidFuture,
txDataFuture,
time,
]);
sendProgressController.triggerSuccess?.call();
await Future<void>.delayed(const Duration(seconds: 5));
txid = results.first as String;
if (wallet is FiroWallet &&
(results.first as TxData).sparkMints != null) {
txids.addAll((results.first as TxData).sparkMints!.map((e) => e.txid!));
} else {
txids.add((results.first as TxData).txid!);
}
ref.refresh(desktopUseUTXOs);
// save note
await ref
.read(notesServiceChangeNotifierProvider(walletId))
.editOrAddNote(txid: txid, note: note);
for (final txid in txids) {
await ref.read(mainDBProvider).putTransactionNote(
TransactionNote(
walletId: walletId,
txid: txid,
value: note,
),
);
}
if (widget.isTokenTx) {
unawaited(ref.read(tokenServiceProvider)!.refresh());
unawaited(ref.read(pCurrentTokenWallet)!.refresh());
} else {
unawaited(manager.refresh());
unawaited(wallet.refresh());
}
widget.onSuccess.call();
// pop back to wallet
if (mounted) {
if (widget.onSuccessInsteadOfRouteOnSuccess == null) {
@ -222,10 +254,14 @@ class _ConfirmTransactionViewState
const SizedBox(
height: 24,
),
Text(
Flexible(
child: SingleChildScrollView(
child: SelectableText(
e.toString(),
style: STextStyles.smallMed14(context),
),
),
),
const SizedBox(
height: 56,
),
@ -274,16 +310,15 @@ class _ConfirmTransactionViewState
@override
void initState() {
isDesktop = Util.isDesktop;
transactionInfo = widget.transactionInfo;
walletId = widget.walletId;
routeOnSuccessName = widget.routeOnSuccessName;
_noteFocusNode = FocusNode();
noteController = TextEditingController();
noteController.text = transactionInfo["note"] as String? ?? "";
noteController.text = widget.txData.note ?? "";
_onChainNoteFocusNode = FocusNode();
onChainNoteController = TextEditingController();
onChainNoteController.text = transactionInfo["onChainNote"] as String? ?? "";
onChainNoteController.text = widget.txData.noteOnChain ?? "";
super.initState();
}
@ -300,20 +335,57 @@ class _ConfirmTransactionViewState
@override
Widget build(BuildContext context) {
final managerProvider = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManagerProvider(walletId)));
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).coin));
final coin = ref.watch(pWalletCoin(walletId));
final String unit;
if (widget.isTokenTx) {
unit = ref.watch(
tokenServiceProvider.select((value) => value!.tokenContract.symbol));
pCurrentTokenWallet.select((value) => value!.tokenContract.symbol));
} else {
unit = coin.ticker;
}
final Amount? fee;
final Amount amountWithoutChange;
final wallet = ref.watch(pWallets).getWallet(walletId);
if (wallet is FiroWallet) {
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
if (widget.txData.sparkMints != null) {
fee = widget.txData.sparkMints!
.map((e) => e.fee!)
.reduce((value, element) => value += element);
amountWithoutChange = widget.txData.sparkMints!
.map((e) => e.amountSpark!)
.reduce((value, element) => value += element);
} else {
fee = widget.txData.fee;
amountWithoutChange = widget.txData.amountWithoutChange!;
}
break;
case FiroType.lelantus:
fee = widget.txData.fee;
amountWithoutChange = widget.txData.amountWithoutChange!;
break;
case FiroType.spark:
fee = widget.txData.fee;
amountWithoutChange = (widget.txData.amountWithoutChange ??
Amount.zeroWith(
fractionDigits: wallet.cryptoCurrency.fractionDigits)) +
(widget.txData.amountSparkWithoutChange ??
Amount.zeroWith(
fractionDigits: wallet.cryptoCurrency.fractionDigits));
break;
}
} else {
fee = widget.txData.fee;
amountWithoutChange = widget.txData.amountWithoutChange!;
}
return ConditionalParent(
condition: !isDesktop,
builder: (child) => Background(
@ -385,7 +457,11 @@ class _ConfirmTransactionViewState
),
],
),
child,
Flexible(
child: SingleChildScrollView(
child: child,
),
),
],
),
child: Column(
@ -418,10 +494,9 @@ class _ConfirmTransactionViewState
),
Text(
widget.isPaynymTransaction
? (transactionInfo["paynymAccountLite"]
as PaynymAccountLite)
.nymName
: "${transactionInfo["address"] ?? "ERROR"}",
? widget.txData.paynymAccountLite!.nymName
: widget.txData.recipients?.first.address ??
widget.txData.sparkRecipients!.first.address,
style: STextStyles.itemSubtitle12(context),
),
],
@ -438,11 +513,11 @@ class _ConfirmTransactionViewState
"Amount",
style: STextStyles.smallMed12(context),
),
Text(
SelectableText(
ref.watch(pAmountFormatter(coin)).format(
transactionInfo["recipientAmt"] as Amount,
amountWithoutChange,
ethContract: ref
.watch(tokenServiceProvider)
.watch(pCurrentTokenWallet)
?.tokenContract,
),
style: STextStyles.itemSubtitle12(context),
@ -464,32 +539,19 @@ class _ConfirmTransactionViewState
"Transaction fee",
style: STextStyles.smallMed12(context),
),
Text(
ref.watch(pAmountFormatter(coin)).format(
(transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int)
.toAmountAsRaw(
fractionDigits: ref.watch(
managerProvider.select(
(value) => value.coin.decimals,
),
),
)),
),
SelectableText(
ref.watch(pAmountFormatter(coin)).format(fee!),
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
],
),
),
if (transactionInfo["fee"] is int &&
transactionInfo["vSize"] is int)
if (widget.txData.fee != null && widget.txData.vSize != null)
const SizedBox(
height: 12,
),
if (transactionInfo["fee"] is int &&
transactionInfo["vSize"] is int)
if (widget.txData.fee != null && widget.txData.vSize != null)
RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -501,20 +563,20 @@ class _ConfirmTransactionViewState
const SizedBox(
height: 4,
),
Text(
"~${(transactionInfo["fee"] / transactionInfo["vSize"]).toInt()}",
SelectableText(
"~${fee!.raw.toInt() ~/ widget.txData.vSize!}",
style: STextStyles.itemSubtitle12(context),
),
],
),
),
if (coin == Coin.epicCash &&
(transactionInfo["onChainNote"] as String).isNotEmpty)
widget.txData.noteOnChain!.isNotEmpty)
const SizedBox(
height: 12,
),
if (coin == Coin.epicCash &&
(transactionInfo["onChainNote"] as String).isNotEmpty)
widget.txData.noteOnChain!.isNotEmpty)
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
@ -526,18 +588,18 @@ class _ConfirmTransactionViewState
const SizedBox(
height: 4,
),
Text(
transactionInfo["onChainNote"] as String,
SelectableText(
widget.txData.noteOnChain!,
style: STextStyles.itemSubtitle12(context),
),
],
),
),
if ((transactionInfo["note"] as String).isNotEmpty)
if (widget.txData.note!.isNotEmpty)
const SizedBox(
height: 12,
),
if ((transactionInfo["note"] as String).isNotEmpty)
if (widget.txData.note!.isNotEmpty)
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
@ -549,8 +611,8 @@ class _ConfirmTransactionViewState
const SizedBox(
height: 4,
),
Text(
transactionInfo["note"] as String,
SelectableText(
widget.txData.note!,
style: STextStyles.itemSubtitle12(context),
),
],
@ -633,13 +695,6 @@ class _ConfirmTransactionViewState
),
Builder(
builder: (context) {
final coin = ref.watch(
managerProvider.select(
(value) => value.coin,
),
);
final amount =
transactionInfo["recipientAmt"] as Amount;
final externalCalls = ref.watch(
prefsChangeNotifierProvider.select(
(value) => value.externalCalls));
@ -652,7 +707,7 @@ class _ConfirmTransactionViewState
priceAnd24hChangeNotifierProvider)
.getTokenPrice(
ref
.read(tokenServiceProvider)!
.read(pCurrentTokenWallet)!
.tokenContract
.address,
)
@ -663,7 +718,8 @@ class _ConfirmTransactionViewState
.getPrice(coin)
.item1;
if (price > Decimal.zero) {
fiatAmount = (amount.decimal * price)
fiatAmount =
(amountWithoutChange.decimal * price)
.toAmount(fractionDigits: 2)
.fiatString(
locale: ref
@ -676,11 +732,11 @@ class _ConfirmTransactionViewState
return Row(
children: [
Text(
SelectableText(
ref.watch(pAmountFormatter(coin)).format(
amount,
amountWithoutChange,
ethContract: ref
.read(tokenServiceProvider)
.read(pCurrentTokenWallet)
?.tokenContract),
style: STextStyles
.desktopTextExtraExtraSmall(
@ -699,7 +755,7 @@ class _ConfirmTransactionViewState
context),
),
if (externalCalls)
Text(
SelectableText(
"~$fiatAmount ${ref.watch(prefsChangeNotifierProvider.select(
(value) => value.currency,
))}",
@ -736,12 +792,13 @@ class _ConfirmTransactionViewState
const SizedBox(
height: 2,
),
Text(
SelectableText(
// TODO: [prio=med] spark transaction specifics - better handling
widget.isPaynymTransaction
? (transactionInfo["paynymAccountLite"]
as PaynymAccountLite)
.nymName
: "${transactionInfo["address"] ?? "ERROR"}",
? widget.txData.paynymAccountLite!.nymName
: widget.txData.recipients?.first.address ??
widget.txData.sparkRecipients!.first
.address,
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
@ -775,35 +832,15 @@ class _ConfirmTransactionViewState
const SizedBox(
height: 2,
),
Builder(
builder: (context) {
final coin = ref
.watch(walletsChangeNotifierProvider
.select((value) =>
value.getManager(walletId)))
.coin;
final fee = transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int)
.toAmountAsRaw(
fractionDigits: coin.decimals,
);
return Text(
ref
.watch(pAmountFormatter(coin))
.format(fee),
style:
STextStyles.desktopTextExtraExtraSmall(
SelectableText(
ref.watch(pAmountFormatter(coin)).format(fee!),
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
);
},
),
],
),
@ -885,8 +922,7 @@ class _ConfirmTransactionViewState
).copyWith(
suffixIcon: onChainNoteController.text.isNotEmpty
? Padding(
padding:
const EdgeInsets.only(right: 0),
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
@ -910,8 +946,9 @@ class _ConfirmTransactionViewState
const SizedBox(
height: 12,
),
Text(
(coin == Coin.epicCash) ? "Local Note (optional)"
SelectableText(
(coin == Coin.epicCash)
? "Local Note (optional)"
: "Note (optional)",
style:
STextStyles.desktopTextExtraSmall(context).copyWith(
@ -1008,31 +1045,16 @@ class _ConfirmTransactionViewState
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
child: Builder(
builder: (context) {
final coin = ref
.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)))
.coin;
final fee = transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int).toAmountAsRaw(
fractionDigits: coin.decimals,
);
return Text(
ref.watch(pAmountFormatter(coin)).format(fee),
child: SelectableText(
ref.watch(pAmountFormatter(coin)).format(fee!),
style: STextStyles.itemSubtitle(context),
);
},
),
),
),
if (isDesktop &&
!widget.isPaynymTransaction &&
transactionInfo["fee"] is int &&
transactionInfo["vSize"] is int)
widget.txData.fee != null &&
widget.txData.vSize != null)
Padding(
padding: const EdgeInsets.only(
left: 32,
@ -1044,8 +1066,8 @@ class _ConfirmTransactionViewState
),
if (isDesktop &&
!widget.isPaynymTransaction &&
transactionInfo["fee"] is int &&
transactionInfo["vSize"] is int)
widget.txData.fee != null &&
widget.txData.vSize != null)
Padding(
padding: const EdgeInsets.only(
top: 10,
@ -1060,8 +1082,8 @@ class _ConfirmTransactionViewState
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
child: Text(
"~${(transactionInfo["fee"] / transactionInfo["vSize"]).toInt()}",
child: SelectableText(
"~${fee!.raw.toInt() ~/ widget.txData.vSize!}",
style: STextStyles.itemSubtitle(context),
),
),
@ -1105,21 +1127,10 @@ class _ConfirmTransactionViewState
.textConfirmTotalAmount,
),
),
Builder(builder: (context) {
final coin = ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(walletId).coin));
final fee = transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int)
.toAmountAsRaw(fractionDigits: coin.decimals);
final amount =
transactionInfo["recipientAmt"] as Amount;
return Text(
SelectableText(
ref
.watch(pAmountFormatter(coin))
.format(amount + fee),
.format(amountWithoutChange + fee!),
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
@ -1133,8 +1144,7 @@ class _ConfirmTransactionViewState
.textConfirmTotalAmount,
),
textAlign: TextAlign.right,
);
}),
),
],
),
),
@ -1154,11 +1164,6 @@ class _ConfirmTransactionViewState
onPressed: () async {
final dynamic unlocked;
final coin = ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.coin;
if (isDesktop) {
unlocked = await showDialog<bool?>(
context: context,
@ -1168,9 +1173,9 @@ class _ConfirmTransactionViewState
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
const Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
children: [
DesktopDialogCloseButton(),
],
),

File diff suppressed because it is too large Load diff

View file

@ -12,11 +12,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
class FiroBalanceSelectionSheet extends ConsumerStatefulWidget {
const FiroBalanceSelectionSheet({
@ -35,13 +35,6 @@ class _FiroBalanceSelectionSheetState
extends ConsumerState<FiroBalanceSelectionSheet> {
late final String walletId;
final stringsToLoopThrough = [
"Loading balance",
"Loading balance.",
"Loading balance..",
"Loading balance...",
];
@override
void initState() {
walletId = widget.walletId;
@ -52,9 +45,11 @@ class _FiroBalanceSelectionSheetState
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)));
final firoWallet = manager.wallet as FiroWallet;
final wallet =
ref.watch(pWallets.select((value) => value.getWallet(walletId)));
final firoWallet = wallet as FiroWallet;
final coin = wallet.info.coin;
return Container(
decoration: BoxDecoration(
@ -106,9 +101,9 @@ class _FiroBalanceSelectionSheetState
onTap: () {
final state =
ref.read(publicPrivateBalanceStateProvider.state).state;
if (state != "Private") {
if (state != FiroType.spark) {
ref.read(publicPrivateBalanceStateProvider.state).state =
"Private";
FiroType.spark;
}
Navigator.of(context).pop();
},
@ -127,7 +122,7 @@ class _FiroBalanceSelectionSheetState
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: "Private",
value: FiroType.spark,
groupValue: ref
.watch(
publicPrivateBalanceStateProvider.state)
@ -136,7 +131,7 @@ class _FiroBalanceSelectionSheetState
ref
.read(publicPrivateBalanceStateProvider
.state)
.state = "Private";
.state = FiroType.spark;
Navigator.of(context).pop();
},
@ -154,7 +149,7 @@ class _FiroBalanceSelectionSheetState
// Row(
// children: [
Text(
"Private balance",
"Spark balance",
style: STextStyles.titleBold12(context),
textAlign: TextAlign.left,
),
@ -162,10 +157,9 @@ class _FiroBalanceSelectionSheetState
width: 2,
),
Text(
ref
.watch(pAmountFormatter(manager.coin))
.format(
firoWallet.availablePrivateBalance(),
ref.watch(pAmountFormatter(coin)).format(
firoWallet
.info.cachedBalanceTertiary.spendable,
),
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
@ -186,9 +180,88 @@ class _FiroBalanceSelectionSheetState
onTap: () {
final state =
ref.read(publicPrivateBalanceStateProvider.state).state;
if (state != "Public") {
if (state != FiroType.lelantus) {
ref.read(publicPrivateBalanceStateProvider.state).state =
"Public";
FiroType.lelantus;
}
Navigator.of(context).pop();
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: FiroType.lelantus,
groupValue: ref
.watch(
publicPrivateBalanceStateProvider.state)
.state,
onChanged: (x) {
ref
.read(publicPrivateBalanceStateProvider
.state)
.state = FiroType.lelantus;
Navigator.of(context).pop();
},
),
),
],
),
const SizedBox(
width: 12,
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row(
// children: [
Text(
"Lelantus balance",
style: STextStyles.titleBold12(context),
textAlign: TextAlign.left,
),
const SizedBox(
width: 2,
),
Text(
ref.watch(pAmountFormatter(coin)).format(
firoWallet.info.cachedBalanceSecondary
.spendable,
),
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
),
],
),
// ],
// ),
)
],
),
),
),
const SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
final state =
ref.read(publicPrivateBalanceStateProvider.state).state;
if (state != FiroType.public) {
ref.read(publicPrivateBalanceStateProvider.state).state =
FiroType.public;
}
Navigator.of(context).pop();
},
@ -206,7 +279,7 @@ class _FiroBalanceSelectionSheetState
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: "Public",
value: FiroType.public,
groupValue: ref
.watch(
publicPrivateBalanceStateProvider.state)
@ -215,7 +288,7 @@ class _FiroBalanceSelectionSheetState
ref
.read(publicPrivateBalanceStateProvider
.state)
.state = "Public";
.state = FiroType.public;
Navigator.of(context).pop();
},
),
@ -240,10 +313,8 @@ class _FiroBalanceSelectionSheetState
width: 2,
),
Text(
ref
.watch(pAmountFormatter(manager.coin))
.format(
firoWallet.availablePublicBalance(),
ref.watch(pAmountFormatter(coin)).format(
firoWallet.info.cachedBalance.spendable,
),
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,

View file

@ -12,11 +12,9 @@ import 'package:cw_core/monero_transaction_priority.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/pages/token_view/token_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
@ -25,6 +23,9 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
import 'package:stackwallet/widgets/animated_text.dart';
final feeSheetSessionCacheProvider =
@ -83,26 +84,34 @@ class _TransactionFeeSelectionSheetState
case FeeRateType.fast:
if (ref.read(feeSheetSessionCacheProvider).fast[amount] == null) {
if (widget.isToken == false) {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
final wallet = ref.read(pWallets).getWallet(walletId);
if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor(
final fee = await wallet.estimateFeeFor(
amount, MoneroTransactionPriority.fast.raw!);
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).fast[amount] =
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
final Amount fee;
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.spark:
fee =
await (wallet as FiroWallet).estimateFeeForSpark(amount);
case FiroType.lelantus:
fee = await (wallet as FiroWallet)
.estimateFeeForLelantus(amount);
case FiroType.public:
fee = await (wallet as FiroWallet)
.estimateFeeFor(amount, feeRate);
}
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
} else {
ref.read(feeSheetSessionCacheProvider).fast[amount] =
await manager.estimateFeeFor(amount, feeRate);
await wallet.estimateFeeFor(amount, feeRate);
}
} else {
final tokenWallet = ref.read(tokenServiceProvider)!;
final fee = tokenWallet.estimateFeeFor(feeRate);
final tokenWallet = ref.read(pCurrentTokenWallet)!;
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
}
}
@ -111,25 +120,32 @@ class _TransactionFeeSelectionSheetState
case FeeRateType.average:
if (ref.read(feeSheetSessionCacheProvider).average[amount] == null) {
if (widget.isToken == false) {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
final wallet = ref.read(pWallets).getWallet(walletId);
if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor(
final fee = await wallet.estimateFeeFor(
amount, MoneroTransactionPriority.regular.raw!);
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).average[amount] =
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
final Amount fee;
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.spark:
fee =
await (wallet as FiroWallet).estimateFeeForSpark(amount);
case FiroType.lelantus:
fee = await (wallet as FiroWallet)
.estimateFeeForLelantus(amount);
case FiroType.public:
fee = await (wallet as FiroWallet)
.estimateFeeFor(amount, feeRate);
}
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
} else {
ref.read(feeSheetSessionCacheProvider).average[amount] =
await manager.estimateFeeFor(amount, feeRate);
await wallet.estimateFeeFor(amount, feeRate);
}
} else {
final tokenWallet = ref.read(tokenServiceProvider)!;
final fee = tokenWallet.estimateFeeFor(feeRate);
final tokenWallet = ref.read(pCurrentTokenWallet)!;
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
}
}
@ -138,25 +154,32 @@ class _TransactionFeeSelectionSheetState
case FeeRateType.slow:
if (ref.read(feeSheetSessionCacheProvider).slow[amount] == null) {
if (widget.isToken == false) {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
final wallet = ref.read(pWallets).getWallet(walletId);
if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor(
final fee = await wallet.estimateFeeFor(
amount, MoneroTransactionPriority.slow.raw!);
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).slow[amount] =
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
final Amount fee;
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.spark:
fee =
await (wallet as FiroWallet).estimateFeeForSpark(amount);
case FiroType.lelantus:
fee = await (wallet as FiroWallet)
.estimateFeeForLelantus(amount);
case FiroType.public:
fee = await (wallet as FiroWallet)
.estimateFeeFor(amount, feeRate);
}
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
} else {
ref.read(feeSheetSessionCacheProvider).slow[amount] =
await manager.estimateFeeFor(amount, feeRate);
await wallet.estimateFeeFor(amount, feeRate);
}
} else {
final tokenWallet = ref.read(tokenServiceProvider)!;
final fee = tokenWallet.estimateFeeFor(feeRate);
final tokenWallet = ref.read(pCurrentTokenWallet)!;
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
}
}
@ -203,8 +226,9 @@ class _TransactionFeeSelectionSheetState
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)));
final wallet = ref.watch(pWallets).getWallet(walletId);
final coin = ref.watch(pWalletCoin(walletId));
return Container(
decoration: BoxDecoration(
@ -243,8 +267,8 @@ class _TransactionFeeSelectionSheetState
),
FutureBuilder(
future: widget.isToken
? ref.read(tokenServiceProvider)!.fees
: manager.fees,
? ref.read(pCurrentTokenWallet)!.fees
: wallet.fees,
builder: (context, AsyncSnapshot<FeeObject> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
@ -270,7 +294,8 @@ class _TransactionFeeSelectionSheetState
ref.read(feeRateTypeStateProvider.state).state =
FeeRateType.fast;
}
String? fee = getAmount(FeeRateType.fast, manager.coin);
String? fee =
getAmount(FeeRateType.fast, wallet.info.coin);
if (fee != null) {
widget.updateChosen(fee);
}
@ -333,7 +358,7 @@ class _TransactionFeeSelectionSheetState
if (feeObject != null)
FutureBuilder(
future: feeFor(
coin: manager.coin,
coin: coin,
feeRateType: FeeRateType.fast,
feeRate: feeObject!.fast,
amount: amount,
@ -346,7 +371,7 @@ class _TransactionFeeSelectionSheetState
return Text(
"(~${ref.watch(
pAmountFormatter(
manager.coin,
coin,
),
).format(
snapshot.data!,
@ -373,18 +398,18 @@ class _TransactionFeeSelectionSheetState
height: 2,
),
if (feeObject == null &&
manager.coin != Coin.ethereum)
coin != Coin.ethereum)
AnimatedText(
stringsToLoopThrough:
stringsToLoopThrough,
style: STextStyles.itemSubtitle(context),
),
if (feeObject != null &&
manager.coin != Coin.ethereum)
coin != Coin.ethereum)
Text(
estimatedTimeToBeIncludedInNextBlock(
Constants.targetBlockTimeInSeconds(
manager.coin),
coin),
feeObject!.numberOfBlocksFast,
),
style: STextStyles.itemSubtitle(context),
@ -408,8 +433,7 @@ class _TransactionFeeSelectionSheetState
ref.read(feeRateTypeStateProvider.state).state =
FeeRateType.average;
}
String? fee =
getAmount(FeeRateType.average, manager.coin);
String? fee = getAmount(FeeRateType.average, coin);
if (fee != null) {
widget.updateChosen(fee);
}
@ -470,7 +494,7 @@ class _TransactionFeeSelectionSheetState
if (feeObject != null)
FutureBuilder(
future: feeFor(
coin: manager.coin,
coin: coin,
feeRateType: FeeRateType.average,
feeRate: feeObject!.medium,
amount: amount,
@ -483,7 +507,7 @@ class _TransactionFeeSelectionSheetState
return Text(
"(~${ref.watch(
pAmountFormatter(
manager.coin,
coin,
),
).format(
snapshot.data!,
@ -510,18 +534,18 @@ class _TransactionFeeSelectionSheetState
height: 2,
),
if (feeObject == null &&
manager.coin != Coin.ethereum)
coin != Coin.ethereum)
AnimatedText(
stringsToLoopThrough:
stringsToLoopThrough,
style: STextStyles.itemSubtitle(context),
),
if (feeObject != null &&
manager.coin != Coin.ethereum)
coin != Coin.ethereum)
Text(
estimatedTimeToBeIncludedInNextBlock(
Constants.targetBlockTimeInSeconds(
manager.coin),
coin),
feeObject!.numberOfBlocksAverage,
),
style: STextStyles.itemSubtitle(context),
@ -545,7 +569,7 @@ class _TransactionFeeSelectionSheetState
ref.read(feeRateTypeStateProvider.state).state =
FeeRateType.slow;
}
String? fee = getAmount(FeeRateType.slow, manager.coin);
String? fee = getAmount(FeeRateType.slow, coin);
if (fee != null) {
widget.updateChosen(fee);
}
@ -606,7 +630,7 @@ class _TransactionFeeSelectionSheetState
if (feeObject != null)
FutureBuilder(
future: feeFor(
coin: manager.coin,
coin: coin,
feeRateType: FeeRateType.slow,
feeRate: feeObject!.slow,
amount: amount,
@ -619,7 +643,7 @@ class _TransactionFeeSelectionSheetState
return Text(
"(~${ref.watch(
pAmountFormatter(
manager.coin,
coin,
),
).format(
snapshot.data!,
@ -646,18 +670,18 @@ class _TransactionFeeSelectionSheetState
height: 2,
),
if (feeObject == null &&
manager.coin != Coin.ethereum)
coin != Coin.ethereum)
AnimatedText(
stringsToLoopThrough:
stringsToLoopThrough,
style: STextStyles.itemSubtitle(context),
),
if (feeObject != null &&
manager.coin != Coin.ethereum)
coin != Coin.ethereum)
Text(
estimatedTimeToBeIncludedInNextBlock(
Constants.targetBlockTimeInSeconds(
manager.coin),
coin),
feeObject!.numberOfBlocksSlow,
),
style: STextStyles.itemSubtitle(context),
@ -673,7 +697,7 @@ class _TransactionFeeSelectionSheetState
const SizedBox(
height: 24,
),
if (manager.coin.isElectrumXCoin)
if (coin.isElectrumXCoin)
GestureDetector(
onTap: () {
final state =
@ -742,7 +766,7 @@ class _TransactionFeeSelectionSheetState
),
),
),
if (manager.coin.isElectrumXCoin)
if (coin.isElectrumXCoin)
const SizedBox(
height: 24,
),

View file

@ -21,12 +21,10 @@ import 'package:stackwallet/pages/address_book_views/address_book_view.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
import 'package:stackwallet/pages/token_view/token_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
import 'package:stackwallet/providers/ui/preview_tx_button_state_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
@ -42,6 +40,10 @@ import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
import 'package:stackwallet/wallets/isar/providers/eth/token_balance_provider.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -193,8 +195,9 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
// now check for non standard encoded basic address
} else if (ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.read(pWallets)
.getWallet(walletId)
.cryptoCurrency
.validateAddress(qrResult.rawContent)) {
_address = qrResult.rawContent.trim();
sendToController.text = _address ?? "";
@ -325,11 +328,16 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
});
}
String? _updateInvalidAddressText(String address, Manager manager) {
String? _updateInvalidAddressText(String address) {
if (_data != null && _data!.contactLabel == address) {
return null;
}
if (address.isNotEmpty && !manager.validateAddress(address)) {
if (address.isNotEmpty &&
!ref
.read(pWallets)
.getWallet(walletId)
.cryptoCurrency
.validateAddress(address)) {
return "Invalid address";
}
return null;
@ -337,15 +345,16 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
void _updatePreviewButtonState(String? address, Amount? amount) {
final isValidAddress = ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.read(pWallets)
.getWallet(walletId)
.cryptoCurrency
.validateAddress(address ?? "");
ref.read(previewTxButtonStateProvider.state).state =
ref.read(previewTokenTxButtonStateProvider.state).state =
(isValidAddress && amount != null && amount > Amount.zero);
}
Future<String> calculateFees() async {
final wallet = ref.read(tokenServiceProvider)!;
final wallet = ref.read(pCurrentTokenWallet)!;
final feeObject = await wallet.fees;
late final int feeRate;
@ -364,7 +373,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
feeRate = -1;
}
final Amount fee = wallet.estimateFeeFor(feeRate);
final Amount fee = await wallet.estimateFeeFor(Amount.zero, feeRate);
cachedFees = ref.read(pAmountFormatter(coin)).format(
fee,
withUnitName: true,
@ -380,9 +389,8 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
await Future<void>.delayed(
const Duration(milliseconds: 100),
);
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
final tokenWallet = ref.read(tokenServiceProvider)!;
final wallet = ref.read(pWallets).getWallet(walletId);
final tokenWallet = ref.read(pCurrentTokenWallet)!;
final Amount amount = _amountToSend!;
@ -448,7 +456,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
barrierDismissible: false,
builder: (context) {
return BuildingTransactionDialog(
coin: manager.coin,
coin: wallet.info.coin,
onCancel: () {
wasCancelled = true;
@ -466,15 +474,21 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
),
);
Map<String, dynamic> txData;
Future<Map<String, dynamic>> txDataFuture;
TxData txData;
Future<TxData> txDataFuture;
txDataFuture = tokenWallet.prepareSend(
txData: TxData(
recipients: [
(
address: _address!,
amount: amount,
args: {
"feeRate": ref.read(feeRateTypeStateProvider),
},
isChange: false,
)
],
feeRateType: ref.read(feeRateTypeStateProvider),
note: noteController.text,
),
);
final results = await Future.wait([
@ -482,22 +496,20 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
time,
]);
txData = results.first as Map<String, dynamic>;
txData = results.first as TxData;
if (!wasCancelled && mounted) {
// pop building dialog
Navigator.of(context).pop();
txData["note"] = noteController.text;
txData["address"] = _address;
unawaited(Navigator.of(context).push(
RouteGenerator.getRoute(
shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
builder: (_) => ConfirmTransactionView(
transactionInfo: txData,
txData: txData,
walletId: walletId,
isTokenTx: true,
onSuccess: clearSendForm,
),
settings: const RouteSettings(
name: ConfirmTransactionView.routeName,
@ -510,7 +522,8 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
// pop building dialog
Navigator.of(context).pop();
unawaited(showDialog<dynamic>(
unawaited(
showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
@ -535,11 +548,25 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
),
);
},
));
),
);
}
}
}
void clearSendForm() {
sendToController.text = "";
cryptoAmountController.text = "";
baseAmountController.text = "";
noteController.text = "";
feeController.text = "";
_address = "";
_addressToggleFlag = false;
if (mounted) {
setState(() {});
}
}
@override
void initState() {
ref.refresh(feeSheetSessionCacheProvider);
@ -598,8 +625,6 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final provider = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManagerProvider(walletId)));
final String locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale));
@ -667,8 +692,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
CrossAxisAlignment.start,
children: [
Text(
ref.watch(provider.select(
(value) => value.walletName)),
ref.watch(pWalletName(walletId)),
style: STextStyles.titleBold12(context)
.copyWith(fontSize: 14),
overflow: TextOverflow.ellipsis,
@ -688,8 +712,11 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
.watch(pAmountFormatter(coin))
.format(
ref
.read(tokenServiceProvider)!
.balance
.read(pTokenBalance((
walletId: widget.walletId,
contractAddress:
tokenContract.address,
)))
.spendable,
ethContract: tokenContract,
withUnitName: false,
@ -706,18 +733,16 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
ref
.watch(pAmountFormatter(coin))
.format(
ref.watch(
tokenServiceProvider.select(
(value) => value!
.balance.spendable,
),
),
ethContract: ref.watch(
tokenServiceProvider.select(
(value) =>
value!.tokenContract,
),
),
ref
.watch(pTokenBalance((
walletId:
widget.walletId,
contractAddress:
tokenContract
.address,
)))
.spendable,
ethContract: tokenContract,
),
style:
STextStyles.titleBold12(context)
@ -727,7 +752,13 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
textAlign: TextAlign.right,
),
Text(
"${(ref.watch(tokenServiceProvider.select((value) => value!.balance.spendable.decimal)) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getTokenPrice(tokenContract.address).item1))).toAmount(
"${(ref.watch(pTokenBalance((
walletId:
widget.walletId,
contractAddress:
tokenContract
.address,
))).spendable.decimal * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getTokenPrice(tokenContract.address).item1))).toAmount(
fractionDigits: 2,
).fiatString(
locale: locale,
@ -860,9 +891,6 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
builder: (_) {
final error = _updateInvalidAddressText(
_address ?? "",
ref
.read(walletsChangeNotifierProvider)
.getManager(walletId),
);
if (error == null || error.isEmpty) {
@ -1224,12 +1252,14 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
),
TextButton(
onPressed: ref
.watch(previewTxButtonStateProvider.state)
.watch(
previewTokenTxButtonStateProvider.state)
.state
? _previewTransaction
: null,
style: ref
.watch(previewTxButtonStateProvider.state)
.watch(
previewTokenTxButtonStateProvider.state)
.state
? Theme.of(context)
.extension<StackColors>()!

View file

@ -525,6 +525,32 @@ class AboutView extends ConsumerWidget {
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Tezos functionality",
style: STextStyles.titleBold12(context),
),
const SizedBox(
height: 4,
),
CustomTextButton(
text: "Powered by TzKT API",
onTap: () {
launchUrl(
Uri.parse("https://tzkt.io"),
mode: LaunchMode.externalApplication,
);
},
),
],
),
),
const SizedBox(
height: 12,
),
const Spacer(),
RichText(
textAlign: TextAlign.center,

View file

@ -9,27 +9,18 @@
*/
import 'dart:async';
import 'dart:typed_data';
import 'package:bitbox/bitbox.dart' as bb;
import 'package:bitcoindart/bitcoindart.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/global/debug_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart';
import 'package:stackwallet/services/mixins/electrum_x_parsing.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.dart';
@ -224,98 +215,7 @@ class HiddenSettings extends StatelessWidget {
),
);
}),
// const SizedBox(
// height: 12,
// ),
// Consumer(builder: (_, ref, __) {
// return GestureDetector(
// onTap: () async {
// final x =
// await MajesticBankAPI.instance.getRates();
// print(x);
// },
// child: RoundedWhiteContainer(
// child: Text(
// "Click me",
// style: STextStyles.button(context).copyWith(
// color: Theme.of(context)
// .extension<StackColors>()!
// .accentColorDark),
// ),
// ),
// );
// }),
// const SizedBox(
// height: 12,
// ),
// Consumer(builder: (_, ref, __) {
// return GestureDetector(
// onTap: () async {
// ref
// .read(priceAnd24hChangeNotifierProvider)
// .tokenContractAddressesToCheck
// .add(
// "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
// ref
// .read(priceAnd24hChangeNotifierProvider)
// .tokenContractAddressesToCheck
// .add(
// "0xdAC17F958D2ee523a2206206994597C13D831ec7");
// await ref
// .read(priceAnd24hChangeNotifierProvider)
// .updatePrice();
//
// final x = ref
// .read(priceAnd24hChangeNotifierProvider)
// .getTokenPrice(
// "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
//
// print(
// "PRICE 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48: $x");
// },
// child: RoundedWhiteContainer(
// child: Text(
// "Click me",
// style: STextStyles.button(context).copyWith(
// color: Theme.of(context)
// .extension<StackColors>()!
// .accentColorDark),
// ),
// ),
// );
// }),
// const SizedBox(
// height: 12,
// ),
// Consumer(builder: (_, ref, __) {
// return GestureDetector(
// onTap: () async {
// // final erc20 = Erc20ContractInfo(
// // contractAddress: 'some con',
// // name: "loonamsn",
// // symbol: "DD",
// // decimals: 19,
// // );
// //
// // final json = erc20.toJson();
// //
// // print(json);
// //
// // final ee = EthContractInfo.fromJson(json);
// //
// // print(ee);
// },
// child: RoundedWhiteContainer(
// child: Text(
// "Click me",
// style: STextStyles.button(context).copyWith(
// color: Theme.of(context)
// .extension<StackColors>()!
// .accentColorDark),
// ),
// ),
// );
// }),
const SizedBox(
height: 12,
),
@ -352,82 +252,15 @@ class HiddenSettings extends StatelessWidget {
}
},
),
const SizedBox(
height: 12,
),
Consumer(
builder: (_, ref, __) {
return GestureDetector(
onTap: () async {
try {
final p = TT();
final n = ref
.read(nodeServiceChangeNotifierProvider)
.getPrimaryNodeFor(
coin: Coin.bitcoincash)!;
final e = ElectrumX.from(
node: ElectrumXNode(
address: n.host,
port: n.port,
name: n.name,
id: n.id,
useSSL: n.useSSL,
),
prefs:
ref.read(prefsChangeNotifierProvider),
failovers: [],
);
final ce =
CachedElectrumX(electrumXClient: e);
final txids = [
"", // cashTokenTxid
"6a0444358bc41913c5b04a8dc06896053184b3641bc62502d18f954865b6ce1e", // normalTxid
"67f13c375f9be897036cac77b7900dc74312c4ba6fe22f419f5cb21d4151678c", // fusionTxid
"c0ac3f88b238a023d2a87226dc90c3b0f9abc3eeb227e2730087b0b95ee5b3f9", // slpTokenSendTxid
"7a427a156fe70f83d3ccdd17e75804cc0df8c95c64ce04d256b3851385002a0b", // slpTokenGenesisTxid
];
// final json =
// await e.getTransaction(txHash: txids[1]);
// await p.parseBchTx(json, "NORMAL TXID:");
//
// final json2 =
// await e.getTransaction(txHash: txids[2]);
// await p.parseBchTx(json2, "FUSION TXID:");
//
// // print("CASH TOKEN TXID:");
// // final json3 =
// // await e.getTransaction(txHash: txids[2]);
// // await p.parseBchTx(json3);
//
await p.getTransaction(
txids[3],
Coin.bitcoincash,
"lol",
ce,
"SLP TOKEN SEND TXID:");
await p.getTransaction(
"009d31380d2dbfb5c91500c861d55b531a8b762b0abb19353db884548dbac8b6",
Coin.bitcoincash,
"lol",
ce,
"COINBASE TXID:");
// final json5 =
// await e.getTransaction(txHash: txids[4]);
// await p.parseBchTx(
// json5, "SLP TOKEN GENESIS TXID:");
} catch (e, s) {
print("$e\n$s");
}
},
child: RoundedWhiteContainer(
child: Text(
"Parse BCH tx test",
"Do nothing",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
@ -437,125 +270,6 @@ class HiddenSettings extends StatelessWidget {
);
},
),
const SizedBox(
height: 12,
),
Consumer(
builder: (_, ref, __) {
return GestureDetector(
onTap: () async {
try {
final p = TT();
final n = ref
.read(nodeServiceChangeNotifierProvider)
.getPrimaryNodeFor(
coin: Coin.bitcoincash)!;
final e = ElectrumX.from(
node: ElectrumXNode(
address: n.host,
port: n.port,
name: n.name,
id: n.id,
useSSL: n.useSSL,
),
prefs:
ref.read(prefsChangeNotifierProvider),
failovers: [],
);
final address =
"qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwyztz2p80d";
List<int> _base32Decode(String string) {
final data = Uint8List(string.length);
for (int i = 0; i < string.length; i++) {
final value = string[i];
if (!_CHARSET_INVERSE_INDEX
.containsKey(value))
throw FormatException(
"Invalid character '$value'");
data[i] =
_CHARSET_INVERSE_INDEX[string[i]]!;
}
return data.sublist(1);
}
final dec = _base32Decode(address);
final pd = PaymentData(
pubkey: Uint8List.fromList(dec));
final p2pkh =
P2PKH(data: pd, network: bitcoincash);
// final addr = p2pkh.data.address!;
final addr = bb.Address.toLegacyAddress(
"bitcoincash:qp352c2skpdxwzzd090mec3v37au5dmfwgwfw686sz",
);
final scripthash =
AddressUtils.convertToScriptHash(
addr, bitcoincash);
final utxos =
await e.getUTXOs(scripthash: scripthash);
Util.printJson(utxos, "UTXOS for $address");
final hist = await e.getTransaction(
txHash: utxos.first["tx_hash"] as String,
);
Util.printJson(hist, "HISTORY for $address");
} catch (e, s) {
print("$e\n$s");
}
},
child: RoundedWhiteContainer(
child: Text(
"UTXOs",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
),
);
},
),
// const SizedBox(
// height: 12,
// ),
// GestureDetector(
// onTap: () async {
// showDialog<void>(
// context: context,
// builder: (_) {
// return StackDialogBase(
// child: SizedBox(
// width: 300,
// child: Lottie.asset(
// Assets.lottie.plain(Coin.bitcoincash),
// ),
// ),
// );
// },
// );
// },
// child: RoundedWhiteContainer(
// child: Text(
// "Lottie test",
// style: STextStyles.button(context).copyWith(
// color: Theme.of(context)
// .extension<StackColors>()!
// .accentColorDark),
// ),
// ),
// ),
],
),
),
@ -568,38 +282,3 @@ class HiddenSettings extends StatelessWidget {
);
}
}
const _CHARSET_INVERSE_INDEX = {
'q': 0,
'p': 1,
'z': 2,
'r': 3,
'y': 4,
'9': 5,
'x': 6,
'8': 7,
'g': 8,
'f': 9,
'2': 10,
't': 11,
'v': 12,
'd': 13,
'w': 14,
'0': 15,
's': 16,
'3': 17,
'j': 18,
'n': 19,
'5': 20,
'4': 21,
'k': 22,
'h': 23,
'c': 24,
'e': 25,
'6': 26,
'm': 27,
'u': 28,
'a': 29,
'7': 30,
'l': 31,
};

View file

@ -14,7 +14,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
@ -30,6 +30,7 @@ import 'package:stackwallet/utilities/test_monero_node_connection.dart';
import 'package:stackwallet/utilities/test_stellar_node_connection.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/api/tezos/tezos_rpc_api.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -170,7 +171,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
case Coin.bitcoincashTestnet:
case Coin.firoTestNet:
case Coin.dogecoinTestNet:
final client = ElectrumX(
final client = ElectrumXClient(
host: formData.host!,
port: formData.port!,
useSSL: formData.useSSL!,
@ -205,9 +206,15 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
case Coin.nano:
case Coin.banano:
case Coin.tezos:
throw UnimplementedError();
//TODO: check network/node
case Coin.tezos:
try {
testPassed = await TezosRpcAPI.testNetworkConnection(
nodeInfo: (host: formData.host!, port: formData.port!),
);
} catch (_) {}
break;
}
if (showFlushBar && mounted) {

View file

@ -13,7 +13,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
@ -29,6 +29,7 @@ import 'package:stackwallet/utilities/test_monero_node_connection.dart';
import 'package:stackwallet/utilities/test_stellar_node_connection.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/api/tezos/tezos_rpc_api.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -147,7 +148,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
case Coin.litecoinTestNet:
case Coin.bitcoincashTestnet:
case Coin.eCash:
final client = ElectrumX(
final client = ElectrumXClient(
host: node!.host,
port: node.port,
useSSL: node.useSSL,
@ -173,9 +174,16 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
case Coin.nano:
case Coin.banano:
case Coin.tezos:
// TODO: fix this lacking code
throw UnimplementedError();
//TODO: check network/node
case Coin.tezos:
try {
testPassed = await TezosRpcAPI.testNetworkConnection(
nodeInfo: (host: node!.host, port: node!.port),
);
} catch (_) {}
break;
case Coin.stellar:
case Coin.stellarTestnet:
try {

View file

@ -13,26 +13,24 @@ import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:isar/isar.dart';
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/models/isar/models/transaction_note.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/stack_restoring_ui_state.dart';
import 'package:stackwallet/models/trade_wallet_lookup.dart';
import 'package:stackwallet/models/wallet_restore_state.dart';
import 'package:stackwallet/services/address_book_service.dart';
import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/notes_service.dart';
import 'package:stackwallet/services/trade_notes_service.dart';
import 'package:stackwallet/services/trade_sent_from_stack_service.dart';
import 'package:stackwallet/services/trade_service.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/services/wallets.dart';
import 'package:stackwallet/services/wallets_service.dart';
import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -43,6 +41,10 @@ import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/private_key_interface.dart';
import 'package:tuple/tuple.dart';
import 'package:uuid/uuid.dart';
import 'package:wakelock/wakelock.dart';
@ -285,27 +287,39 @@ abstract class SWB {
);
List<dynamic> backupWallets = [];
for (var manager in _wallets.managers) {
for (var wallet in _wallets.wallets) {
Map<String, dynamic> backupWallet = {};
backupWallet['name'] = manager.walletName;
backupWallet['id'] = manager.walletId;
backupWallet['isFavorite'] = manager.isFavorite;
backupWallet['mnemonic'] = await manager.mnemonic;
backupWallet['mnemonicPassphrase'] = await manager.mnemonicPassphrase;
backupWallet['coinName'] = manager.coin.name;
backupWallet['storedChainHeight'] = DB.instance
.get<dynamic>(boxName: manager.walletId, key: 'storedChainHeight');
backupWallet['name'] = wallet.info.name;
backupWallet['id'] = wallet.walletId;
backupWallet['isFavorite'] = wallet.info.isFavourite;
backupWallet['otherDataJsonString'] = wallet.info.otherDataJsonString;
backupWallet['txidList'] = DB.instance.get<dynamic>(
boxName: manager.walletId, key: "cachedTxids") as List?;
if (wallet is MnemonicInterface) {
backupWallet['mnemonic'] = await wallet.getMnemonic();
backupWallet['mnemonicPassphrase'] =
await wallet.getMnemonicPassphrase();
} else if (wallet is PrivateKeyInterface) {
backupWallet['privateKey'] = await wallet.getPrivateKey();
}
backupWallet['coinName'] = wallet.info.coin.name;
backupWallet['storedChainHeight'] = wallet.info.cachedChainHeight;
// backupWallet['txidList'] = DB.instance.get<dynamic>(
// boxName: wallet.walletId, key: "cachedTxids") as List?;
// the following can cause a deadlock
// (await manager.transactionData).getAllTransactions().keys.toList();
backupWallet['restoreHeight'] = DB.instance
.get<dynamic>(boxName: manager.walletId, key: 'restoreHeight');
backupWallet['restoreHeight'] = wallet.info.restoreHeight;
final isarNotes = await MainDB.instance.isar.transactionNotes
.where()
.walletIdEqualTo(wallet.walletId)
.findAll();
final notes = isarNotes
.asMap()
.map((key, value) => MapEntry(value.txid, value.value));
NotesService notesService = NotesService(walletId: manager.walletId);
var notes = await notesService.notes;
backupWallet['notes'] = notes;
backupWallets.add(backupWallet);
@ -355,92 +369,113 @@ abstract class SWB {
}
static Future<bool> asyncRestore(
Tuple2<dynamic, Manager> tuple,
Tuple2<dynamic, WalletInfo> tuple,
Prefs prefs,
NodeService nodeService,
SecureStorageInterface secureStorageInterface,
StackRestoringUIState? uiState,
WalletsService walletsService,
) async {
final manager = tuple.item2;
final info = tuple.item2;
final walletbackup = tuple.item1;
String? mnemonic, mnemonicPassphrase, privateKey;
if (walletbackup['mnemonic'] == null) {
// probably private key based
privateKey = walletbackup['privateKey'] as String;
} else {
if (walletbackup['mnemonic'] is List) {
List<String> mnemonicList = (walletbackup['mnemonic'] as List<dynamic>)
.map<String>((e) => e as String)
.toList();
final String mnemonic = mnemonicList.join(" ").trim();
final String mnemonicPassphrase =
walletbackup['mnemonicPassphrase'] as String? ?? "";
mnemonic = mnemonicList.join(" ").trim();
} else {
mnemonic = walletbackup['mnemonic'] as String;
}
mnemonicPassphrase = walletbackup['mnemonicPassphrase'] as String? ?? "";
}
uiState?.update(
walletId: manager.walletId,
walletId: info.walletId,
restoringStatus: StackRestoringStatus.restoring,
mnemonic: mnemonic,
mnemonicPassphrase: mnemonicPassphrase,
);
if (_shouldCancelRestore) {
return false;
}
try {
int restoreHeight = 0;
final wallet = await Wallet.create(
walletInfo: info,
mainDB: MainDB.instance,
secureStorageInterface: secureStorageInterface,
nodeService: nodeService,
prefs: prefs,
mnemonic: mnemonic,
mnemonicPassphrase: mnemonicPassphrase,
privateKey: privateKey,
);
restoreHeight = walletbackup['restoreHeight'] as int? ?? 0;
await wallet.init();
int restoreHeight = walletbackup['restoreHeight'] as int? ?? 0;
if (restoreHeight <= 0) {
restoreHeight = walletbackup['storedChainHeight'] as int? ?? 0;
}
manager.isFavorite = walletbackup['isFavorite'] == "false" ? false : true;
uiState?.update(
walletId: info.walletId,
restoringStatus: StackRestoringStatus.restoring,
wallet: wallet,
);
if (_shouldCancelRestore) {
return false;
}
// restore notes
NotesService notesService = NotesService(walletId: manager.walletId);
final notes = walletbackup["notes"] as Map?;
if (notes != null) {
for (final note in notes.entries) {
await notesService.editOrAddNote(
txid: note.key as String, note: note.value as String);
}
}
final notesMap =
Map<String, String>.from(walletbackup["notes"] as Map? ?? {});
final List<TransactionNote> notes = [];
if (_shouldCancelRestore) {
return false;
}
// TODO GUI option to set maxUnusedAddressGap?
// default is 20 but it may miss some transactions if
// the previous wallet software generated many addresses
// without using them
await manager.recoverFromMnemonic(
mnemonic: mnemonic,
mnemonicPassphrase: mnemonicPassphrase,
maxUnusedAddressGap: manager.coin == Coin.firo ? 50 : 20,
maxNumberOfIndexesToCheck: 1000,
height: restoreHeight,
for (final key in notesMap.keys) {
if (notesMap[key] != null && notesMap[key]!.isNotEmpty) {
notes.add(
TransactionNote(
walletId: info.walletId,
txid: key,
value: notesMap[key]!,
),
);
}
}
if (notes.isNotEmpty) {
await MainDB.instance.isar.writeTxn(() async {
await MainDB.instance.isar.transactionNotes.putAll(notes);
});
}
if (_shouldCancelRestore) {
return false;
}
// if mnemonic verified does not get set the wallet will be deleted on app restart
await walletsService.setMnemonicVerified(walletId: manager.walletId);
await wallet.info.setMnemonicVerified(isar: MainDB.instance.isar);
if (_shouldCancelRestore) {
return false;
}
Logging.instance.log(
"SWB restored: ${manager.walletId} ${manager.walletName} ${manager.coin.prettyName}",
"SWB restored: ${info.walletId} ${info.name} ${info.coin.prettyName}",
level: LogLevel.Info);
final currentAddress = await manager.currentReceivingAddress;
final currentAddress = await wallet.getCurrentReceivingAddress();
uiState?.update(
walletId: manager.walletId,
walletId: info.walletId,
restoringStatus: StackRestoringStatus.success,
manager: manager,
address: currentAddress,
wallet: wallet,
address: currentAddress?.value,
height: restoreHeight,
mnemonic: mnemonic,
mnemonicPassphrase: mnemonicPassphrase,
@ -448,9 +483,8 @@ abstract class SWB {
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Warning);
uiState?.update(
walletId: manager.walletId,
walletId: info.walletId,
restoringStatus: StackRestoringStatus.failed,
manager: manager,
mnemonic: mnemonic,
mnemonicPassphrase: mnemonicPassphrase,
);
@ -578,13 +612,10 @@ abstract class SWB {
level: LogLevel.Warning,
);
List<String> _currentWalletIds = Map<String, dynamic>.from(DB.instance
.get<dynamic>(
boxName: DB.boxNameAllWalletsData, key: "names") as Map? ??
{})
.values
.map((e) => e["id"] as String)
.toList();
List<String> _currentWalletIds = await MainDB.instance.isar.walletInfo
.where()
.walletIdProperty()
.findAll();
final preRestoreState =
PreRestoreState(_currentWalletIds.toSet(), preRestoreJSON);
@ -634,13 +665,11 @@ abstract class SWB {
final nodeService = NodeService(
secureStorageInterface: secureStorageInterface,
);
final walletsService = WalletsService(
secureStorageInterface: secureStorageInterface,
);
final _prefs = Prefs.instance;
await _prefs.init();
final List<Tuple2<dynamic, Manager>> managers = [];
final List<Tuple2<dynamic, WalletInfo>> managers = [];
Map<String, WalletRestoreState> walletStates = {};
@ -653,26 +682,22 @@ abstract class SWB {
return false;
}
Coin coin = Coin.values
final coin = Coin.values
.firstWhere((element) => element.name == walletbackup['coinName']);
String walletName = walletbackup['name'] as String;
final walletName = walletbackup['name'] as String;
final walletId = oldToNewWalletIdMap[walletbackup["id"] as String]!;
// TODO: use these for monero and possibly other coins later on?
// final List<String> txidList = List<String>.from(walletbackup['txidList'] as List? ?? []);
const int sanityCheckMax = 100;
int count = 0;
while (await walletsService.checkForDuplicate(walletName) &&
count < sanityCheckMax) {
walletName += " (restored)";
}
await walletsService.addExistingStackWallet(
name: walletName,
final info = WalletInfo(
coinName: coin.name,
walletId: walletId,
coin: coin,
shouldNotifyListeners: false,
name: walletName,
mainAddressType: coin.primaryAddressType,
restoreHeight: walletbackup['restoreHeight'] as int? ?? 0,
otherDataJsonString: walletbackup["otherDataJsonString"] as String?,
cachedChainHeight: walletbackup['storedChainHeight'] as int? ?? 0,
);
var node = nodeService.getPrimaryNodeFor(coin: coin);
@ -682,9 +707,9 @@ abstract class SWB {
await nodeService.setPrimaryNodeFor(coin: coin, node: node);
}
final txTracker = TransactionNotificationTracker(walletId: walletId);
final failovers = nodeService.failoverNodesFor(coin: coin);
// final txTracker = TransactionNotificationTracker(walletId: walletId);
//
// final failovers = nodeService.failoverNodesFor(coin: coin);
// check if cancel was requested and restore previous state
if (_checkShouldCancel(
@ -694,20 +719,7 @@ abstract class SWB {
return false;
}
final wallet = CoinServiceAPI.from(
coin,
walletId,
walletName,
secureStorageInterface,
node,
txTracker,
_prefs,
failovers,
);
final manager = Manager(wallet);
managers.add(Tuple2(walletbackup, manager));
managers.add(Tuple2(walletbackup, info));
// check if cancel was requested and restore previous state
if (_checkShouldCancel(
preRestoreState,
@ -721,7 +733,6 @@ abstract class SWB {
restoringStatus: StackRestoringStatus.waiting,
walletId: walletId,
walletName: walletName,
manager: manager,
);
}
@ -746,7 +757,13 @@ abstract class SWB {
)) {
return false;
}
final bools = await asyncRestore(tuple, uiState, walletsService);
final bools = await asyncRestore(
tuple,
_prefs,
nodeService,
secureStorageInterface,
uiState,
);
restoreStatuses.add(Future(() => bools));
}
@ -781,7 +798,7 @@ abstract class SWB {
Logging.instance.log("done with SWB restore", level: LogLevel.Warning);
if (Util.isDesktop) {
await Wallets.sharedInstance
.loadAfterStackRestore(_prefs, managers.map((e) => e.item2).toList());
.loadAfterStackRestore(_prefs, uiState?.wallets ?? []);
}
return true;
}
@ -984,14 +1001,16 @@ abstract class SWB {
}
// finally remove any added wallets
final walletsService =
WalletsService(secureStorageInterface: secureStorageInterface);
final namesData = await walletsService.walletNames;
for (final entry in namesData.entries) {
if (!revertToState.walletIds.contains(entry.value.walletId)) {
await walletsService.deleteWallet(entry.key, true);
}
}
final allWalletIds = (await MainDB.instance.isar.walletInfo
.where()
.walletIdProperty()
.findAll())
.toSet();
final walletIdsToDelete = allWalletIds.difference(revertToState.walletIds);
await MainDB.instance.isar.writeTxn(() async {
await MainDB.instance.isar.walletInfo
.deleteAllByWalletId(walletIdsToDelete.toList());
});
_cancelCompleter!.complete();
_shouldCancelRestore = false;

View file

@ -215,9 +215,9 @@ class _StackRestoreProgressViewState
}
void _addWalletsToHomeView() {
ref.read(walletsChangeNotifierProvider).loadAfterStackRestore(
ref.read(pWallets).loadAfterStackRestore(
ref.read(prefsChangeNotifierProvider),
ref.read(stackRestoringUIStateProvider).managers,
ref.read(stackRestoringUIStateProvider).wallets,
);
}

View file

@ -105,49 +105,29 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
),
onRightTapped: restoringStatus == StackRestoringStatus.failed
? () async {
final manager = ref.read(provider).manager!;
final wallet = ref.read(provider).wallet!;
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
walletId: wallet.walletId,
restoringStatus: StackRestoringStatus.restoring);
try {
final mnemonicList = await manager.mnemonic;
int maxUnusedAddressGap = 20;
if (coin == Coin.firo) {
maxUnusedAddressGap = 50;
}
const maxNumberOfIndexesToCheck = 1000;
if (mnemonicList.isEmpty) {
await manager.recoverFromMnemonic(
mnemonic: ref.read(provider).mnemonic!,
mnemonicPassphrase:
ref.read(provider).mnemonicPassphrase!,
maxUnusedAddressGap: maxUnusedAddressGap,
maxNumberOfIndexesToCheck: maxNumberOfIndexesToCheck,
height: ref.read(provider).height ?? 0,
);
} else {
await manager.fullRescan(
maxUnusedAddressGap,
maxNumberOfIndexesToCheck,
);
}
await wallet.recover(isRescan: true);
if (mounted) {
final address = await manager.currentReceivingAddress;
final address =
await wallet.getCurrentReceivingAddress();
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
walletId: wallet.walletId,
restoringStatus: StackRestoringStatus.success,
address: address,
address: address!.value,
);
}
} catch (_) {
if (mounted) {
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
walletId: wallet.walletId,
restoringStatus: StackRestoringStatus.failed,
);
}
@ -223,7 +203,7 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
: null,
)
: RoundedContainer(
padding: EdgeInsets.all(0),
padding: const EdgeInsets.all(0),
color: Theme.of(context).extension<StackColors>()!.popupBG,
borderColor: Theme.of(context).extension<StackColors>()!.background,
child: RestoringItemCard(
@ -250,50 +230,53 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
),
onRightTapped: restoringStatus == StackRestoringStatus.failed
? () async {
final manager = ref.read(provider).manager!;
final wallet = ref.read(provider).wallet!;
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
walletId: wallet.walletId,
restoringStatus: StackRestoringStatus.restoring);
try {
final mnemonicList = await manager.mnemonic;
int maxUnusedAddressGap = 20;
if (coin == Coin.firo) {
maxUnusedAddressGap = 50;
}
const maxNumberOfIndexesToCheck = 1000;
// final mnemonicList = await manager.mnemonic;
// int maxUnusedAddressGap = 20;
// if (coin == Coin.firo) {
// maxUnusedAddressGap = 50;
// }
// const maxNumberOfIndexesToCheck = 1000;
//
// if (mnemonicList.isEmpty) {
// await manager.recoverFromMnemonic(
// mnemonic: ref.read(provider).mnemonic!,
// mnemonicPassphrase:
// ref.read(provider).mnemonicPassphrase!,
// maxUnusedAddressGap: maxUnusedAddressGap,
// maxNumberOfIndexesToCheck:
// maxNumberOfIndexesToCheck,
// height: ref.read(provider).height ?? 0,
// );
// } else {
// await manager.fullRescan(
// maxUnusedAddressGap,
// maxNumberOfIndexesToCheck,
// );
// }
if (mnemonicList.isEmpty) {
await manager.recoverFromMnemonic(
mnemonic: ref.read(provider).mnemonic!,
mnemonicPassphrase:
ref.read(provider).mnemonicPassphrase!,
maxUnusedAddressGap: maxUnusedAddressGap,
maxNumberOfIndexesToCheck:
maxNumberOfIndexesToCheck,
height: ref.read(provider).height ?? 0,
);
} else {
await manager.fullRescan(
maxUnusedAddressGap,
maxNumberOfIndexesToCheck,
);
}
await wallet.recover(isRescan: true);
if (mounted) {
final address = await manager.currentReceivingAddress;
final address =
await wallet.getCurrentReceivingAddress();
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
walletId: wallet.walletId,
restoringStatus: StackRestoringStatus.success,
address: address,
address: address!.value,
);
}
} catch (_) {
if (mounted) {
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
walletId: wallet.walletId,
restoringStatus: StackRestoringStatus.failed,
);
}

View file

@ -19,6 +19,7 @@ import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -45,7 +46,7 @@ class _StartupPreferencesViewState
// check if wallet exists (hasn't been deleted or otherwise missing)
if (possibleWalletId != null) {
try {
ref.read(walletsChangeNotifierProvider).getManager(possibleWalletId);
ref.read(pWallets).getWallet(possibleWalletId);
} catch (_) {
safe = false;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
@ -252,19 +253,14 @@ class _StartupPreferencesViewState
File(
ref.watch(
coinIconProvider(
ref
.watch(
walletsChangeNotifierProvider
.select(
(value) =>
value.getManager(
ref.watch(
prefsChangeNotifierProvider.select((value) => value.startupWalletId!),
pWalletCoin(
ref.watch(
prefsChangeNotifierProvider.select((value) =>
value.startupWalletId!),
),
),
),
)
.coin,
),
),
),
@ -273,21 +269,15 @@ class _StartupPreferencesViewState
width: 10,
),
Text(
ref
.watch(
walletsChangeNotifierProvider
.select(
(value) =>
value
.getManager(
ref.watch(
prefsChangeNotifierProvider.select((value) =>
pWalletName(
ref.watch(
prefsChangeNotifierProvider.select(
(value) =>
value.startupWalletId!),
),
),
),
)
.walletName,
style: STextStyles
.itemSubtitle(
context),

View file

@ -18,6 +18,7 @@ import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
@ -38,11 +39,10 @@ class _StartupWalletSelectionViewState
@override
Widget build(BuildContext context) {
final managers = ref
.watch(walletsChangeNotifierProvider.select((value) => value.managers));
final wallets = ref.watch(pWallets).wallets;
_controllers.clear();
for (final manager in managers) {
for (final manager in wallets) {
_controllers[manager.walletId] = DSBController();
}
@ -95,18 +95,21 @@ class _StartupWalletSelectionViewState
padding: const EdgeInsets.all(0),
child: Column(
children: [
...managers.map(
(manager) => Padding(
...wallets.map(
(wallet) => Padding(
padding: const EdgeInsets.all(12),
child: Row(
key: Key(
"startupWalletSelectionGroupKey_${manager.walletId}"),
"startupWalletSelectionGroupKey_${wallet.walletId}"),
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.colorForCoin(manager.coin)
.colorForCoin(
ref.watch(pWalletCoin(
wallet.walletId)),
)
.withOpacity(0.5),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
@ -117,7 +120,10 @@ class _StartupWalletSelectionViewState
child: SvgPicture.file(
File(
ref.watch(
coinIconProvider(manager.coin),
coinIconProvider(
ref.watch(pWalletCoin(
wallet.walletId)),
),
),
),
width: 20,
@ -136,7 +142,8 @@ class _StartupWalletSelectionViewState
CrossAxisAlignment.start,
children: [
Text(
manager.walletName,
ref.watch(
pWalletName(wallet.walletId)),
style: STextStyles.titleBold12(
context),
),
@ -184,7 +191,7 @@ class _StartupWalletSelectionViewState
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: manager.walletId,
value: wallet.walletId,
groupValue: ref.watch(
prefsChangeNotifierProvider.select(
(value) =>

View file

@ -11,6 +11,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart';
import 'package:stackwallet/providers/global/active_wallet_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -95,11 +96,8 @@ class SyncingOptionsView extends ConsumerWidget {
SyncingType.currentWalletOnly;
// disable auto sync on all wallets that aren't active/current
ref
.read(walletsChangeNotifierProvider)
.managers
.forEach((e) {
if (!e.isActiveWallet) {
ref.read(pWallets).wallets.forEach((e) {
if (e.walletId != ref.read(currentWalletIdProvider)) {
e.shouldAutoSync = false;
}
});
@ -178,8 +176,8 @@ class SyncingOptionsView extends ConsumerWidget {
// enable auto sync on all wallets
ref
.read(walletsChangeNotifierProvider)
.managers
.read(pWallets)
.wallets
.forEach((e) => e.shouldAutoSync = true);
}
},
@ -259,11 +257,8 @@ class SyncingOptionsView extends ConsumerWidget {
.walletIdsSyncOnStartup;
// enable auto sync on selected wallets only
ref
.read(walletsChangeNotifierProvider)
.managers
.forEach((e) =>
e.shouldAutoSync = ids.contains(e.walletId));
ref.read(pWallets).wallets.forEach(
(e) => e.shouldAutoSync = ids.contains(e.walletId));
}
},
child: Container(

View file

@ -13,6 +13,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/providers/global/active_wallet_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
@ -21,6 +22,7 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/sync_type_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -34,8 +36,7 @@ class WalletSyncingOptionsView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final managers = ref
.watch(walletsChangeNotifierProvider.select((value) => value.managers));
final walletInfos = ref.watch(pWallets).wallets.map((e) => e.info);
final isDesktop = Util.isDesktop;
return ConditionalParent(
@ -74,7 +75,7 @@ class WalletSyncingOptionsView extends ConsumerWidget {
condition: isDesktop,
builder: (child) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 32),
padding: const EdgeInsets.symmetric(horizontal: 32),
child: child,
);
},
@ -109,18 +110,18 @@ class WalletSyncingOptionsView extends ConsumerWidget {
.background,
child: Column(
children: [
...managers.map(
(manager) => Padding(
...walletInfos.map(
(info) => Padding(
padding: const EdgeInsets.all(12),
child: Row(
key: Key(
"syncingPrefsSelectedWalletIdGroupKey_${manager.walletId}"),
"syncingPrefsSelectedWalletIdGroupKey_${info.walletId}"),
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.colorForCoin(manager.coin)
.colorForCoin(info.coin)
.withOpacity(0.5),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
@ -131,7 +132,7 @@ class WalletSyncingOptionsView extends ConsumerWidget {
child: SvgPicture.file(
File(
ref.watch(
coinIconProvider(manager.coin),
coinIconProvider(info.coin),
),
),
width: 20,
@ -149,7 +150,7 @@ class WalletSyncingOptionsView extends ConsumerWidget {
CrossAxisAlignment.start,
children: [
Text(
manager.walletName,
info.name,
style:
STextStyles.titleBold12(context),
),
@ -158,9 +159,12 @@ class WalletSyncingOptionsView extends ConsumerWidget {
),
Text(
ref
.watch(pAmountFormatter(
manager.coin))
.format(manager.balance.total),
.watch(
pAmountFormatter(info.coin))
.format(ref
.watch(pWalletBalance(
info.walletId))
.total),
style:
STextStyles.itemSubtitle(context),
)
@ -175,7 +179,7 @@ class WalletSyncingOptionsView extends ConsumerWidget {
.watch(prefsChangeNotifierProvider
.select((value) => value
.walletIdsSyncOnStartup))
.contains(manager.walletId),
.contains(info.walletId),
onValueChanged: (value) {
final syncType = ref
.read(prefsChangeNotifierProvider)
@ -185,22 +189,28 @@ class WalletSyncingOptionsView extends ConsumerWidget {
.walletIdsSyncOnStartup
.toList();
if (value) {
ids.add(manager.walletId);
ids.add(info.walletId);
} else {
ids.remove(manager.walletId);
ids.remove(info.walletId);
}
final wallet = ref
.read(pWallets)
.getWallet(info.walletId);
switch (syncType) {
case SyncingType.currentWalletOnly:
if (manager.isActiveWallet) {
manager.shouldAutoSync = value;
if (info.walletId ==
ref.read(
currentWalletIdProvider)) {
wallet.shouldAutoSync = value;
}
break;
case SyncingType
.selectedWalletsAtStartup:
case SyncingType
.allWalletsOnStartup:
manager.shouldAutoSync = value;
wallet.shouldAutoSync = value;
break;
}

View file

@ -17,13 +17,13 @@ import 'package:flutter_svg/svg.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
@ -98,10 +98,7 @@ class WalletBackupView extends ConsumerWidget {
height: 4,
),
Text(
ref
.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)))
.walletName,
ref.watch(pWalletName(walletId)),
textAlign: TextAlign.center,
style: STextStyles.label(context).copyWith(
fontSize: 12,

View file

@ -70,7 +70,7 @@ class _RescanningDialogState extends State<RescanningDialog>
child: ConditionalParent(
condition: isDesktop,
builder: (child) => DesktopDialog(
maxHeight: 200,
maxHeight: 150,
maxWidth: 500,
child: child,
),

View file

@ -22,9 +22,6 @@ import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_net
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/rescanning_dialog.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/coins/monero/monero_wallet.dart';
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart';
import 'package:stackwallet/services/event_bus/events/global/blocks_remaining_event.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
@ -38,11 +35,16 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/impl/epiccash_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/monero_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/wownero_wallet.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.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/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/expandable.dart';
import 'package:stackwallet/widgets/progress_bar.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
@ -122,10 +124,8 @@ class _WalletNetworkSettingsViewState
Future<void> _attemptRescan() async {
if (!Platform.isLinux) await Wakelock.enable();
int maxUnusedAddressGap = 20;
const int maxNumberOfIndexesToCheck = 1000;
try {
if (mounted) {
unawaited(
showDialog<dynamic>(
context: context,
@ -136,19 +136,10 @@ class _WalletNetworkSettingsViewState
);
try {
if (ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.coin ==
Coin.firo) {
maxUnusedAddressGap = 50;
}
await ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.fullRescan(
maxUnusedAddressGap,
maxNumberOfIndexesToCheck,
final wallet = ref.read(pWallets).getWallet(widget.walletId);
await wallet.recover(
isRescan: true,
);
if (mounted) {
@ -160,7 +151,14 @@ class _WalletNetworkSettingsViewState
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) => StackDialog(
builder: (context) => ConditionalParent(
condition: isDesktop,
builder: (child) => DesktopDialog(
maxHeight: 150,
maxWidth: 500,
child: child,
),
child: StackDialog(
title: "Rescan completed",
rightButton: TextButton(
style: Theme.of(context)
@ -175,6 +173,7 @@ class _WalletNetworkSettingsViewState
},
),
),
),
);
}
} catch (e) {
@ -208,9 +207,11 @@ class _WalletNetworkSettingsViewState
);
}
}
}
} finally {
if (!Platform.isLinux) await Wakelock.disable();
}
}
String _percentString(double value) {
double realPercent = (value * 10000).ceil().clamp(0, 10000) / 100.0;
@ -257,10 +258,7 @@ class _WalletNetworkSettingsViewState
},
);
final coin = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.coin;
final coin = ref.read(pWalletCoin(widget.walletId));
if (coin == Coin.monero || coin == Coin.wownero || coin == Coin.epicCash) {
_blocksRemainingSubscription = eventBus.on<BlocksRemainingEvent>().listen(
@ -319,34 +317,25 @@ class _WalletNetworkSettingsViewState
? 430.0
: screenWidth - (_padding * 2) - (_boxPadding * 3) - _iconSize;
final coin = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.coin;
final coin = ref.watch(pWalletCoin(widget.walletId));
if (coin == Coin.monero) {
double highestPercent = (ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as MoneroWallet)
double highestPercent =
(ref.read(pWallets).getWallet(widget.walletId) as MoneroWallet)
.highestPercentCached;
if (_percent < highestPercent) {
_percent = highestPercent.clamp(0.0, 1.0);
}
} else if (coin == Coin.wownero) {
double highestPercent = (ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as WowneroWallet)
double highestPercent =
(ref.watch(pWallets).getWallet(widget.walletId) as WowneroWallet)
.highestPercentCached;
if (_percent < highestPercent) {
_percent = highestPercent.clamp(0.0, 1.0);
}
} else if (coin == Coin.epicCash) {
double highestPercent = (ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as EpicCashWallet)
double highestPercent =
(ref.watch(pWallets).getWallet(widget.walletId) as EpiccashWallet)
.highestPercent;
if (_percent < highestPercent) {
_percent = highestPercent.clamp(0.0, 1.0);
@ -371,11 +360,7 @@ class _WalletNetworkSettingsViewState
style: STextStyles.navBarTitle(context),
),
actions: [
if (ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.coin !=
Coin.epicCash)
if (ref.watch(pWalletCoin(widget.walletId)) != Coin.epicCash)
Padding(
padding: const EdgeInsets.only(
top: 10,
@ -499,10 +484,7 @@ class _WalletNetworkSettingsViewState
CustomTextButton(
text: "Resync",
onTap: () {
ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.refresh();
ref.read(pWallets).getWallet(widget.walletId).refresh();
},
),
],
@ -905,7 +887,7 @@ class _WalletNetworkSettingsViewState
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${ref.watch(walletsChangeNotifierProvider.select((value) => value.getManager(widget.walletId).coin)).prettyName} nodes",
"${ref.watch(pWalletCoin(widget.walletId)).prettyName} nodes",
textAlign: TextAlign.left,
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
@ -918,10 +900,7 @@ class _WalletNetworkSettingsViewState
AddEditNodeView.routeName,
arguments: Tuple4(
AddEditNodeViewType.add,
ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.coin,
ref.read(pWalletCoin(widget.walletId)),
null,
WalletNetworkSettingsView.routeName,
),
@ -934,25 +913,16 @@ class _WalletNetworkSettingsViewState
height: isDesktop ? 12 : 8,
),
NodesList(
coin: ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).coin)),
coin: ref.watch(pWalletCoin(widget.walletId)),
popBackToRoute: WalletNetworkSettingsView.routeName,
),
if (isDesktop &&
ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.coin !=
Coin.epicCash)
ref.watch(pWalletCoin(widget.walletId)) != Coin.epicCash)
const SizedBox(
height: 32,
),
if (isDesktop &&
ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.coin !=
Coin.epicCash)
ref.watch(pWalletCoin(widget.walletId)) != Coin.epicCash)
Padding(
padding: const EdgeInsets.only(
bottom: 12,
@ -969,11 +939,7 @@ class _WalletNetworkSettingsViewState
),
),
if (isDesktop &&
ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.coin !=
Coin.epicCash)
ref.watch(pWalletCoin(widget.walletId)) != Coin.epicCash)
RoundedWhiteContainer(
borderColor: isDesktop
? Theme.of(context).extension<StackColors>()!.background

View file

@ -30,7 +30,6 @@ import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_set
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
@ -40,6 +39,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/wallet/impl/epiccash_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
@ -88,8 +89,9 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
void initState() {
walletId = widget.walletId;
coin = widget.coin;
xPubEnabled =
ref.read(walletsChangeNotifierProvider).getManager(walletId).hasXPub;
// TODO: [prio=low] xpubs
// xPubEnabled = ref.read(pWallets).getWallet(walletId).hasXPub;
xPubEnabled = false;
xpub = "";
_currentSyncStatus = widget.initialSyncStatus;
@ -230,10 +232,13 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
iconSize: 16,
title: "Wallet backup",
onPressed: () async {
final mnemonic = await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.mnemonic;
final wallet = ref
.read(pWallets)
.getWallet(widget.walletId);
// TODO: [prio=frost] take wallets that don't have a mnemonic into account
if (wallet is MnemonicInterface) {
final mnemonic =
await wallet.getMnemonicAsWords();
if (mounted) {
await Navigator.push(
@ -244,10 +249,12 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
.useMaterialPageRoute,
builder: (_) => LockscreenView(
routeOnSuccessArguments:
Tuple2(walletId, mnemonic),
Tuple2(
walletId, mnemonic),
showBackButton: true,
routeOnSuccess:
WalletBackupView.routeName,
WalletBackupView
.routeName,
biometricsCancelButtonString:
"CANCEL",
biometricsLocalizedReason:
@ -261,6 +268,7 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
),
);
}
}
},
);
},
@ -406,10 +414,11 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
builder: (_, ref, __) {
return TextButton(
onPressed: () {
ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.isActiveWallet = false;
// TODO: [prio=med] needs more thought if this is still required
// ref
// .read(pWallets)
// .getWallet(walletId)
// .isActiveWallet = false;
ref
.read(transactionFilterProvider.state)
.state = null;
@ -461,14 +470,11 @@ class _EpiBoxInfoFormState extends ConsumerState<EpicBoxInfoForm> {
final hostController = TextEditingController();
final portController = TextEditingController();
late EpicCashWallet wallet;
late EpiccashWallet wallet;
@override
void initState() {
wallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as EpicCashWallet;
wallet = ref.read(pWallets).getWallet(widget.walletId) as EpiccashWallet;
wallet.getEpicBoxConfig().then((EpicBoxConfigModel epicBoxConfig) {
hostController.text = epicBoxConfig.host;

View file

@ -16,16 +16,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/banano/banano_wallet.dart';
import 'package:stackwallet/services/coins/nano/nano_wallet.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -51,10 +49,12 @@ class ChangeRepresentativeView extends ConsumerStatefulWidget {
static const String routeName = "/changeRepresentative";
@override
ConsumerState<ChangeRepresentativeView> createState() => _XPubViewState();
ConsumerState<ChangeRepresentativeView> createState() =>
_ChangeRepresentativeViewState();
}
class _XPubViewState extends ConsumerState<ChangeRepresentativeView> {
class _ChangeRepresentativeViewState
extends ConsumerState<ChangeRepresentativeView> {
final _textController = TextEditingController();
final _textFocusNode = FocusNode();
final bool isDesktop = Util.isDesktop;
@ -64,24 +64,20 @@ class _XPubViewState extends ConsumerState<ChangeRepresentativeView> {
String? representative;
Future<String> loadRepresentative() async {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(widget.walletId);
final wallet = ref.read(pWallets).getWallet(widget.walletId);
if (manager.coin == Coin.nano) {
return (manager.wallet as NanoWallet).getCurrentRepresentative();
} else if (manager.coin == Coin.banano) {
return (manager.wallet as BananoWallet).getCurrentRepresentative();
}
if (wallet is NanoInterface) {
return wallet.getCurrentRepresentative();
} else {
throw Exception("Unsupported wallet attempted to show representative!");
}
}
Future<void> _save() async {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(widget.walletId);
final wallet =
ref.read(pWallets).getWallet(widget.walletId) as NanoInterface;
final changeFuture = manager.coin == Coin.nano
? (manager.wallet as NanoWallet).changeRepresentative
: (manager.wallet as BananoWallet).changeRepresentative;
final changeFuture = wallet.changeRepresentative;
final result = await showLoading(
whileFuture: changeFuture(_textController.text),

View file

@ -8,6 +8,8 @@
*
*/
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -15,13 +17,14 @@ import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
@ -29,14 +32,14 @@ import 'package:stackwallet/widgets/stack_dialog.dart';
class DeleteWalletRecoveryPhraseView extends ConsumerStatefulWidget {
const DeleteWalletRecoveryPhraseView({
Key? key,
required this.manager,
required this.walletId,
required this.mnemonic,
this.clipboardInterface = const ClipboardWrapper(),
}) : super(key: key);
static const routeName = "/deleteWalletRecoveryPhrase";
final Manager manager;
final String walletId;
final List<String> mnemonic;
final ClipboardInterface clipboardInterface;
@ -48,13 +51,11 @@ class DeleteWalletRecoveryPhraseView extends ConsumerStatefulWidget {
class _DeleteWalletRecoveryPhraseViewState
extends ConsumerState<DeleteWalletRecoveryPhraseView> {
late Manager _manager;
late List<String> _mnemonic;
late ClipboardInterface _clipboardInterface;
@override
void initState() {
_manager = widget.manager;
_mnemonic = widget.mnemonic;
_clipboardInterface = widget.clipboardInterface;
super.initState();
@ -90,15 +91,18 @@ class _DeleteWalletRecoveryPhraseViewState
.topNavIconPrimary,
),
onPressed: () async {
final words = await _manager.mnemonic;
await _clipboardInterface
.setData(ClipboardData(text: words.join(" ")));
.setData(ClipboardData(text: _mnemonic.join(" ")));
if (mounted) {
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
),
);
}
},
),
),
@ -114,7 +118,7 @@ class _DeleteWalletRecoveryPhraseViewState
height: 4,
),
Text(
_manager.walletName,
ref.watch(pWalletName(widget.walletId)),
textAlign: TextAlign.center,
style: STextStyles.label(context).copyWith(
fontSize: 12,
@ -192,22 +196,15 @@ class _DeleteWalletRecoveryPhraseViewState
.extension<StackColors>()!
.getPrimaryEnabledButtonStyle(context),
onPressed: () async {
final walletId = _manager.walletId;
final walletsInstance =
ref.read(walletsChangeNotifierProvider);
await ref
.read(walletsServiceChangeNotifierProvider)
.deleteWallet(_manager.walletName, true);
await ref.read(pWallets).deleteWallet(
ref.read(pWalletInfo(widget.walletId)),
ref.read(secureStoreProvider),
);
if (mounted) {
Navigator.of(context).popUntil(
ModalRoute.withName(HomeView.routeName));
}
// wait for widget tree to dispose of any widgets watching the manager
await Future<void>.delayed(
const Duration(seconds: 1));
walletsInstance.removeWallet(walletId: walletId);
},
child: Text(
"Ok",

View file

@ -14,10 +14,10 @@ import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_set
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:tuple/tuple.dart';
class DeleteWalletWarningView extends ConsumerWidget {
const DeleteWalletWarningView({
@ -99,17 +99,18 @@ class DeleteWalletWarningView extends ConsumerWidget {
.extension<StackColors>()!
.getPrimaryEnabledButtonStyle(context),
onPressed: () async {
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(walletId);
final mnemonic = await manager.mnemonic;
Navigator.of(context).pushNamed(
final wallet = ref.read(pWallets).getWallet(walletId);
final mnemonic =
await (wallet as MnemonicInterface).getMnemonicAsWords();
if (context.mounted) {
await Navigator.of(context).pushNamed(
DeleteWalletRecoveryPhraseView.routeName,
arguments: Tuple2(
manager,
mnemonic,
arguments: (
walletId: walletId,
mnemonicWords: mnemonic,
),
);
}
},
child: Text(
"View Backup Key",

View file

@ -8,14 +8,17 @@
*
*/
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
@ -47,8 +50,7 @@ class _RenameWalletViewState extends ConsumerState<RenameWalletView> {
void initState() {
_controller = TextEditingController();
walletId = widget.walletId;
originalName =
ref.read(walletsChangeNotifierProvider).getManager(walletId).walletName;
originalName = ref.read(pWalletName(walletId));
_controller.text = originalName;
super.initState();
}
@ -126,32 +128,43 @@ class _RenameWalletViewState extends ConsumerState<RenameWalletView> {
.getPrimaryEnabledButtonStyle(context),
onPressed: () async {
final newName = _controller.text;
final success = await ref
.read(walletsServiceChangeNotifierProvider)
.renameWallet(
from: originalName,
to: newName,
shouldNotifyListeners: true,
);
if (success) {
ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.walletName = newName;
String? errMessage;
try {
await ref.read(pWalletInfo(walletId)).updateName(
newName: newName,
isar: ref.read(mainDBProvider).isar,
);
} catch (e) {
if (e
.toString()
.contains("Empty wallet name not allowed!")) {
errMessage = "Empty wallet name not allowed.";
} else {
errMessage = e.toString();
}
}
if (mounted) {
if (errMessage == null) {
Navigator.of(context).pop();
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "Wallet renamed",
context: context,
),
);
} else {
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Wallet named \"$newName\" already exists",
context: context,
),
);
}
}
},
child: Text(
"Save",

View file

@ -13,11 +13,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -111,7 +111,7 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
context: context,
builder: (_) => StackDialog(
title:
"Do you want to delete ${ref.read(walletsChangeNotifierProvider).getManager(walletId).walletName}?",
"Do you want to delete ${ref.read(pWalletName(walletId))}?",
leftButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!

View file

@ -17,12 +17,12 @@ import 'package:flutter_svg/svg.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -53,15 +53,14 @@ class _XPubViewState extends ConsumerState<XPubView> {
final bool isDesktop = Util.isDesktop;
late ClipboardInterface _clipboardInterface;
late final Manager manager;
late final Wallet wallet;
String? xpub;
@override
void initState() {
_clipboardInterface = widget.clipboardInterface;
manager =
ref.read(walletsChangeNotifierProvider).getManager(widget.walletId);
wallet = ref.read(pWallets).getWallet(widget.walletId);
super.initState();
}
@ -153,7 +152,7 @@ class _XPubViewState extends ConsumerState<XPubView> {
left: 32,
),
child: Text(
"${manager.walletName} xPub",
"${wallet.info.name} xPub",
style: STextStyles.desktopH2(context),
),
),
@ -186,7 +185,8 @@ class _XPubViewState extends ConsumerState<XPubView> {
child: child,
),
child: FutureBuilder(
future: manager.xpub,
future: Future(() => "fixme"),
// future: wallet.xpub,
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {

View file

@ -12,6 +12,8 @@ import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -131,12 +133,13 @@ class _FiroRescanRecoveryErrorViewState
.topNavIconPrimary,
),
onPressed: () async {
final walletName =
ref.read(pWalletName(widget.walletId));
await showDialog<void>(
barrierDismissible: true,
context: context,
builder: (_) => StackDialog(
title:
"Do you want to delete ${ref.read(walletsChangeNotifierProvider).getManager(widget.walletId).walletName}?",
title: "Do you want to delete $walletName?",
leftButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
@ -253,10 +256,11 @@ class _FiroRescanRecoveryErrorViewState
),
);
} else {
final mnemonic = await ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.mnemonic;
final wallet =
ref.read(pWallets).getWallet(widget.walletId);
// TODO: [prio=low] take wallets that don't have a mnemonic into account
if (wallet is MnemonicInterface) {
final mnemonic = await wallet.getMnemonicAsWords();
if (mounted) {
await Navigator.push(
@ -281,6 +285,7 @@ class _FiroRescanRecoveryErrorViewState
);
}
}
}
},
),
const SizedBox(

View file

@ -15,13 +15,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
import 'package:stackwallet/pages/token_view/sub_widgets/my_tokens_list.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -89,8 +88,7 @@ class _MyTokensViewState extends ConsumerState<MyTokensView> {
),
title: Text(
"${ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(widget.walletId).walletName),
pWalletName(widget.walletId),
)} Tokens",
style: STextStyles.navBarTitle(context),
),
@ -233,11 +231,7 @@ class _MyTokensViewState extends ConsumerState<MyTokensView> {
child: MyTokensList(
walletId: widget.walletId,
searchTerm: _searchString,
tokenContracts: ref
.watch(walletsChangeNotifierProvider.select((value) => value
.getManager(widget.walletId)
.wallet as EthereumWallet))
.getWalletTokenContractAddresses(),
tokenContracts: ref.watch(pWalletTokenAddresses(widget.walletId)),
),
),
],

View file

@ -8,17 +8,16 @@
*
*/
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/pages/token_view/token_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/services/ethereum/cached_eth_token_balance.dart';
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -26,6 +25,12 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
import 'package:stackwallet/wallets/isar/providers/eth/token_balance_provider.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/impl/ethereum_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/dialogs/basic_dialog.dart';
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
@ -55,7 +60,7 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
WidgetRef ref,
) async {
try {
await ref.read(tokenServiceProvider)!.initialize();
await ref.read(pCurrentTokenWallet)!.init();
return true;
} catch (_) {
await showDialog<void>(
@ -81,17 +86,14 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
}
void _onPressed() async {
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
token: widget.token,
secureStore: ref.read(secureStoreProvider),
ethWallet: ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as EthereumWallet,
tracker: TransactionNotificationTracker(
walletId: widget.walletId,
),
);
final old = ref.read(tokenServiceStateProvider);
// exit previous if there is one
unawaited(old?.exit());
ref.read(tokenServiceStateProvider.state).state = Wallet.loadTokenWallet(
ethWallet:
ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet,
contract: widget.token,
) as EthTokenWallet;
final success = await showLoading<bool>(
whileFuture: _loadTokenWallet(context, ref),
@ -105,6 +107,7 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
}
if (mounted) {
unawaited(ref.read(pCurrentTokenWallet)!.refresh());
await Navigator.of(context).pushNamed(
isDesktop ? DesktopTokenView.routeName : TokenView.routeName,
arguments: widget.walletId,
@ -117,14 +120,14 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
cachedBalance = CachedEthTokenBalance(widget.walletId, widget.token);
WidgetsBinding.instance.addPostFrameCallback((_) async {
final address = await ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.currentReceivingAddress;
await cachedBalance.fetchAndUpdateCachedBalance(address);
if (mounted) {
final address = ref.read(pWalletReceivingAddress(widget.walletId));
await cachedBalance.fetchAndUpdateCachedBalance(
address, ref.read(mainDBProvider));
if (mounted) {
setState(() {});
}
}
});
super.initState();
@ -176,7 +179,14 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
const Spacer(),
Text(
ref.watch(pAmountFormatter(Coin.ethereum)).format(
cachedBalance.getCachedBalance().total,
ref
.watch(pTokenBalance(
(
walletId: widget.walletId,
contractAddress: widget.token.address
),
))
.total,
ethContract: widget.token,
),
style: isDesktop

View file

@ -19,12 +19,10 @@ import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart';
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
import 'package:stackwallet/pages/receive_view/receive_view.dart';
import 'package:stackwallet/pages/send_view/token_send_view.dart';
import 'package:stackwallet/pages/token_view/token_view.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/price_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_providers.dart';
@ -34,6 +32,9 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
import 'package:stackwallet/wallets/isar/providers/eth/token_balance_provider.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:tuple/tuple.dart';
@ -51,9 +52,9 @@ class TokenSummary extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final token =
ref.watch(tokenServiceProvider.select((value) => value!.tokenContract));
final balance =
ref.watch(tokenServiceProvider.select((value) => value!.balance));
ref.watch(pCurrentTokenWallet.select((value) => value!.tokenContract));
final balance = ref.watch(
pTokenBalance((walletId: walletId, contractAddress: token.address)));
return Stack(
children: [
@ -78,9 +79,7 @@ class TokenSummary extends ConsumerWidget {
),
Text(
ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(walletId).walletName,
),
pWalletName(walletId),
),
style: STextStyles.w500_12(context).copyWith(
color: Theme.of(context)
@ -159,7 +158,7 @@ class TokenSummary extends ConsumerWidget {
walletId: walletId,
initialSyncStatus: initialSyncStatus,
tokenContractAddress: ref.watch(
tokenServiceProvider.select(
pCurrentTokenWallet.select(
(value) => value!.tokenContract.address,
),
),
@ -366,10 +365,11 @@ class CoinTickerTag extends ConsumerWidget {
radiusMultiplier: 0.25,
color: Theme.of(context).extension<StackColors>()!.ethTagBG,
child: Text(
ref.watch(
walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).coin.ticker),
),
ref
.watch(
pWalletCoin(walletId),
)
.ticker,
style: STextStyles.w600_12(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.ethTagText,
),

View file

@ -12,24 +12,18 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
import 'package:stackwallet/pages/token_view/token_view.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/no_transactions_found.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/trade_card.dart';
import 'package:stackwallet/widgets/transaction_card.dart';
import 'package:tuple/tuple.dart';
class TokenTransactionsList extends ConsumerStatefulWidget {
const TokenTransactionsList({
@ -45,9 +39,13 @@ class TokenTransactionsList extends ConsumerStatefulWidget {
}
class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
//
late final int minConfirms;
bool _hasLoaded = false;
List<Transaction> _transactions2 = [];
List<TransactionV2> _transactions = [];
late final StreamSubscription<List<TransactionV2>> _subscription;
late final QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> _query;
BorderRadius get _borderRadiusFirst {
return BorderRadius.only(
@ -71,157 +69,54 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
);
}
Widget itemBuilder(
BuildContext context,
Transaction tx,
BorderRadius? radius,
Coin coin,
) {
final matchingTrades = ref
.read(tradesServiceProvider)
.trades
.where((e) => e.payInTxid == tx.txid || e.payOutTxid == tx.txid);
@override
void initState() {
minConfirms = ref
.read(pWallets)
.getWallet(widget.walletId)
.cryptoCurrency
.minConfirms;
if (tx.type == TransactionType.outgoing && matchingTrades.isNotEmpty) {
final trade = matchingTrades.first;
return Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG,
borderRadius: radius,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TransactionCard(
// this may mess with combined firo transactions
key: tx.isConfirmed(
ref.watch(walletsChangeNotifierProvider.select((value) =>
value.getManager(widget.walletId).currentHeight)),
coin.requiredConfirmations)
? Key(tx.txid + tx.type.name + tx.address.value.toString())
: UniqueKey(), //
transaction: tx,
walletId: widget.walletId,
),
TradeCard(
// this may mess with combined firo transactions
key: Key(tx.txid +
tx.type.name +
tx.address.value.toString() +
trade.uuid), //
trade: trade,
onTap: () async {
final walletName = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.walletName;
if (Util.isDesktop) {
await showDialog<void>(
context: context,
builder: (context) => Navigator(
initialRoute: TradeDetailsView.routeName,
onGenerateRoute: RouteGenerator.generateRoute,
onGenerateInitialRoutes: (_, __) {
return [
FadePageRoute(
DesktopDialog(
maxHeight: null,
maxWidth: 580,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
bottom: 16,
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Trade details",
style: STextStyles.desktopH3(context),
),
DesktopDialogCloseButton(
onPressedOverride: Navigator.of(
context,
rootNavigator: true,
).pop,
),
],
),
),
Flexible(
child: TradeDetailsView(
tradeId: trade.tradeId,
transactionIfSentFromStack: tx,
walletName: walletName,
walletId: widget.walletId,
),
),
],
),
),
const RouteSettings(
name: TradeDetailsView.routeName,
),
),
];
},
),
);
} else {
unawaited(
Navigator.of(context).pushNamed(
TradeDetailsView.routeName,
arguments: Tuple4(
trade.tradeId,
tx,
widget.walletId,
walletName,
),
),
);
}
},
)
],
),
);
} else {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG,
borderRadius: radius,
),
child: TransactionCard(
// this may mess with combined firo transactions
key: tx.isConfirmed(
ref.watch(walletsChangeNotifierProvider.select((value) =>
value.getManager(widget.walletId).currentHeight)),
coin.requiredConfirmations)
? Key(tx.txid + tx.type.name + tx.address.value.toString())
: UniqueKey(),
transaction: tx,
walletId: widget.walletId,
),
);
_query = ref
.read(mainDBProvider)
.isar
.transactionV2s
.where()
.walletIdEqualTo(widget.walletId)
.filter()
.subTypeEqualTo(TransactionSubType.ethToken)
.and()
.contractAddressEqualTo(
ref.read(pCurrentTokenWallet)!.tokenContract.address)
.sortByTimestampDesc();
_subscription = _query.watch().listen((event) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_transactions = event;
});
});
});
super.initState();
}
@override
void dispose() {
_subscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId)));
final wallet =
ref.watch(pWallets.select((value) => value.getWallet(widget.walletId)));
return FutureBuilder(
future: ref
.watch(tokenServiceProvider.select((value) => value!.transactions)),
builder: (fbContext, AsyncSnapshot<List<Transaction>> snapshot) {
future: _query.findAll(),
builder: (fbContext, AsyncSnapshot<List<TransactionV2>> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
_transactions2 = snapshot.data!;
_transactions = snapshot.data!;
_hasLoaded = true;
}
if (!_hasLoaded) {
@ -240,31 +135,35 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
],
);
}
if (_transactions2.isEmpty) {
if (_transactions.isEmpty) {
return const NoTransActionsFound();
} else {
_transactions2.sort((a, b) => b.timestamp - a.timestamp);
_transactions.sort((a, b) => b.timestamp - a.timestamp);
return RefreshIndicator(
onRefresh: () async {
if (!ref.read(tokenServiceProvider)!.isRefreshing) {
unawaited(ref.read(tokenServiceProvider)!.refresh());
if (!ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked) {
unawaited(ref.read(pCurrentTokenWallet)!.refresh());
}
},
child: Util.isDesktop
? ListView.separated(
itemBuilder: (context, index) {
BorderRadius? radius;
if (_transactions2.length == 1) {
if (_transactions.length == 1) {
radius = BorderRadius.circular(
Constants.size.circularBorderRadius,
);
} else if (index == _transactions2.length - 1) {
} else if (index == _transactions.length - 1) {
radius = _borderRadiusLast;
} else if (index == 0) {
radius = _borderRadiusFirst;
}
final tx = _transactions2[index];
return itemBuilder(context, tx, radius, manager.coin);
final tx = _transactions[index];
return TxListItem(
tx: tx,
coin: wallet.info.coin,
radius: radius,
);
},
separatorBuilder: (context, index) {
return Container(
@ -275,23 +174,27 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
.background,
);
},
itemCount: _transactions2.length,
itemCount: _transactions.length,
)
: ListView.builder(
itemCount: _transactions2.length,
itemCount: _transactions.length,
itemBuilder: (context, index) {
BorderRadius? radius;
if (_transactions2.length == 1) {
if (_transactions.length == 1) {
radius = BorderRadius.circular(
Constants.size.circularBorderRadius,
);
} else if (index == _transactions2.length - 1) {
} else if (index == _transactions.length - 1) {
radius = _borderRadiusLast;
} else if (index == 0) {
radius = _borderRadiusFirst;
}
final tx = _transactions2[index];
return itemBuilder(context, tx, radius, manager.coin);
final tx = _transactions[index];
return TxListItem(
tx: tx,
coin: wallet.info.coin,
radius: radius,
);
},
),
);

View file

@ -15,23 +15,19 @@ import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/token_view/sub_widgets/token_summary.dart';
import 'package:stackwallet/pages/token_view/sub_widgets/token_transaction_list_widget.dart';
import 'package:stackwallet/pages/token_view/token_contract_details_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart';
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
import 'package:stackwallet/widgets/background.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/icon_widgets/eth_token_icon.dart';
import 'package:tuple/tuple.dart';
final tokenServiceStateProvider = StateProvider<EthTokenWallet?>((ref) => null);
final tokenServiceProvider = ChangeNotifierProvider<EthTokenWallet?>(
(ref) => ref.watch(tokenServiceStateProvider));
/// [eventBus] should only be set during testing
class TokenView extends ConsumerStatefulWidget {
const TokenView({
@ -56,7 +52,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
@override
void initState() {
initialSyncStatus = ref.read(tokenServiceProvider)!.isRefreshing
initialSyncStatus = ref.read(pCurrentTokenWallet)!.refreshMutex.isLocked
? WalletSyncStatus.syncing
: WalletSyncStatus.synced;
super.initState();
@ -105,7 +101,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
children: [
EthTokenIcon(
contractAddress: ref.watch(
tokenServiceProvider.select(
pCurrentTokenWallet.select(
(value) => value!.tokenContract.address,
),
),
@ -116,7 +112,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
),
Flexible(
child: Text(
ref.watch(tokenServiceProvider
ref.watch(pCurrentTokenWallet
.select((value) => value!.tokenContract.name)),
style: STextStyles.navBarTitle(context),
overflow: TextOverflow.ellipsis,
@ -145,7 +141,7 @@ class _TokenViewState extends ConsumerState<TokenView> {
Navigator.of(context).pushNamed(
TokenContractDetailsView.routeName,
arguments: Tuple2(
ref.watch(tokenServiceProvider
ref.watch(pCurrentTokenWallet
.select((value) => value!.tokenContract.address)),
widget.walletId,
),
@ -190,10 +186,14 @@ class _TokenViewState extends ConsumerState<TokenView> {
text: "See all",
onTap: () {
Navigator.of(context).pushNamed(
AllTransactionsView.routeName,
AllTransactionsV2View.routeName,
arguments: (
walletId: widget.walletId,
isTokens: true,
contractAddress: ref.watch(
pCurrentTokenWallet.select(
(value) => value!.tokenContract.address,
),
),
),
);
},

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