mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-21 14:48:55 +00:00
Merge pull request #713 from cypherstack/wallets_refactor_spark_integrationn
firo spark added
This commit is contained in:
commit
8a42287a77
74 changed files with 8537 additions and 2033 deletions
|
@ -53,8 +53,12 @@ class DB {
|
||||||
// firo only
|
// firo only
|
||||||
String _boxNameSetCache({required Coin coin}) =>
|
String _boxNameSetCache({required Coin coin}) =>
|
||||||
"${coin.name}_anonymitySetCache";
|
"${coin.name}_anonymitySetCache";
|
||||||
|
String _boxNameSetSparkCache({required Coin coin}) =>
|
||||||
|
"${coin.name}_anonymitySetSparkCache";
|
||||||
String _boxNameUsedSerialsCache({required Coin coin}) =>
|
String _boxNameUsedSerialsCache({required Coin coin}) =>
|
||||||
"${coin.name}_usedSerialsCache";
|
"${coin.name}_usedSerialsCache";
|
||||||
|
String _boxNameSparkUsedCoinsTagsCache({required Coin coin}) =>
|
||||||
|
"${coin.name}_sparkUsedCoinsTagsCache";
|
||||||
|
|
||||||
Box<NodeModel>? _boxNodeModels;
|
Box<NodeModel>? _boxNodeModels;
|
||||||
Box<NodeModel>? _boxPrimaryNodes;
|
Box<NodeModel>? _boxPrimaryNodes;
|
||||||
|
@ -75,7 +79,9 @@ class DB {
|
||||||
|
|
||||||
final Map<Coin, Box<dynamic>> _txCacheBoxes = {};
|
final Map<Coin, Box<dynamic>> _txCacheBoxes = {};
|
||||||
final Map<Coin, Box<dynamic>> _setCacheBoxes = {};
|
final Map<Coin, Box<dynamic>> _setCacheBoxes = {};
|
||||||
|
final Map<Coin, Box<dynamic>> _setSparkCacheBoxes = {};
|
||||||
final Map<Coin, Box<dynamic>> _usedSerialsCacheBoxes = {};
|
final Map<Coin, Box<dynamic>> _usedSerialsCacheBoxes = {};
|
||||||
|
final Map<Coin, Box<dynamic>> _getSparkUsedCoinsTagsCacheBoxes = {};
|
||||||
|
|
||||||
// exposed for monero
|
// exposed for monero
|
||||||
Box<xmr.WalletInfo> get moneroWalletInfoBox => _walletInfoSource!;
|
Box<xmr.WalletInfo> get moneroWalletInfoBox => _walletInfoSource!;
|
||||||
|
@ -197,6 +203,15 @@ class DB {
|
||||||
await Hive.openBox<dynamic>(_boxNameSetCache(coin: coin));
|
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 {
|
Future<void> closeAnonymitySetCacheBox({required Coin coin}) async {
|
||||||
await _setCacheBoxes[coin]?.close();
|
await _setCacheBoxes[coin]?.close();
|
||||||
}
|
}
|
||||||
|
@ -209,6 +224,16 @@ class DB {
|
||||||
await Hive.openBox<dynamic>(_boxNameUsedSerialsCache(coin: coin));
|
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 {
|
Future<void> closeUsedSerialsCacheBox({required Coin coin}) async {
|
||||||
await _usedSerialsCacheBoxes[coin]?.close();
|
await _usedSerialsCacheBoxes[coin]?.close();
|
||||||
}
|
}
|
||||||
|
@ -216,9 +241,12 @@ class DB {
|
||||||
/// Clear all cached transactions for the specified coin
|
/// Clear all cached transactions for the specified coin
|
||||||
Future<void> clearSharedTransactionCache({required Coin coin}) async {
|
Future<void> clearSharedTransactionCache({required Coin coin}) async {
|
||||||
await deleteAll<dynamic>(boxName: _boxNameTxCache(coin: coin));
|
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: _boxNameSetCache(coin: coin));
|
||||||
|
await deleteAll<dynamic>(boxName: _boxNameSetSparkCache(coin: coin));
|
||||||
await deleteAll<dynamic>(boxName: _boxNameUsedSerialsCache(coin: coin));
|
await deleteAll<dynamic>(boxName: _boxNameUsedSerialsCache(coin: coin));
|
||||||
|
await deleteAll<dynamic>(
|
||||||
|
boxName: _boxNameSparkUsedCoinsTagsCache(coin: coin));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import 'package:stackwallet/models/isar/stack_theme.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/stack_file_system.dart';
|
import 'package:stackwallet/utilities/stack_file_system.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/models/spark_coin.dart';
|
||||||
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ class MainDB {
|
||||||
LelantusCoinSchema,
|
LelantusCoinSchema,
|
||||||
WalletInfoSchema,
|
WalletInfoSchema,
|
||||||
TransactionV2Schema,
|
TransactionV2Schema,
|
||||||
|
SparkCoinSchema,
|
||||||
],
|
],
|
||||||
directory: (await StackFileSystem.applicationIsarDirectory()).path,
|
directory: (await StackFileSystem.applicationIsarDirectory()).path,
|
||||||
// inspector: kDebugMode,
|
// inspector: kDebugMode,
|
||||||
|
@ -482,6 +484,12 @@ class MainDB {
|
||||||
// .findAll();
|
// .findAll();
|
||||||
// await isar.lelantusCoins.deleteAll(lelantusCoinIds);
|
// await isar.lelantusCoins.deleteAll(lelantusCoinIds);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// spark coins
|
||||||
|
await isar.sparkCoins
|
||||||
|
.where()
|
||||||
|
.walletIdEqualToAnyLTagHash(walletId)
|
||||||
|
.deleteAll();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import 'package:stackwallet/wallets/wallet/supporting/epiccash_wallet_info_exten
|
||||||
Future<void> migrateWalletsToIsar({
|
Future<void> migrateWalletsToIsar({
|
||||||
required SecureStorageInterface secureStore,
|
required SecureStorageInterface secureStore,
|
||||||
}) async {
|
}) async {
|
||||||
|
await MainDB.instance.initMainDB();
|
||||||
final allWalletsBox = await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
|
final allWalletsBox = await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
|
||||||
|
|
||||||
final names = DB.instance
|
final names = DB.instance
|
||||||
|
|
|
@ -107,6 +107,63 @@ class CachedElectrumXClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) =>
|
String base64ToHex(String source) =>
|
||||||
base64Decode(LineSplitter.split(source).join())
|
base64Decode(LineSplitter.split(source).join())
|
||||||
.map((e) => e.toRadixString(16).padLeft(2, '0'))
|
.map((e) => e.toRadixString(16).padLeft(2, '0'))
|
||||||
|
@ -136,6 +193,7 @@ class CachedElectrumXClient {
|
||||||
|
|
||||||
result.remove("hex");
|
result.remove("hex");
|
||||||
result.remove("lelantusData");
|
result.remove("lelantusData");
|
||||||
|
result.remove("sparkData");
|
||||||
|
|
||||||
if (result["confirmations"] != null &&
|
if (result["confirmations"] != null &&
|
||||||
result["confirmations"] as int > minCacheConfirms) {
|
result["confirmations"] as int > minCacheConfirms) {
|
||||||
|
@ -198,14 +256,62 @@ class CachedElectrumXClient {
|
||||||
return resultingList;
|
return resultingList;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Failed to process CachedElectrumX.getTransaction(): $e\n$s",
|
"Failed to process CachedElectrumX.getUsedCoinSerials(): $e\n$s",
|
||||||
level: LogLevel.Error);
|
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;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all cached transactions for the specified coin
|
/// Clear all cached transactions for the specified coin
|
||||||
Future<void> clearSharedTransactionCache({required Coin coin}) async {
|
Future<void> clearSharedTransactionCache({required Coin coin}) async {
|
||||||
|
await DB.instance.clearSharedTransactionCache(coin: coin);
|
||||||
await DB.instance.closeAnonymitySetCacheBox(coin: coin);
|
await DB.instance.closeAnonymitySetCacheBox(coin: coin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import 'dart:io';
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:event_bus/event_bus.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:mutex/mutex.dart';
|
||||||
import 'package:stackwallet/electrumx_rpc/rpc.dart';
|
import 'package:stackwallet/electrumx_rpc/rpc.dart';
|
||||||
import 'package:stackwallet/exceptions/electrumx/no_such_transaction.dart';
|
import 'package:stackwallet/exceptions/electrumx/no_such_transaction.dart';
|
||||||
|
@ -467,9 +469,9 @@ class ElectrumXClient {
|
||||||
/// and the binary header as a hexadecimal string.
|
/// and the binary header as a hexadecimal string.
|
||||||
/// Ex:
|
/// Ex:
|
||||||
/// {
|
/// {
|
||||||
// "height": 520481,
|
/// "height": 520481,
|
||||||
// "hex": "00000020890208a0ae3a3892aa047c5468725846577cfcd9b512b50000000000000000005dc2b02f2d297a9064ee103036c14d678f9afc7e3d9409cf53fd58b82e938e8ecbeca05a2d2103188ce804c4"
|
/// "hex": "00000020890208a0ae3a3892aa047c5468725846577cfcd9b512b50000000000000000005dc2b02f2d297a9064ee103036c14d678f9afc7e3d9409cf53fd58b82e938e8ecbeca05a2d2103188ce804c4"
|
||||||
// }
|
/// }
|
||||||
Future<Map<String, dynamic>> getBlockHeadTip({String? requestID}) async {
|
Future<Map<String, dynamic>> getBlockHeadTip({String? requestID}) async {
|
||||||
try {
|
try {
|
||||||
final response = await request(
|
final response = await request(
|
||||||
|
@ -493,15 +495,15 @@ class ElectrumXClient {
|
||||||
///
|
///
|
||||||
/// Returns a map with server information
|
/// Returns a map with server information
|
||||||
/// Ex:
|
/// Ex:
|
||||||
// {
|
/// {
|
||||||
// "genesis_hash": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
|
/// "genesis_hash": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
|
||||||
// "hosts": {"14.3.140.101": {"tcp_port": 51001, "ssl_port": 51002}},
|
/// "hosts": {"14.3.140.101": {"tcp_port": 51001, "ssl_port": 51002}},
|
||||||
// "protocol_max": "1.0",
|
/// "protocol_max": "1.0",
|
||||||
// "protocol_min": "1.0",
|
/// "protocol_min": "1.0",
|
||||||
// "pruning": null,
|
/// "pruning": null,
|
||||||
// "server_version": "ElectrumX 1.0.17",
|
/// "server_version": "ElectrumX 1.0.17",
|
||||||
// "hash_function": "sha256"
|
/// "hash_function": "sha256"
|
||||||
// }
|
/// }
|
||||||
Future<Map<String, dynamic>> getServerFeatures({String? requestID}) async {
|
Future<Map<String, dynamic>> getServerFeatures({String? requestID}) async {
|
||||||
try {
|
try {
|
||||||
final response = await request(
|
final response = await request(
|
||||||
|
@ -567,15 +569,15 @@ class ElectrumXClient {
|
||||||
/// Returns a list of maps that contain the tx_hash and height of the tx.
|
/// Returns a list of maps that contain the tx_hash and height of the tx.
|
||||||
/// Ex:
|
/// Ex:
|
||||||
/// [
|
/// [
|
||||||
// {
|
/// {
|
||||||
// "height": 200004,
|
/// "height": 200004,
|
||||||
// "tx_hash": "acc3758bd2a26f869fcc67d48ff30b96464d476bca82c1cd6656e7d506816412"
|
/// "tx_hash": "acc3758bd2a26f869fcc67d48ff30b96464d476bca82c1cd6656e7d506816412"
|
||||||
// },
|
/// },
|
||||||
// {
|
/// {
|
||||||
// "height": 215008,
|
/// "height": 215008,
|
||||||
// "tx_hash": "f3e1bf48975b8d6060a9de8884296abb80be618dc00ae3cb2f6cee3085e09403"
|
/// "tx_hash": "f3e1bf48975b8d6060a9de8884296abb80be618dc00ae3cb2f6cee3085e09403"
|
||||||
// }
|
/// }
|
||||||
// ]
|
/// ]
|
||||||
Future<List<Map<String, dynamic>>> getHistory({
|
Future<List<Map<String, dynamic>>> getHistory({
|
||||||
required String scripthash,
|
required String scripthash,
|
||||||
String? requestID,
|
String? requestID,
|
||||||
|
@ -627,19 +629,19 @@ class ElectrumXClient {
|
||||||
/// Returns a list of maps.
|
/// Returns a list of maps.
|
||||||
/// Ex:
|
/// Ex:
|
||||||
/// [
|
/// [
|
||||||
// {
|
/// {
|
||||||
// "tx_pos": 0,
|
/// "tx_pos": 0,
|
||||||
// "value": 45318048,
|
/// "value": 45318048,
|
||||||
// "tx_hash": "9f2c45a12db0144909b5db269415f7319179105982ac70ed80d76ea79d923ebf",
|
/// "tx_hash": "9f2c45a12db0144909b5db269415f7319179105982ac70ed80d76ea79d923ebf",
|
||||||
// "height": 437146
|
/// "height": 437146
|
||||||
// },
|
/// },
|
||||||
// {
|
/// {
|
||||||
// "tx_pos": 0,
|
/// "tx_pos": 0,
|
||||||
// "value": 919195,
|
/// "value": 919195,
|
||||||
// "tx_hash": "3d2290c93436a3e964cfc2f0950174d8847b1fbe3946432c4784e168da0f019f",
|
/// "tx_hash": "3d2290c93436a3e964cfc2f0950174d8847b1fbe3946432c4784e168da0f019f",
|
||||||
// "height": 441696
|
/// "height": 441696
|
||||||
// }
|
/// }
|
||||||
// ]
|
/// ]
|
||||||
Future<List<Map<String, dynamic>>> getUTXOs({
|
Future<List<Map<String, dynamic>>> getUTXOs({
|
||||||
required String scripthash,
|
required String scripthash,
|
||||||
String? requestID,
|
String? requestID,
|
||||||
|
@ -881,7 +883,7 @@ class ElectrumXClient {
|
||||||
///
|
///
|
||||||
/// Returns blockHash (last block hash),
|
/// Returns blockHash (last block hash),
|
||||||
/// setHash (hash of current set)
|
/// setHash (hash of current set)
|
||||||
/// and mints (the list of pairs serialized coin and tx hash)
|
/// and coins (the list of pairs serialized coin and tx hash)
|
||||||
Future<Map<String, dynamic>> getSparkAnonymitySet({
|
Future<Map<String, dynamic>> getSparkAnonymitySet({
|
||||||
String coinGroupId = "1",
|
String coinGroupId = "1",
|
||||||
String startBlockHash = "",
|
String startBlockHash = "",
|
||||||
|
@ -908,7 +910,7 @@ class ElectrumXClient {
|
||||||
|
|
||||||
/// Takes [startNumber], if it is 0, we get the full set,
|
/// Takes [startNumber], if it is 0, we get the full set,
|
||||||
/// otherwise the used tags after that number
|
/// otherwise the used tags after that number
|
||||||
Future<Map<String, dynamic>> getSparkUsedCoinsTags({
|
Future<Set<String>> getSparkUsedCoinsTags({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
required int startNumber,
|
required int startNumber,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -921,7 +923,9 @@ class ElectrumXClient {
|
||||||
],
|
],
|
||||||
requestTimeout: const Duration(minutes: 2),
|
requestTimeout: const Duration(minutes: 2),
|
||||||
);
|
);
|
||||||
return Map<String, dynamic>.from(response["result"] as Map);
|
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) {
|
} catch (e) {
|
||||||
Logging.instance.log(e, level: LogLevel.Error);
|
Logging.instance.log(e, level: LogLevel.Error);
|
||||||
rethrow;
|
rethrow;
|
||||||
|
@ -984,8 +988,8 @@ class ElectrumXClient {
|
||||||
/// Returns a map with the kay "rate" that corresponds to the free rate in satoshis
|
/// Returns a map with the kay "rate" that corresponds to the free rate in satoshis
|
||||||
/// Ex:
|
/// Ex:
|
||||||
/// {
|
/// {
|
||||||
// "rate": 1000,
|
/// "rate": 1000,
|
||||||
// }
|
/// }
|
||||||
Future<Map<String, dynamic>> getFeeRate({String? requestID}) async {
|
Future<Map<String, dynamic>> getFeeRate({String? requestID}) async {
|
||||||
try {
|
try {
|
||||||
final response = await request(
|
final response = await request(
|
||||||
|
@ -1035,3 +1039,7 @@ class ElectrumXClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<String> _ffiHashTagsComputeWrapper(Set<String> base64Tags) {
|
||||||
|
return LibSpark.hashTags(base64Tags: base64Tags);
|
||||||
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@ import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/prefs.dart';
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
import 'package:stackwallet/utilities/stack_file_system.dart';
|
import 'package:stackwallet/utilities/stack_file_system.dart';
|
||||||
import 'package:stackwallet/utilities/util.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:stackwallet/widgets/crypto_notifications.dart';
|
||||||
import 'package:window_size/window_size.dart';
|
import 'package:window_size/window_size.dart';
|
||||||
|
|
||||||
|
@ -747,7 +748,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
||||||
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
|
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
// FlutterNativeSplash.remove();
|
// FlutterNativeSplash.remove();
|
||||||
if (ref.read(pWallets).hasWallets ||
|
if (ref.read(pAllWalletsInfo).isNotEmpty ||
|
||||||
ref.read(prefsChangeNotifierProvider).hasPin) {
|
ref.read(prefsChangeNotifierProvider).hasPin) {
|
||||||
// return HomeView();
|
// return HomeView();
|
||||||
|
|
||||||
|
|
|
@ -252,5 +252,7 @@ enum TransactionSubType {
|
||||||
mint, // firo specific
|
mint, // firo specific
|
||||||
join, // firo specific
|
join, // firo specific
|
||||||
ethToken, // eth token
|
ethToken, // eth token
|
||||||
cashFusion;
|
cashFusion,
|
||||||
|
sparkMint, // firo specific
|
||||||
|
sparkSpend; // firo specific
|
||||||
}
|
}
|
||||||
|
|
|
@ -365,6 +365,8 @@ const _TransactionsubTypeEnumValueMap = {
|
||||||
'join': 3,
|
'join': 3,
|
||||||
'ethToken': 4,
|
'ethToken': 4,
|
||||||
'cashFusion': 5,
|
'cashFusion': 5,
|
||||||
|
'sparkMint': 6,
|
||||||
|
'sparkSpend': 7,
|
||||||
};
|
};
|
||||||
const _TransactionsubTypeValueEnumMap = {
|
const _TransactionsubTypeValueEnumMap = {
|
||||||
0: TransactionSubType.none,
|
0: TransactionSubType.none,
|
||||||
|
@ -373,6 +375,8 @@ const _TransactionsubTypeValueEnumMap = {
|
||||||
3: TransactionSubType.join,
|
3: TransactionSubType.join,
|
||||||
4: TransactionSubType.ethToken,
|
4: TransactionSubType.ethToken,
|
||||||
5: TransactionSubType.cashFusion,
|
5: TransactionSubType.cashFusion,
|
||||||
|
6: TransactionSubType.sparkMint,
|
||||||
|
7: TransactionSubType.sparkSpend,
|
||||||
};
|
};
|
||||||
const _TransactiontypeEnumValueMap = {
|
const _TransactiontypeEnumValueMap = {
|
||||||
'outgoing': 0,
|
'outgoing': 0,
|
||||||
|
|
|
@ -46,7 +46,7 @@ class OutputV2 {
|
||||||
Map<String, dynamic> json, {
|
Map<String, dynamic> json, {
|
||||||
required bool walletOwns,
|
required bool walletOwns,
|
||||||
required int decimalPlaces,
|
required int decimalPlaces,
|
||||||
bool isECashFullAmountNotSats = false,
|
bool isFullAmountNotSats = false,
|
||||||
}) {
|
}) {
|
||||||
try {
|
try {
|
||||||
List<String> addresses = [];
|
List<String> addresses = [];
|
||||||
|
@ -61,9 +61,11 @@ class OutputV2 {
|
||||||
|
|
||||||
return OutputV2.isarCantDoRequiredInDefaultConstructor(
|
return OutputV2.isarCantDoRequiredInDefaultConstructor(
|
||||||
scriptPubKeyHex: json["scriptPubKey"]["hex"] as String,
|
scriptPubKeyHex: json["scriptPubKey"]["hex"] as String,
|
||||||
valueStringSats: parseOutputAmountString(json["value"].toString(),
|
valueStringSats: parseOutputAmountString(
|
||||||
|
json["value"].toString(),
|
||||||
decimalPlaces: decimalPlaces,
|
decimalPlaces: decimalPlaces,
|
||||||
isECashFullAmountNotSats: isECashFullAmountNotSats),
|
isFullAmountNotSats: isFullAmountNotSats,
|
||||||
|
),
|
||||||
addresses: addresses,
|
addresses: addresses,
|
||||||
walletOwns: walletOwns,
|
walletOwns: walletOwns,
|
||||||
);
|
);
|
||||||
|
@ -75,7 +77,7 @@ class OutputV2 {
|
||||||
static String parseOutputAmountString(
|
static String parseOutputAmountString(
|
||||||
String amount, {
|
String amount, {
|
||||||
required int decimalPlaces,
|
required int decimalPlaces,
|
||||||
bool isECashFullAmountNotSats = false,
|
bool isFullAmountNotSats = false,
|
||||||
}) {
|
}) {
|
||||||
final temp = Decimal.parse(amount);
|
final temp = Decimal.parse(amount);
|
||||||
if (temp < Decimal.zero) {
|
if (temp < Decimal.zero) {
|
||||||
|
@ -83,7 +85,7 @@ class OutputV2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
final String valueStringSats;
|
final String valueStringSats;
|
||||||
if (isECashFullAmountNotSats) {
|
if (isFullAmountNotSats) {
|
||||||
valueStringSats = temp.shift(decimalPlaces).toBigInt().toString();
|
valueStringSats = temp.shift(decimalPlaces).toBigInt().toString();
|
||||||
} else if (temp.isInteger) {
|
} else if (temp.isInteger) {
|
||||||
valueStringSats = temp.toString();
|
valueStringSats = temp.toString();
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
@ -6,6 +7,8 @@ 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/models/isar/models/blockchain_data/v2/output_v2.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.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';
|
part 'transaction_v2.g.dart';
|
||||||
|
|
||||||
|
@ -37,6 +40,8 @@ class TransactionV2 {
|
||||||
@enumerated
|
@enumerated
|
||||||
final TransactionSubType subType;
|
final TransactionSubType subType;
|
||||||
|
|
||||||
|
final String? otherData;
|
||||||
|
|
||||||
TransactionV2({
|
TransactionV2({
|
||||||
required this.walletId,
|
required this.walletId,
|
||||||
required this.blockHash,
|
required this.blockHash,
|
||||||
|
@ -49,6 +54,7 @@ class TransactionV2 {
|
||||||
required this.version,
|
required this.version,
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.subType,
|
required this.subType,
|
||||||
|
required this.otherData,
|
||||||
});
|
});
|
||||||
|
|
||||||
int getConfirmations(int currentChainHeight) {
|
int getConfirmations(int currentChainHeight) {
|
||||||
|
@ -71,7 +77,7 @@ class TransactionV2 {
|
||||||
return Amount(rawValue: inSum - outSum, fractionDigits: coin.decimals);
|
return Amount(rawValue: inSum - outSum, fractionDigits: coin.decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
Amount getAmountReceivedThisWallet({required Coin coin}) {
|
Amount getAmountReceivedInThisWallet({required Coin coin}) {
|
||||||
final outSum = outputs
|
final outSum = outputs
|
||||||
.where((e) => e.walletOwns)
|
.where((e) => e.walletOwns)
|
||||||
.fold(BigInt.zero, (p, e) => p + e.value);
|
.fold(BigInt.zero, (p, e) => p + e.value);
|
||||||
|
@ -79,12 +85,28 @@ class TransactionV2 {
|
||||||
return Amount(rawValue: outSum, fractionDigits: coin.decimals);
|
return Amount(rawValue: outSum, fractionDigits: coin.decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Amount getAmountSparkSelfMinted({required Coin coin}) {
|
||||||
|
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: coin.decimals);
|
||||||
|
}
|
||||||
|
|
||||||
Amount getAmountSentFromThisWallet({required Coin coin}) {
|
Amount getAmountSentFromThisWallet({required Coin coin}) {
|
||||||
final inSum = inputs
|
final inSum = inputs
|
||||||
.where((e) => e.walletOwns)
|
.where((e) => e.walletOwns)
|
||||||
.fold(BigInt.zero, (p, e) => p + e.value);
|
.fold(BigInt.zero, (p, e) => p + e.value);
|
||||||
|
|
||||||
return Amount(rawValue: inSum, fractionDigits: coin.decimals);
|
return Amount(
|
||||||
|
rawValue: inSum,
|
||||||
|
fractionDigits: coin.decimals,
|
||||||
|
) -
|
||||||
|
getAmountReceivedInThisWallet(
|
||||||
|
coin: coin,
|
||||||
|
) -
|
||||||
|
getFee(coin: coin);
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> associatedAddresses() => {
|
Set<String> associatedAddresses() => {
|
||||||
|
@ -92,6 +114,82 @@ class TransactionV2 {
|
||||||
...outputs.map((e) => e.addresses).expand((e) => e),
|
...outputs.map((e) => e.addresses).expand((e) => e),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Amount? getAnonFee() {
|
||||||
|
try {
|
||||||
|
final map = jsonDecode(otherData!) as Map;
|
||||||
|
return Amount.fromSerializedJsonString(map["anonFees"] 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 (coin == Coin.epicCash) {
|
||||||
|
// if (_transaction.isCancelled) {
|
||||||
|
// return "Cancelled";
|
||||||
|
// } else if (type == TransactionType.incoming) {
|
||||||
|
// if (isConfirmed(height, minConfirms)) {
|
||||||
|
// return "Received";
|
||||||
|
// } else {
|
||||||
|
// if (_transaction.numberOfMessages == 1) {
|
||||||
|
// return "Receiving (waiting for sender)";
|
||||||
|
// } else if ((_transaction.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(height, minConfirms)) {
|
||||||
|
// return "Sent (confirmed)";
|
||||||
|
// } else {
|
||||||
|
// if (_transaction.numberOfMessages == 1) {
|
||||||
|
// return "Sending (waiting for receiver)";
|
||||||
|
// } else if ((_transaction.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'TransactionV2(\n'
|
return 'TransactionV2(\n'
|
||||||
|
@ -106,6 +204,7 @@ class TransactionV2 {
|
||||||
' version: $version,\n'
|
' version: $version,\n'
|
||||||
' inputs: $inputs,\n'
|
' inputs: $inputs,\n'
|
||||||
' outputs: $outputs,\n'
|
' outputs: $outputs,\n'
|
||||||
|
' otherData: $otherData,\n'
|
||||||
')';
|
')';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,41 +38,46 @@ const TransactionV2Schema = CollectionSchema(
|
||||||
type: IsarType.objectList,
|
type: IsarType.objectList,
|
||||||
target: r'InputV2',
|
target: r'InputV2',
|
||||||
),
|
),
|
||||||
r'outputs': PropertySchema(
|
r'otherData': PropertySchema(
|
||||||
id: 4,
|
id: 4,
|
||||||
|
name: r'otherData',
|
||||||
|
type: IsarType.string,
|
||||||
|
),
|
||||||
|
r'outputs': PropertySchema(
|
||||||
|
id: 5,
|
||||||
name: r'outputs',
|
name: r'outputs',
|
||||||
type: IsarType.objectList,
|
type: IsarType.objectList,
|
||||||
target: r'OutputV2',
|
target: r'OutputV2',
|
||||||
),
|
),
|
||||||
r'subType': PropertySchema(
|
r'subType': PropertySchema(
|
||||||
id: 5,
|
id: 6,
|
||||||
name: r'subType',
|
name: r'subType',
|
||||||
type: IsarType.byte,
|
type: IsarType.byte,
|
||||||
enumMap: _TransactionV2subTypeEnumValueMap,
|
enumMap: _TransactionV2subTypeEnumValueMap,
|
||||||
),
|
),
|
||||||
r'timestamp': PropertySchema(
|
r'timestamp': PropertySchema(
|
||||||
id: 6,
|
id: 7,
|
||||||
name: r'timestamp',
|
name: r'timestamp',
|
||||||
type: IsarType.long,
|
type: IsarType.long,
|
||||||
),
|
),
|
||||||
r'txid': PropertySchema(
|
r'txid': PropertySchema(
|
||||||
id: 7,
|
id: 8,
|
||||||
name: r'txid',
|
name: r'txid',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'type': PropertySchema(
|
r'type': PropertySchema(
|
||||||
id: 8,
|
id: 9,
|
||||||
name: r'type',
|
name: r'type',
|
||||||
type: IsarType.byte,
|
type: IsarType.byte,
|
||||||
enumMap: _TransactionV2typeEnumValueMap,
|
enumMap: _TransactionV2typeEnumValueMap,
|
||||||
),
|
),
|
||||||
r'version': PropertySchema(
|
r'version': PropertySchema(
|
||||||
id: 9,
|
id: 10,
|
||||||
name: r'version',
|
name: r'version',
|
||||||
type: IsarType.long,
|
type: IsarType.long,
|
||||||
),
|
),
|
||||||
r'walletId': PropertySchema(
|
r'walletId': PropertySchema(
|
||||||
id: 10,
|
id: 11,
|
||||||
name: r'walletId',
|
name: r'walletId',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
)
|
)
|
||||||
|
@ -161,6 +166,12 @@ int _transactionV2EstimateSize(
|
||||||
bytesCount += InputV2Schema.estimateSize(value, offsets, allOffsets);
|
bytesCount += InputV2Schema.estimateSize(value, offsets, allOffsets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
final value = object.otherData;
|
||||||
|
if (value != null) {
|
||||||
|
bytesCount += 3 + value.length * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
bytesCount += 3 + object.outputs.length * 3;
|
bytesCount += 3 + object.outputs.length * 3;
|
||||||
{
|
{
|
||||||
final offsets = allOffsets[OutputV2]!;
|
final offsets = allOffsets[OutputV2]!;
|
||||||
|
@ -189,18 +200,19 @@ void _transactionV2Serialize(
|
||||||
InputV2Schema.serialize,
|
InputV2Schema.serialize,
|
||||||
object.inputs,
|
object.inputs,
|
||||||
);
|
);
|
||||||
|
writer.writeString(offsets[4], object.otherData);
|
||||||
writer.writeObjectList<OutputV2>(
|
writer.writeObjectList<OutputV2>(
|
||||||
offsets[4],
|
offsets[5],
|
||||||
allOffsets,
|
allOffsets,
|
||||||
OutputV2Schema.serialize,
|
OutputV2Schema.serialize,
|
||||||
object.outputs,
|
object.outputs,
|
||||||
);
|
);
|
||||||
writer.writeByte(offsets[5], object.subType.index);
|
writer.writeByte(offsets[6], object.subType.index);
|
||||||
writer.writeLong(offsets[6], object.timestamp);
|
writer.writeLong(offsets[7], object.timestamp);
|
||||||
writer.writeString(offsets[7], object.txid);
|
writer.writeString(offsets[8], object.txid);
|
||||||
writer.writeByte(offsets[8], object.type.index);
|
writer.writeByte(offsets[9], object.type.index);
|
||||||
writer.writeLong(offsets[9], object.version);
|
writer.writeLong(offsets[10], object.version);
|
||||||
writer.writeString(offsets[10], object.walletId);
|
writer.writeString(offsets[11], object.walletId);
|
||||||
}
|
}
|
||||||
|
|
||||||
TransactionV2 _transactionV2Deserialize(
|
TransactionV2 _transactionV2Deserialize(
|
||||||
|
@ -220,22 +232,23 @@ TransactionV2 _transactionV2Deserialize(
|
||||||
InputV2(),
|
InputV2(),
|
||||||
) ??
|
) ??
|
||||||
[],
|
[],
|
||||||
|
otherData: reader.readStringOrNull(offsets[4]),
|
||||||
outputs: reader.readObjectList<OutputV2>(
|
outputs: reader.readObjectList<OutputV2>(
|
||||||
offsets[4],
|
offsets[5],
|
||||||
OutputV2Schema.deserialize,
|
OutputV2Schema.deserialize,
|
||||||
allOffsets,
|
allOffsets,
|
||||||
OutputV2(),
|
OutputV2(),
|
||||||
) ??
|
) ??
|
||||||
[],
|
[],
|
||||||
subType:
|
subType:
|
||||||
_TransactionV2subTypeValueEnumMap[reader.readByteOrNull(offsets[5])] ??
|
_TransactionV2subTypeValueEnumMap[reader.readByteOrNull(offsets[6])] ??
|
||||||
TransactionSubType.none,
|
TransactionSubType.none,
|
||||||
timestamp: reader.readLong(offsets[6]),
|
timestamp: reader.readLong(offsets[7]),
|
||||||
txid: reader.readString(offsets[7]),
|
txid: reader.readString(offsets[8]),
|
||||||
type: _TransactionV2typeValueEnumMap[reader.readByteOrNull(offsets[8])] ??
|
type: _TransactionV2typeValueEnumMap[reader.readByteOrNull(offsets[9])] ??
|
||||||
TransactionType.outgoing,
|
TransactionType.outgoing,
|
||||||
version: reader.readLong(offsets[9]),
|
version: reader.readLong(offsets[10]),
|
||||||
walletId: reader.readString(offsets[10]),
|
walletId: reader.readString(offsets[11]),
|
||||||
);
|
);
|
||||||
object.id = id;
|
object.id = id;
|
||||||
return object;
|
return object;
|
||||||
|
@ -263,6 +276,8 @@ P _transactionV2DeserializeProp<P>(
|
||||||
) ??
|
) ??
|
||||||
[]) as P;
|
[]) as P;
|
||||||
case 4:
|
case 4:
|
||||||
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
|
case 5:
|
||||||
return (reader.readObjectList<OutputV2>(
|
return (reader.readObjectList<OutputV2>(
|
||||||
offset,
|
offset,
|
||||||
OutputV2Schema.deserialize,
|
OutputV2Schema.deserialize,
|
||||||
|
@ -270,20 +285,20 @@ P _transactionV2DeserializeProp<P>(
|
||||||
OutputV2(),
|
OutputV2(),
|
||||||
) ??
|
) ??
|
||||||
[]) as P;
|
[]) as P;
|
||||||
case 5:
|
case 6:
|
||||||
return (_TransactionV2subTypeValueEnumMap[
|
return (_TransactionV2subTypeValueEnumMap[
|
||||||
reader.readByteOrNull(offset)] ??
|
reader.readByteOrNull(offset)] ??
|
||||||
TransactionSubType.none) as P;
|
TransactionSubType.none) as P;
|
||||||
case 6:
|
|
||||||
return (reader.readLong(offset)) as P;
|
|
||||||
case 7:
|
case 7:
|
||||||
return (reader.readString(offset)) as P;
|
return (reader.readLong(offset)) as P;
|
||||||
case 8:
|
case 8:
|
||||||
|
return (reader.readString(offset)) as P;
|
||||||
|
case 9:
|
||||||
return (_TransactionV2typeValueEnumMap[reader.readByteOrNull(offset)] ??
|
return (_TransactionV2typeValueEnumMap[reader.readByteOrNull(offset)] ??
|
||||||
TransactionType.outgoing) as P;
|
TransactionType.outgoing) as P;
|
||||||
case 9:
|
|
||||||
return (reader.readLong(offset)) as P;
|
|
||||||
case 10:
|
case 10:
|
||||||
|
return (reader.readLong(offset)) as P;
|
||||||
|
case 11:
|
||||||
return (reader.readString(offset)) as P;
|
return (reader.readString(offset)) as P;
|
||||||
default:
|
default:
|
||||||
throw IsarError('Unknown property with id $propertyId');
|
throw IsarError('Unknown property with id $propertyId');
|
||||||
|
@ -297,6 +312,8 @@ const _TransactionV2subTypeEnumValueMap = {
|
||||||
'join': 3,
|
'join': 3,
|
||||||
'ethToken': 4,
|
'ethToken': 4,
|
||||||
'cashFusion': 5,
|
'cashFusion': 5,
|
||||||
|
'sparkMint': 6,
|
||||||
|
'sparkSpend': 7,
|
||||||
};
|
};
|
||||||
const _TransactionV2subTypeValueEnumMap = {
|
const _TransactionV2subTypeValueEnumMap = {
|
||||||
0: TransactionSubType.none,
|
0: TransactionSubType.none,
|
||||||
|
@ -305,6 +322,8 @@ const _TransactionV2subTypeValueEnumMap = {
|
||||||
3: TransactionSubType.join,
|
3: TransactionSubType.join,
|
||||||
4: TransactionSubType.ethToken,
|
4: TransactionSubType.ethToken,
|
||||||
5: TransactionSubType.cashFusion,
|
5: TransactionSubType.cashFusion,
|
||||||
|
6: TransactionSubType.sparkMint,
|
||||||
|
7: TransactionSubType.sparkSpend,
|
||||||
};
|
};
|
||||||
const _TransactionV2typeEnumValueMap = {
|
const _TransactionV2typeEnumValueMap = {
|
||||||
'outgoing': 0,
|
'outgoing': 0,
|
||||||
|
@ -1244,6 +1263,160 @@ extension TransactionV2QueryFilter
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataIsNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const FilterCondition.isNull(
|
||||||
|
property: r'otherData',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataIsNotNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||||
|
property: r'otherData',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataEqualTo(
|
||||||
|
String? value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.equalTo(
|
||||||
|
property: r'otherData',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataGreaterThan(
|
||||||
|
String? value, {
|
||||||
|
bool include = false,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||||
|
include: include,
|
||||||
|
property: r'otherData',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataLessThan(
|
||||||
|
String? value, {
|
||||||
|
bool include = false,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.lessThan(
|
||||||
|
include: include,
|
||||||
|
property: r'otherData',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataBetween(
|
||||||
|
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'otherData',
|
||||||
|
lower: lower,
|
||||||
|
includeLower: includeLower,
|
||||||
|
upper: upper,
|
||||||
|
includeUpper: includeUpper,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataStartsWith(
|
||||||
|
String value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.startsWith(
|
||||||
|
property: r'otherData',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataEndsWith(
|
||||||
|
String value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.endsWith(
|
||||||
|
property: r'otherData',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataContains(String value, {bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.contains(
|
||||||
|
property: r'otherData',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataMatches(String pattern, {bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.matches(
|
||||||
|
property: r'otherData',
|
||||||
|
wildcard: pattern,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataIsEmpty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.equalTo(
|
||||||
|
property: r'otherData',
|
||||||
|
value: '',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
|
otherDataIsNotEmpty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||||
|
property: r'otherData',
|
||||||
|
value: '',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
QueryBuilder<TransactionV2, TransactionV2, QAfterFilterCondition>
|
||||||
outputsLengthEqualTo(int length) {
|
outputsLengthEqualTo(int length) {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
@ -1887,6 +2060,19 @@ extension TransactionV2QuerySortBy
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> sortByOtherData() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'otherData', Sort.asc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy>
|
||||||
|
sortByOtherDataDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'otherData', Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> sortBySubType() {
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> sortBySubType() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addSortBy(r'subType', Sort.asc);
|
return query.addSortBy(r'subType', Sort.asc);
|
||||||
|
@ -2013,6 +2199,19 @@ extension TransactionV2QuerySortThenBy
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> thenByOtherData() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'otherData', Sort.asc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy>
|
||||||
|
thenByOtherDataDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'otherData', Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> thenBySubType() {
|
QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> thenBySubType() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addSortBy(r'subType', Sort.asc);
|
return query.addSortBy(r'subType', Sort.asc);
|
||||||
|
@ -2110,6 +2309,13 @@ extension TransactionV2QueryWhereDistinct
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, TransactionV2, QDistinct> distinctByOtherData(
|
||||||
|
{bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addDistinctBy(r'otherData', caseSensitive: caseSensitive);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, TransactionV2, QDistinct> distinctBySubType() {
|
QueryBuilder<TransactionV2, TransactionV2, QDistinct> distinctBySubType() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addDistinctBy(r'subType');
|
return query.addDistinctBy(r'subType');
|
||||||
|
@ -2182,6 +2388,12 @@ extension TransactionV2QueryProperty
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<TransactionV2, String?, QQueryOperations> otherDataProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addPropertyName(r'otherData');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<TransactionV2, List<OutputV2>, QQueryOperations>
|
QueryBuilder<TransactionV2, List<OutputV2>, QQueryOperations>
|
||||||
outputsProperty() {
|
outputsProperty() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
|
|
@ -126,7 +126,7 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
|
||||||
void initState() {
|
void initState() {
|
||||||
_searchFieldController = TextEditingController();
|
_searchFieldController = TextEditingController();
|
||||||
_searchFocusNode = FocusNode();
|
_searchFocusNode = FocusNode();
|
||||||
_coinsTestnet.remove(Coin.firoTestNet);
|
// _coinsTestnet.remove(Coin.firoTestNet);
|
||||||
if (Platform.isWindows) {
|
if (Platform.isWindows) {
|
||||||
_coins.remove(Coin.monero);
|
_coins.remove(Coin.monero);
|
||||||
_coins.remove(Coin.wownero);
|
_coins.remove(Coin.wownero);
|
||||||
|
|
|
@ -352,6 +352,16 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
||||||
data: address.derivationPath!.value,
|
data: address.derivationPath!.value,
|
||||||
button: Container(),
|
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(
|
const _Div(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
|
|
|
@ -10,15 +10,18 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||||
import 'package:stackwallet/notifications/show_flush_bar.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/addresses/wallet_addresses_view.dart';
|
||||||
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_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/providers/providers.dart';
|
||||||
import 'package:stackwallet/route_generator.dart';
|
import 'package:stackwallet/route_generator.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
@ -30,7 +33,9 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.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/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/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/app_bar_icon_button.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||||
|
@ -58,6 +63,11 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
||||||
late final Coin coin;
|
late final Coin coin;
|
||||||
late final String walletId;
|
late final String walletId;
|
||||||
late final ClipboardInterface clipboard;
|
late final ClipboardInterface clipboard;
|
||||||
|
late final bool supportsSpark;
|
||||||
|
|
||||||
|
String? _sparkAddress;
|
||||||
|
String? _qrcodeContent;
|
||||||
|
bool _showSparkAddress = true;
|
||||||
|
|
||||||
Future<void> generateNewAddress() async {
|
Future<void> generateNewAddress() async {
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
|
@ -96,23 +106,106 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
walletId = widget.walletId;
|
walletId = widget.walletId;
|
||||||
coin = ref.read(pWalletCoin(walletId));
|
coin = ref.read(pWalletCoin(walletId));
|
||||||
clipboard = widget.clipboard;
|
clipboard = widget.clipboard;
|
||||||
|
supportsSpark = ref.read(pWallets).getWallet(walletId) is SparkInterface;
|
||||||
|
|
||||||
|
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(() {
|
||||||
|
_sparkAddress = event?.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_streamSub?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
debugPrint("BUILD: $runtimeType");
|
debugPrint("BUILD: $runtimeType");
|
||||||
|
|
||||||
final receivingAddress = ref.watch(pWalletReceivingAddress(walletId));
|
|
||||||
|
|
||||||
final ticker = widget.tokenContract?.symbol ?? coin.ticker;
|
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(
|
return Background(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||||
|
@ -225,11 +318,166 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
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(
|
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: () {
|
onTap: () {
|
||||||
HapticFeedback.lightImpact();
|
HapticFeedback.lightImpact();
|
||||||
clipboard.setData(
|
clipboard.setData(
|
||||||
ClipboardData(text: receivingAddress),
|
ClipboardData(
|
||||||
|
text:
|
||||||
|
ref.watch(pWalletReceivingAddress(walletId))),
|
||||||
);
|
);
|
||||||
showFloatingFlushBar(
|
showFloatingFlushBar(
|
||||||
type: FlushBarType.info,
|
type: FlushBarType.info,
|
||||||
|
@ -276,7 +524,8 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
receivingAddress,
|
ref.watch(
|
||||||
|
pWalletReceivingAddress(walletId)),
|
||||||
style: STextStyles.itemSubtitle12(context),
|
style: STextStyles.itemSubtitle12(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -286,25 +535,22 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (coin != Coin.epicCash &&
|
),
|
||||||
coin != Coin.ethereum &&
|
if (ref.watch(pWallets
|
||||||
coin != Coin.banano &&
|
.select((value) => value.getWallet(walletId)))
|
||||||
coin != Coin.nano &&
|
is MultiAddressInterface ||
|
||||||
coin != Coin.stellar &&
|
supportsSpark)
|
||||||
coin != Coin.stellarTestnet &&
|
|
||||||
coin != Coin.tezos)
|
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
if (coin != Coin.epicCash &&
|
if (ref.watch(pWallets
|
||||||
coin != Coin.ethereum &&
|
.select((value) => value.getWallet(walletId)))
|
||||||
coin != Coin.banano &&
|
is MultiAddressInterface ||
|
||||||
coin != Coin.nano &&
|
supportsSpark)
|
||||||
coin != Coin.stellar &&
|
|
||||||
coin != Coin.stellarTestnet &&
|
|
||||||
coin != Coin.tezos)
|
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: generateNewAddress,
|
onPressed: supportsSpark && _showSparkAddress
|
||||||
|
? generateNewSparkAddress
|
||||||
|
: generateNewAddress,
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.getSecondaryEnabledButtonStyle(context),
|
.getSecondaryEnabledButtonStyle(context),
|
||||||
|
@ -328,7 +574,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
||||||
QrImageView(
|
QrImageView(
|
||||||
data: AddressUtils.buildUriString(
|
data: AddressUtils.buildUriString(
|
||||||
coin,
|
coin,
|
||||||
receivingAddress,
|
_qrcodeContent ?? "",
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
size: MediaQuery.of(context).size.width / 2,
|
size: MediaQuery.of(context).size.width / 2,
|
||||||
|
@ -347,7 +593,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
||||||
RouteGenerator.useMaterialPageRoute,
|
RouteGenerator.useMaterialPageRoute,
|
||||||
builder: (_) => GenerateUriQrCodeView(
|
builder: (_) => GenerateUriQrCodeView(
|
||||||
coin: coin,
|
coin: coin,
|
||||||
receivingAddress: receivingAddress,
|
receivingAddress: _qrcodeContent ?? "",
|
||||||
),
|
),
|
||||||
settings: const RouteSettings(
|
settings: const RouteSettings(
|
||||||
name: GenerateUriQrCodeView.routeName,
|
name: GenerateUriQrCodeView.routeName,
|
||||||
|
|
|
@ -120,7 +120,7 @@ class _ConfirmTransactionViewState
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
late String txid;
|
final List<String> txids = [];
|
||||||
Future<TxData> txDataFuture;
|
Future<TxData> txDataFuture;
|
||||||
|
|
||||||
final note = noteController.text;
|
final note = noteController.text;
|
||||||
|
@ -140,10 +140,25 @@ class _ConfirmTransactionViewState
|
||||||
} else if (widget.isPaynymTransaction) {
|
} else if (widget.isPaynymTransaction) {
|
||||||
txDataFuture = wallet.confirmSend(txData: widget.txData);
|
txDataFuture = wallet.confirmSend(txData: widget.txData);
|
||||||
} else {
|
} else {
|
||||||
if (wallet is FiroWallet &&
|
if (wallet is FiroWallet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state ==
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
"Private") {
|
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);
|
txDataFuture = wallet.confirmSendLelantus(txData: widget.txData);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FiroType.spark:
|
||||||
|
txDataFuture = wallet.confirmSendSpark(txData: widget.txData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (coin == Coin.epicCash) {
|
if (coin == Coin.epicCash) {
|
||||||
txDataFuture = wallet.confirmSend(
|
txDataFuture = wallet.confirmSend(
|
||||||
|
@ -165,10 +180,16 @@ class _ConfirmTransactionViewState
|
||||||
sendProgressController.triggerSuccess?.call();
|
sendProgressController.triggerSuccess?.call();
|
||||||
await Future<void>.delayed(const Duration(seconds: 5));
|
await Future<void>.delayed(const Duration(seconds: 5));
|
||||||
|
|
||||||
txid = (results.first as TxData).txid!;
|
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);
|
ref.refresh(desktopUseUTXOs);
|
||||||
|
|
||||||
// save note
|
// save note
|
||||||
|
for (final txid in txids) {
|
||||||
await ref.read(mainDBProvider).putTransactionNote(
|
await ref.read(mainDBProvider).putTransactionNote(
|
||||||
TransactionNote(
|
TransactionNote(
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
|
@ -176,6 +197,7 @@ class _ConfirmTransactionViewState
|
||||||
value: note,
|
value: note,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (widget.isTokenTx) {
|
if (widget.isTokenTx) {
|
||||||
unawaited(ref.read(tokenServiceProvider)!.refresh());
|
unawaited(ref.read(tokenServiceProvider)!.refresh());
|
||||||
|
@ -233,10 +255,14 @@ class _ConfirmTransactionViewState
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
Text(
|
Flexible(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: SelectableText(
|
||||||
e.toString(),
|
e.toString(),
|
||||||
style: STextStyles.smallMed14(context),
|
style: STextStyles.smallMed14(context),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 56,
|
height: 56,
|
||||||
),
|
),
|
||||||
|
@ -319,6 +345,48 @@ class _ConfirmTransactionViewState
|
||||||
} else {
|
} else {
|
||||||
unit = coin.ticker;
|
unit = coin.ticker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Amount? fee;
|
||||||
|
final Amount amount;
|
||||||
|
|
||||||
|
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);
|
||||||
|
amount = widget.txData.sparkMints!
|
||||||
|
.map((e) => e.amountSpark!)
|
||||||
|
.reduce((value, element) => value += element);
|
||||||
|
} else {
|
||||||
|
fee = widget.txData.fee;
|
||||||
|
amount = widget.txData.amount!;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FiroType.lelantus:
|
||||||
|
fee = widget.txData.fee;
|
||||||
|
amount = widget.txData.amount!;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FiroType.spark:
|
||||||
|
fee = widget.txData.fee;
|
||||||
|
amount = (widget.txData.amount ??
|
||||||
|
Amount.zeroWith(
|
||||||
|
fractionDigits: wallet.cryptoCurrency.fractionDigits)) +
|
||||||
|
(widget.txData.amountSpark ??
|
||||||
|
Amount.zeroWith(
|
||||||
|
fractionDigits: wallet.cryptoCurrency.fractionDigits));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fee = widget.txData.fee;
|
||||||
|
amount = widget.txData.amount!;
|
||||||
|
}
|
||||||
|
|
||||||
return ConditionalParent(
|
return ConditionalParent(
|
||||||
condition: !isDesktop,
|
condition: !isDesktop,
|
||||||
builder: (child) => Background(
|
builder: (child) => Background(
|
||||||
|
@ -424,7 +492,8 @@ class _ConfirmTransactionViewState
|
||||||
Text(
|
Text(
|
||||||
widget.isPaynymTransaction
|
widget.isPaynymTransaction
|
||||||
? widget.txData.paynymAccountLite!.nymName
|
? widget.txData.paynymAccountLite!.nymName
|
||||||
: widget.txData.recipients!.first.address,
|
: widget.txData.recipients?.first.address ??
|
||||||
|
widget.txData.sparkRecipients!.first.address,
|
||||||
style: STextStyles.itemSubtitle12(context),
|
style: STextStyles.itemSubtitle12(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -443,7 +512,7 @@ class _ConfirmTransactionViewState
|
||||||
),
|
),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
ref.watch(pAmountFormatter(coin)).format(
|
ref.watch(pAmountFormatter(coin)).format(
|
||||||
widget.txData.amount!,
|
amount,
|
||||||
ethContract: ref
|
ethContract: ref
|
||||||
.watch(tokenServiceProvider)
|
.watch(tokenServiceProvider)
|
||||||
?.tokenContract,
|
?.tokenContract,
|
||||||
|
@ -468,9 +537,7 @@ class _ConfirmTransactionViewState
|
||||||
style: STextStyles.smallMed12(context),
|
style: STextStyles.smallMed12(context),
|
||||||
),
|
),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
ref
|
ref.watch(pAmountFormatter(coin)).format(fee!),
|
||||||
.watch(pAmountFormatter(coin))
|
|
||||||
.format(widget.txData.fee!),
|
|
||||||
style: STextStyles.itemSubtitle12(context),
|
style: STextStyles.itemSubtitle12(context),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
),
|
),
|
||||||
|
@ -494,7 +561,7 @@ class _ConfirmTransactionViewState
|
||||||
height: 4,
|
height: 4,
|
||||||
),
|
),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
"~${widget.txData.fee!.raw.toInt() ~/ widget.txData.vSize!}",
|
"~${fee!.raw.toInt() ~/ widget.txData.vSize!}",
|
||||||
style: STextStyles.itemSubtitle12(context),
|
style: STextStyles.itemSubtitle12(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -625,7 +692,6 @@ class _ConfirmTransactionViewState
|
||||||
),
|
),
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final amount = widget.txData.amount!;
|
|
||||||
final externalCalls = ref.watch(
|
final externalCalls = ref.watch(
|
||||||
prefsChangeNotifierProvider.select(
|
prefsChangeNotifierProvider.select(
|
||||||
(value) => value.externalCalls));
|
(value) => value.externalCalls));
|
||||||
|
@ -723,9 +789,12 @@ class _ConfirmTransactionViewState
|
||||||
height: 2,
|
height: 2,
|
||||||
),
|
),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
|
// TODO: [prio=high] spark transaction specifics - better handling
|
||||||
widget.isPaynymTransaction
|
widget.isPaynymTransaction
|
||||||
? widget.txData.paynymAccountLite!.nymName
|
? widget.txData.paynymAccountLite!.nymName
|
||||||
: widget.txData.recipients!.first.address,
|
: widget.txData.recipients?.first.address ??
|
||||||
|
widget.txData.sparkRecipients!.first
|
||||||
|
.address,
|
||||||
style: STextStyles.desktopTextExtraExtraSmall(
|
style: STextStyles.desktopTextExtraExtraSmall(
|
||||||
context)
|
context)
|
||||||
.copyWith(
|
.copyWith(
|
||||||
|
@ -759,24 +828,15 @@ class _ConfirmTransactionViewState
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 2,
|
height: 2,
|
||||||
),
|
),
|
||||||
Builder(
|
SelectableText(
|
||||||
builder: (context) {
|
ref.watch(pAmountFormatter(coin)).format(fee!),
|
||||||
final fee = widget.txData.fee!;
|
style: STextStyles.desktopTextExtraExtraSmall(
|
||||||
|
|
||||||
return SelectableText(
|
|
||||||
ref
|
|
||||||
.watch(pAmountFormatter(coin))
|
|
||||||
.format(fee),
|
|
||||||
style:
|
|
||||||
STextStyles.desktopTextExtraExtraSmall(
|
|
||||||
context)
|
context)
|
||||||
.copyWith(
|
.copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.textDark,
|
.textDark,
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -981,15 +1041,9 @@ class _ConfirmTransactionViewState
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.textFieldDefaultBG,
|
.textFieldDefaultBG,
|
||||||
child: Builder(
|
child: SelectableText(
|
||||||
builder: (context) {
|
ref.watch(pAmountFormatter(coin)).format(fee!),
|
||||||
final fee = widget.txData.fee!;
|
|
||||||
|
|
||||||
return SelectableText(
|
|
||||||
ref.watch(pAmountFormatter(coin)).format(fee),
|
|
||||||
style: STextStyles.itemSubtitle(context),
|
style: STextStyles.itemSubtitle(context),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1025,7 +1079,7 @@ class _ConfirmTransactionViewState
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.textFieldDefaultBG,
|
.textFieldDefaultBG,
|
||||||
child: SelectableText(
|
child: SelectableText(
|
||||||
"~${widget.txData.fee!.raw.toInt() ~/ widget.txData.vSize!}",
|
"~${fee!.raw.toInt() ~/ widget.txData.vSize!}",
|
||||||
style: STextStyles.itemSubtitle(context),
|
style: STextStyles.itemSubtitle(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1069,14 +1123,8 @@ class _ConfirmTransactionViewState
|
||||||
.textConfirmTotalAmount,
|
.textConfirmTotalAmount,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Builder(builder: (context) {
|
SelectableText(
|
||||||
final fee = widget.txData.fee!;
|
ref.watch(pAmountFormatter(coin)).format(amount + fee!),
|
||||||
|
|
||||||
final amount = widget.txData.amount!;
|
|
||||||
return SelectableText(
|
|
||||||
ref
|
|
||||||
.watch(pAmountFormatter(coin))
|
|
||||||
.format(amount + fee),
|
|
||||||
style: isDesktop
|
style: isDesktop
|
||||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||||
.copyWith(
|
.copyWith(
|
||||||
|
@ -1090,8 +1138,7 @@ class _ConfirmTransactionViewState
|
||||||
.textConfirmTotalAmount,
|
.textConfirmTotalAmount,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
);
|
),
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -101,9 +101,9 @@ class _FiroBalanceSelectionSheetState
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final state =
|
final state =
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state;
|
ref.read(publicPrivateBalanceStateProvider.state).state;
|
||||||
if (state != "Private") {
|
if (state != FiroType.spark) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
"Private";
|
FiroType.spark;
|
||||||
}
|
}
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
|
@ -122,7 +122,7 @@ class _FiroBalanceSelectionSheetState
|
||||||
activeColor: Theme.of(context)
|
activeColor: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.radioButtonIconEnabled,
|
.radioButtonIconEnabled,
|
||||||
value: "Private",
|
value: FiroType.spark,
|
||||||
groupValue: ref
|
groupValue: ref
|
||||||
.watch(
|
.watch(
|
||||||
publicPrivateBalanceStateProvider.state)
|
publicPrivateBalanceStateProvider.state)
|
||||||
|
@ -131,7 +131,7 @@ class _FiroBalanceSelectionSheetState
|
||||||
ref
|
ref
|
||||||
.read(publicPrivateBalanceStateProvider
|
.read(publicPrivateBalanceStateProvider
|
||||||
.state)
|
.state)
|
||||||
.state = "Private";
|
.state = FiroType.spark;
|
||||||
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
|
@ -149,7 +149,86 @@ class _FiroBalanceSelectionSheetState
|
||||||
// Row(
|
// Row(
|
||||||
// children: [
|
// children: [
|
||||||
Text(
|
Text(
|
||||||
"Private balance",
|
"Spark balance",
|
||||||
|
style: STextStyles.titleBold12(context),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
ref.watch(pAmountFormatter(coin)).format(
|
||||||
|
firoWallet
|
||||||
|
.info.cachedBalanceTertiary.spendable,
|
||||||
|
),
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
final state =
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state;
|
||||||
|
if (state != FiroType.lelantus) {
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
|
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),
|
style: STextStyles.titleBold12(context),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
|
@ -180,9 +259,9 @@ class _FiroBalanceSelectionSheetState
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final state =
|
final state =
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state;
|
ref.read(publicPrivateBalanceStateProvider.state).state;
|
||||||
if (state != "Public") {
|
if (state != FiroType.public) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
"Public";
|
FiroType.public;
|
||||||
}
|
}
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
|
@ -200,7 +279,7 @@ class _FiroBalanceSelectionSheetState
|
||||||
activeColor: Theme.of(context)
|
activeColor: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.radioButtonIconEnabled,
|
.radioButtonIconEnabled,
|
||||||
value: "Public",
|
value: FiroType.public,
|
||||||
groupValue: ref
|
groupValue: ref
|
||||||
.watch(
|
.watch(
|
||||||
publicPrivateBalanceStateProvider.state)
|
publicPrivateBalanceStateProvider.state)
|
||||||
|
@ -209,7 +288,7 @@ class _FiroBalanceSelectionSheetState
|
||||||
ref
|
ref
|
||||||
.read(publicPrivateBalanceStateProvider
|
.read(publicPrivateBalanceStateProvider
|
||||||
.state)
|
.state)
|
||||||
.state = "Public";
|
.state = FiroType.public;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -348,7 +348,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
.getWallet(walletId)
|
.getWallet(walletId)
|
||||||
.cryptoCurrency
|
.cryptoCurrency
|
||||||
.validateAddress(address ?? "");
|
.validateAddress(address ?? "");
|
||||||
ref.read(previewTxButtonStateProvider.state).state =
|
ref.read(previewTokenTxButtonStateProvider.state).state =
|
||||||
(isValidAddress && amount != null && amount > Amount.zero);
|
(isValidAddress && amount != null && amount > Amount.zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,12 +1227,14 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: ref
|
onPressed: ref
|
||||||
.watch(previewTxButtonStateProvider.state)
|
.watch(
|
||||||
|
previewTokenTxButtonStateProvider.state)
|
||||||
.state
|
.state
|
||||||
? _previewTransaction
|
? _previewTransaction
|
||||||
: null,
|
: null,
|
||||||
style: ref
|
style: ref
|
||||||
.watch(previewTxButtonStateProvider.state)
|
.watch(
|
||||||
|
previewTokenTxButtonStateProvider.state)
|
||||||
.state
|
.state
|
||||||
? Theme.of(context)
|
? Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
|
|
@ -15,14 +15,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:stackwallet/db/hive/db.dart';
|
import 'package:stackwallet/db/hive/db.dart';
|
||||||
import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
|
|
||||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||||
import 'package:stackwallet/providers/global/debug_service_provider.dart';
|
import 'package:stackwallet/providers/global/debug_service_provider.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/default_nodes.dart';
|
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/widgets/background.dart';
|
import 'package:stackwallet/widgets/background.dart';
|
||||||
|
@ -217,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(
|
const SizedBox(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
|
@ -345,9 +252,6 @@ class HiddenSettings extends StatelessWidget {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(
|
|
||||||
height: 12,
|
|
||||||
),
|
|
||||||
Consumer(
|
Consumer(
|
||||||
builder: (_, ref, __) {
|
builder: (_, ref, __) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
@ -366,221 +270,6 @@ class HiddenSettings extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(
|
|
||||||
height: 12,
|
|
||||||
),
|
|
||||||
Consumer(
|
|
||||||
builder: (_, ref, __) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
try {
|
|
||||||
final n = DefaultNodes.firoTestnet;
|
|
||||||
|
|
||||||
final e = ElectrumXClient.from(
|
|
||||||
node: ElectrumXNode(
|
|
||||||
address: n.host,
|
|
||||||
port: n.port,
|
|
||||||
name: n.name,
|
|
||||||
id: n.id,
|
|
||||||
useSSL: n.useSSL,
|
|
||||||
),
|
|
||||||
prefs:
|
|
||||||
ref.read(prefsChangeNotifierProvider),
|
|
||||||
failovers: [],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Call and print getSparkAnonymitySet.
|
|
||||||
final anonymitySet =
|
|
||||||
await e.getSparkAnonymitySet(
|
|
||||||
coinGroupId: "1",
|
|
||||||
startBlockHash: "",
|
|
||||||
);
|
|
||||||
|
|
||||||
Util.printJson(anonymitySet, "anonymitySet");
|
|
||||||
} catch (e, s) {
|
|
||||||
print("$e\n$s");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: RoundedWhiteContainer(
|
|
||||||
child: Text(
|
|
||||||
"Spark getSparkAnonymitySet",
|
|
||||||
style: STextStyles.button(context).copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 12,
|
|
||||||
),
|
|
||||||
Consumer(
|
|
||||||
builder: (_, ref, __) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
try {
|
|
||||||
final n = DefaultNodes.firoTestnet;
|
|
||||||
|
|
||||||
final e = ElectrumXClient.from(
|
|
||||||
node: ElectrumXNode(
|
|
||||||
address: n.host,
|
|
||||||
port: n.port,
|
|
||||||
name: n.name,
|
|
||||||
id: n.id,
|
|
||||||
useSSL: n.useSSL,
|
|
||||||
),
|
|
||||||
prefs:
|
|
||||||
ref.read(prefsChangeNotifierProvider),
|
|
||||||
failovers: [],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Call and print getUsedCoinsTags.
|
|
||||||
final usedCoinsTags = await e
|
|
||||||
.getSparkUsedCoinsTags(startNumber: 0);
|
|
||||||
|
|
||||||
print(
|
|
||||||
"usedCoinsTags['tags'].length: ${usedCoinsTags["tags"].length}");
|
|
||||||
Util.printJson(
|
|
||||||
usedCoinsTags, "usedCoinsTags");
|
|
||||||
} catch (e, s) {
|
|
||||||
print("$e\n$s");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: RoundedWhiteContainer(
|
|
||||||
child: Text(
|
|
||||||
"Spark getSparkUsedCoinsTags",
|
|
||||||
style: STextStyles.button(context).copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 12,
|
|
||||||
),
|
|
||||||
Consumer(
|
|
||||||
builder: (_, ref, __) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
try {
|
|
||||||
final n = DefaultNodes.firoTestnet;
|
|
||||||
|
|
||||||
final e = ElectrumXClient.from(
|
|
||||||
node: ElectrumXNode(
|
|
||||||
address: n.host,
|
|
||||||
port: n.port,
|
|
||||||
name: n.name,
|
|
||||||
id: n.id,
|
|
||||||
useSSL: n.useSSL,
|
|
||||||
),
|
|
||||||
prefs:
|
|
||||||
ref.read(prefsChangeNotifierProvider),
|
|
||||||
failovers: [],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Call and print getSparkMintMetaData.
|
|
||||||
final mintMetaData =
|
|
||||||
await e.getSparkMintMetaData(
|
|
||||||
sparkCoinHashes: [
|
|
||||||
"b476ed2b374bb081ea51d111f68f0136252521214e213d119b8dc67b92f5a390",
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Util.printJson(mintMetaData, "mintMetaData");
|
|
||||||
} catch (e, s) {
|
|
||||||
print("$e\n$s");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: RoundedWhiteContainer(
|
|
||||||
child: Text(
|
|
||||||
"Spark getSparkMintMetaData",
|
|
||||||
style: STextStyles.button(context).copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 12,
|
|
||||||
),
|
|
||||||
Consumer(
|
|
||||||
builder: (_, ref, __) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
try {
|
|
||||||
final n = DefaultNodes.firoTestnet;
|
|
||||||
|
|
||||||
final e = ElectrumXClient.from(
|
|
||||||
node: ElectrumXNode(
|
|
||||||
address: n.host,
|
|
||||||
port: n.port,
|
|
||||||
name: n.name,
|
|
||||||
id: n.id,
|
|
||||||
useSSL: n.useSSL,
|
|
||||||
),
|
|
||||||
prefs:
|
|
||||||
ref.read(prefsChangeNotifierProvider),
|
|
||||||
failovers: [],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Call and print getSparkLatestCoinId.
|
|
||||||
final latestCoinId =
|
|
||||||
await e.getSparkLatestCoinId();
|
|
||||||
|
|
||||||
Util.printJson(latestCoinId, "latestCoinId");
|
|
||||||
} catch (e, s) {
|
|
||||||
print("$e\n$s");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: RoundedWhiteContainer(
|
|
||||||
child: Text(
|
|
||||||
"Spark getSparkLatestCoinId",
|
|
||||||
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),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -40,13 +40,16 @@ class TxIcon extends ConsumerWidget {
|
||||||
bool isReceived,
|
bool isReceived,
|
||||||
bool isPending,
|
bool isPending,
|
||||||
TransactionSubType subType,
|
TransactionSubType subType,
|
||||||
|
TransactionType type,
|
||||||
IThemeAssets assets,
|
IThemeAssets assets,
|
||||||
) {
|
) {
|
||||||
if (subType == TransactionSubType.cashFusion) {
|
if (subType == TransactionSubType.cashFusion) {
|
||||||
return Assets.svg.txCashFusion;
|
return Assets.svg.txCashFusion;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isReceived && subType == TransactionSubType.mint) {
|
if ((!isReceived && subType == TransactionSubType.mint) ||
|
||||||
|
(subType == TransactionSubType.sparkMint &&
|
||||||
|
type == TransactionType.sentToSelf)) {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return Assets.svg.anonymizeFailed;
|
return Assets.svg.anonymizeFailed;
|
||||||
}
|
}
|
||||||
|
@ -91,6 +94,7 @@ class TxIcon extends ConsumerWidget {
|
||||||
ref.watch(pWallets).getWallet(tx.walletId).cryptoCurrency.minConfirms,
|
ref.watch(pWallets).getWallet(tx.walletId).cryptoCurrency.minConfirms,
|
||||||
),
|
),
|
||||||
tx.subType,
|
tx.subType,
|
||||||
|
tx.type,
|
||||||
ref.watch(themeAssetsProvider),
|
ref.watch(themeAssetsProvider),
|
||||||
);
|
);
|
||||||
} else if (transaction is TransactionV2) {
|
} else if (transaction is TransactionV2) {
|
||||||
|
@ -104,6 +108,7 @@ class TxIcon extends ConsumerWidget {
|
||||||
ref.watch(pWallets).getWallet(tx.walletId).cryptoCurrency.minConfirms,
|
ref.watch(pWallets).getWallet(tx.walletId).cryptoCurrency.minConfirms,
|
||||||
),
|
),
|
||||||
tx.subType,
|
tx.subType,
|
||||||
|
tx.type,
|
||||||
ref.watch(themeAssetsProvider),
|
ref.watch(themeAssetsProvider),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -25,8 +25,10 @@ import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
enum _BalanceType {
|
enum _BalanceType {
|
||||||
available,
|
available,
|
||||||
full,
|
full,
|
||||||
privateAvailable,
|
lelantusAvailable,
|
||||||
privateFull;
|
lelantusFull,
|
||||||
|
sparkAvailable,
|
||||||
|
sparkFull;
|
||||||
}
|
}
|
||||||
|
|
||||||
class WalletBalanceToggleSheet extends ConsumerWidget {
|
class WalletBalanceToggleSheet extends ConsumerWidget {
|
||||||
|
@ -39,9 +41,10 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final maxHeight = MediaQuery.of(context).size.height * 0.60;
|
final maxHeight = MediaQuery.of(context).size.height * 0.90;
|
||||||
|
|
||||||
final coin = ref.watch(pWalletCoin(walletId));
|
final coin = ref.watch(pWalletCoin(walletId));
|
||||||
|
final isFiro = coin == Coin.firo || coin == Coin.firoTestNet;
|
||||||
|
|
||||||
Balance balance = ref.watch(pWalletBalance(walletId));
|
Balance balance = ref.watch(pWalletBalance(walletId));
|
||||||
|
|
||||||
|
@ -52,18 +55,27 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
|
||||||
: _BalanceType.full;
|
: _BalanceType.full;
|
||||||
|
|
||||||
Balance? balanceSecondary;
|
Balance? balanceSecondary;
|
||||||
if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
Balance? balanceTertiary;
|
||||||
|
if (isFiro) {
|
||||||
balanceSecondary = ref.watch(pWalletBalanceSecondary(walletId));
|
balanceSecondary = ref.watch(pWalletBalanceSecondary(walletId));
|
||||||
|
balanceTertiary = ref.watch(pWalletBalanceTertiary(walletId));
|
||||||
|
|
||||||
final temp = balance;
|
switch (ref.watch(publicPrivateBalanceStateProvider.state).state) {
|
||||||
balance = balanceSecondary!;
|
case FiroType.spark:
|
||||||
balanceSecondary = temp;
|
|
||||||
|
|
||||||
if (ref.watch(publicPrivateBalanceStateProvider.state).state ==
|
|
||||||
"Private") {
|
|
||||||
_bal = _bal == _BalanceType.available
|
_bal = _bal == _BalanceType.available
|
||||||
? _BalanceType.privateAvailable
|
? _BalanceType.sparkAvailable
|
||||||
: _BalanceType.privateFull;
|
: _BalanceType.sparkFull;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FiroType.lelantus:
|
||||||
|
_bal = _bal == _BalanceType.available
|
||||||
|
? _BalanceType.lelantusAvailable
|
||||||
|
: _BalanceType.lelantusFull;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FiroType.public:
|
||||||
|
// already set above
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,22 +128,21 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
|
||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
BalanceSelector(
|
BalanceSelector(
|
||||||
title:
|
title: "Available${isFiro ? " public" : ""} balance",
|
||||||
"Available${balanceSecondary != null ? " public" : ""} balance",
|
|
||||||
coin: coin,
|
coin: coin,
|
||||||
balance: balance.spendable,
|
balance: balance.spendable,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.read(walletBalanceToggleStateProvider.state).state =
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
WalletBalanceToggleState.available;
|
WalletBalanceToggleState.available;
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
"Public";
|
FiroType.public;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
ref.read(walletBalanceToggleStateProvider.state).state =
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
WalletBalanceToggleState.available;
|
WalletBalanceToggleState.available;
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
"Public";
|
FiroType.public;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
value: _BalanceType.available,
|
value: _BalanceType.available,
|
||||||
|
@ -141,22 +152,21 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
BalanceSelector(
|
BalanceSelector(
|
||||||
title:
|
title: "Full${isFiro ? " public" : ""} balance",
|
||||||
"Full${balanceSecondary != null ? " public" : ""} balance",
|
|
||||||
coin: coin,
|
coin: coin,
|
||||||
balance: balance.total,
|
balance: balance.total,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.read(walletBalanceToggleStateProvider.state).state =
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
WalletBalanceToggleState.full;
|
WalletBalanceToggleState.full;
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
"Public";
|
FiroType.public;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
ref.read(walletBalanceToggleStateProvider.state).state =
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
WalletBalanceToggleState.full;
|
WalletBalanceToggleState.full;
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
"Public";
|
FiroType.public;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
value: _BalanceType.full,
|
value: _BalanceType.full,
|
||||||
|
@ -168,24 +178,24 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
if (balanceSecondary != null)
|
if (balanceSecondary != null)
|
||||||
BalanceSelector(
|
BalanceSelector(
|
||||||
title: "Available private balance",
|
title: "Available lelantus balance",
|
||||||
coin: coin,
|
coin: coin,
|
||||||
balance: balanceSecondary.spendable,
|
balance: balanceSecondary.spendable,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.read(walletBalanceToggleStateProvider.state).state =
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
WalletBalanceToggleState.available;
|
WalletBalanceToggleState.available;
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
"Private";
|
FiroType.lelantus;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
ref.read(walletBalanceToggleStateProvider.state).state =
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
WalletBalanceToggleState.available;
|
WalletBalanceToggleState.available;
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
"Private";
|
FiroType.lelantus;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
value: _BalanceType.privateAvailable,
|
value: _BalanceType.lelantusAvailable,
|
||||||
groupValue: _bal,
|
groupValue: _bal,
|
||||||
),
|
),
|
||||||
if (balanceSecondary != null)
|
if (balanceSecondary != null)
|
||||||
|
@ -194,24 +204,76 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
if (balanceSecondary != null)
|
if (balanceSecondary != null)
|
||||||
BalanceSelector(
|
BalanceSelector(
|
||||||
title: "Full private balance",
|
title: "Full lelantus balance",
|
||||||
coin: coin,
|
coin: coin,
|
||||||
balance: balanceSecondary.total,
|
balance: balanceSecondary.total,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.read(walletBalanceToggleStateProvider.state).state =
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
WalletBalanceToggleState.full;
|
WalletBalanceToggleState.full;
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
"Private";
|
FiroType.lelantus;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
ref.read(walletBalanceToggleStateProvider.state).state =
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
WalletBalanceToggleState.full;
|
WalletBalanceToggleState.full;
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
"Private";
|
FiroType.lelantus;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
value: _BalanceType.privateFull,
|
value: _BalanceType.lelantusFull,
|
||||||
|
groupValue: _bal,
|
||||||
|
),
|
||||||
|
if (balanceTertiary != null)
|
||||||
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
if (balanceTertiary != null)
|
||||||
|
BalanceSelector(
|
||||||
|
title: "Available spark balance",
|
||||||
|
coin: coin,
|
||||||
|
balance: balanceTertiary.spendable,
|
||||||
|
onPressed: () {
|
||||||
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
|
WalletBalanceToggleState.available;
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
|
FiroType.spark;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
onChanged: (_) {
|
||||||
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
|
WalletBalanceToggleState.available;
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
|
FiroType.spark;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
value: _BalanceType.sparkAvailable,
|
||||||
|
groupValue: _bal,
|
||||||
|
),
|
||||||
|
if (balanceTertiary != null)
|
||||||
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
if (balanceTertiary != null)
|
||||||
|
BalanceSelector(
|
||||||
|
title: "Full spark balance",
|
||||||
|
coin: coin,
|
||||||
|
balance: balanceTertiary.total,
|
||||||
|
onPressed: () {
|
||||||
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
|
WalletBalanceToggleState.full;
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
|
FiroType.spark;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
onChanged: (_) {
|
||||||
|
ref.read(walletBalanceToggleStateProvider.state).state =
|
||||||
|
WalletBalanceToggleState.full;
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
|
FiroType.spark;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
value: _BalanceType.sparkFull,
|
||||||
groupValue: _bal,
|
groupValue: _bal,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
|
|
@ -12,6 +12,7 @@ import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_native_splash/cli_commands.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart';
|
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart';
|
||||||
|
@ -29,6 +30,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
|
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/impl/banano_wallet.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
|
|
||||||
class WalletSummaryInfo extends ConsumerWidget {
|
class WalletSummaryInfo extends ConsumerWidget {
|
||||||
|
@ -45,6 +47,8 @@ class WalletSummaryInfo extends ConsumerWidget {
|
||||||
showModalBottomSheet<dynamic>(
|
showModalBottomSheet<dynamic>(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
context: context,
|
context: context,
|
||||||
|
useSafeArea: true,
|
||||||
|
isScrollControlled: true,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.vertical(
|
borderRadius: BorderRadius.vertical(
|
||||||
top: Radius.circular(20),
|
top: Radius.circular(20),
|
||||||
|
@ -58,10 +62,6 @@ class WalletSummaryInfo extends ConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
debugPrint("BUILD: $runtimeType");
|
debugPrint("BUILD: $runtimeType");
|
||||||
|
|
||||||
bool isMonkey = true;
|
|
||||||
|
|
||||||
final receivingAddress = ref.watch(pWalletReceivingAddress(walletId));
|
|
||||||
|
|
||||||
final externalCalls = ref.watch(
|
final externalCalls = ref.watch(
|
||||||
prefsChangeNotifierProvider.select((value) => value.externalCalls));
|
prefsChangeNotifierProvider.select((value) => value.externalCalls));
|
||||||
final coin = ref.watch(pWalletCoin(walletId));
|
final coin = ref.watch(pWalletCoin(walletId));
|
||||||
|
@ -81,19 +81,28 @@ class WalletSummaryInfo extends ConsumerWidget {
|
||||||
WalletBalanceToggleState.available;
|
WalletBalanceToggleState.available;
|
||||||
|
|
||||||
final Amount balanceToShow;
|
final Amount balanceToShow;
|
||||||
String title;
|
final String title;
|
||||||
|
|
||||||
if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
final _showPrivate =
|
final type = ref.watch(publicPrivateBalanceStateProvider.state).state;
|
||||||
ref.watch(publicPrivateBalanceStateProvider.state).state == "Private";
|
title =
|
||||||
|
"${_showAvailable ? "Available" : "Full"} ${type.name.capitalize()} balance";
|
||||||
|
switch (type) {
|
||||||
|
case FiroType.spark:
|
||||||
|
final balance = ref.watch(pWalletBalanceTertiary(walletId));
|
||||||
|
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
||||||
|
break;
|
||||||
|
|
||||||
final secondaryBal = ref.watch(pWalletBalanceSecondary(walletId));
|
case FiroType.lelantus:
|
||||||
|
final balance = ref.watch(pWalletBalanceSecondary(walletId));
|
||||||
|
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
||||||
|
break;
|
||||||
|
|
||||||
final bal = _showPrivate ? balance : secondaryBal;
|
case FiroType.public:
|
||||||
|
final balance = ref.watch(pWalletBalance(walletId));
|
||||||
balanceToShow = _showAvailable ? bal.spendable : bal.total;
|
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
||||||
title = _showAvailable ? "Available" : "Full";
|
break;
|
||||||
title += _showPrivate ? " private balance" : " public balance";
|
}
|
||||||
} else {
|
} else {
|
||||||
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
||||||
title = _showAvailable ? "Available balance" : "Full balance";
|
title = _showAvailable ? "Available balance" : "Full balance";
|
||||||
|
@ -102,8 +111,8 @@ class WalletSummaryInfo extends ConsumerWidget {
|
||||||
List<int>? imageBytes;
|
List<int>? imageBytes;
|
||||||
|
|
||||||
if (coin == Coin.banano) {
|
if (coin == Coin.banano) {
|
||||||
// TODO: [prio=high] fix this and uncomment:
|
imageBytes = (ref.watch(pWallets).getWallet(walletId) as BananoWallet)
|
||||||
// imageBytes = (manager.wallet as BananoWallet).getMonkeyImageBytes();
|
.getMonkeyImageBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConditionalParent(
|
return ConditionalParent(
|
||||||
|
|
|
@ -31,7 +31,6 @@ import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|
||||||
import 'package:stackwallet/utilities/format.dart';
|
import 'package:stackwallet/utilities/format.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
@ -843,34 +842,10 @@ class _DesktopTransactionCardRowState
|
||||||
late final String walletId;
|
late final String walletId;
|
||||||
late final int minConfirms;
|
late final int minConfirms;
|
||||||
|
|
||||||
String whatIsIt(TransactionType type, Coin coin, int height) {
|
String whatIsIt(TransactionV2 tx, int height) => tx.statusLabel(
|
||||||
if (_transaction.subType == TransactionSubType.mint ||
|
currentChainHeight: height,
|
||||||
_transaction.subType == TransactionSubType.cashFusion) {
|
minConfirms: minConfirms,
|
||||||
if (_transaction.isConfirmed(height, minConfirms)) {
|
);
|
||||||
return "Anonymized";
|
|
||||||
} else {
|
|
||||||
return "Anonymizing";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == TransactionType.incoming) {
|
|
||||||
if (_transaction.isConfirmed(height, minConfirms)) {
|
|
||||||
return "Received";
|
|
||||||
} else {
|
|
||||||
return "Receiving";
|
|
||||||
}
|
|
||||||
} else if (type == TransactionType.outgoing) {
|
|
||||||
if (_transaction.isConfirmed(height, minConfirms)) {
|
|
||||||
return "Sent";
|
|
||||||
} else {
|
|
||||||
return "Sending";
|
|
||||||
}
|
|
||||||
} else if (type == TransactionType.sentToSelf) {
|
|
||||||
return "Sent to self";
|
|
||||||
} else {
|
|
||||||
return type.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -917,7 +892,7 @@ class _DesktopTransactionCardRowState
|
||||||
final Amount amount;
|
final Amount amount;
|
||||||
|
|
||||||
if (_transaction.subType == TransactionSubType.cashFusion) {
|
if (_transaction.subType == TransactionSubType.cashFusion) {
|
||||||
amount = _transaction.getAmountReceivedThisWallet(coin: coin);
|
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
|
||||||
} else {
|
} else {
|
||||||
switch (_transaction.type) {
|
switch (_transaction.type) {
|
||||||
case TransactionType.outgoing:
|
case TransactionType.outgoing:
|
||||||
|
@ -926,7 +901,11 @@ class _DesktopTransactionCardRowState
|
||||||
|
|
||||||
case TransactionType.incoming:
|
case TransactionType.incoming:
|
||||||
case TransactionType.sentToSelf:
|
case TransactionType.sentToSelf:
|
||||||
amount = _transaction.getAmountReceivedThisWallet(coin: coin);
|
if (_transaction.subType == TransactionSubType.sparkMint) {
|
||||||
|
amount = _transaction.getAmountSparkSelfMinted(coin: coin);
|
||||||
|
} else {
|
||||||
|
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TransactionType.unknown:
|
case TransactionType.unknown:
|
||||||
|
@ -994,8 +973,7 @@ class _DesktopTransactionCardRowState
|
||||||
flex: 3,
|
flex: 3,
|
||||||
child: Text(
|
child: Text(
|
||||||
whatIsIt(
|
whatIsIt(
|
||||||
_transaction.type,
|
_transaction,
|
||||||
coin,
|
|
||||||
currentHeight,
|
currentHeight,
|
||||||
),
|
),
|
||||||
style:
|
style:
|
||||||
|
|
|
@ -44,42 +44,13 @@ class _TransactionCardStateV2 extends ConsumerState<TransactionCardV2> {
|
||||||
String whatIsIt(
|
String whatIsIt(
|
||||||
Coin coin,
|
Coin coin,
|
||||||
int currentHeight,
|
int currentHeight,
|
||||||
) {
|
) =>
|
||||||
final confirmedStatus = _transaction.isConfirmed(
|
_transaction.statusLabel(
|
||||||
currentHeight,
|
currentChainHeight: currentHeight,
|
||||||
|
minConfirms:
|
||||||
ref.read(pWallets).getWallet(walletId).cryptoCurrency.minConfirms,
|
ref.read(pWallets).getWallet(walletId).cryptoCurrency.minConfirms,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_transaction.subType == TransactionSubType.cashFusion) {
|
|
||||||
if (confirmedStatus) {
|
|
||||||
return "Anonymized";
|
|
||||||
} else {
|
|
||||||
return "Anonymizing";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_transaction.type == TransactionType.incoming) {
|
|
||||||
// if (_transaction.isMinting) {
|
|
||||||
// return "Minting";
|
|
||||||
// } else
|
|
||||||
if (confirmedStatus) {
|
|
||||||
return "Received";
|
|
||||||
} else {
|
|
||||||
return "Receiving";
|
|
||||||
}
|
|
||||||
} else if (_transaction.type == TransactionType.outgoing) {
|
|
||||||
if (confirmedStatus) {
|
|
||||||
return "Sent";
|
|
||||||
} else {
|
|
||||||
return "Sending";
|
|
||||||
}
|
|
||||||
} else if (_transaction.type == TransactionType.sentToSelf) {
|
|
||||||
return "Sent to self";
|
|
||||||
} else {
|
|
||||||
return _transaction.type.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_transaction = widget.transaction;
|
_transaction = widget.transaction;
|
||||||
|
@ -121,7 +92,7 @@ class _TransactionCardStateV2 extends ConsumerState<TransactionCardV2> {
|
||||||
final Amount amount;
|
final Amount amount;
|
||||||
|
|
||||||
if (_transaction.subType == TransactionSubType.cashFusion) {
|
if (_transaction.subType == TransactionSubType.cashFusion) {
|
||||||
amount = _transaction.getAmountReceivedThisWallet(coin: coin);
|
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
|
||||||
} else {
|
} else {
|
||||||
switch (_transaction.type) {
|
switch (_transaction.type) {
|
||||||
case TransactionType.outgoing:
|
case TransactionType.outgoing:
|
||||||
|
@ -130,7 +101,11 @@ class _TransactionCardStateV2 extends ConsumerState<TransactionCardV2> {
|
||||||
|
|
||||||
case TransactionType.incoming:
|
case TransactionType.incoming:
|
||||||
case TransactionType.sentToSelf:
|
case TransactionType.sentToSelf:
|
||||||
amount = _transaction.getAmountReceivedThisWallet(coin: coin);
|
if (_transaction.subType == TransactionSubType.sparkMint) {
|
||||||
|
amount = _transaction.getAmountSparkSelfMinted(coin: coin);
|
||||||
|
} else {
|
||||||
|
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TransactionType.unknown:
|
case TransactionType.unknown:
|
||||||
|
|
|
@ -95,7 +95,12 @@ class _TransactionV2DetailsViewState
|
||||||
minConfirms =
|
minConfirms =
|
||||||
ref.read(pWallets).getWallet(walletId).cryptoCurrency.minConfirms;
|
ref.read(pWallets).getWallet(walletId).cryptoCurrency.minConfirms;
|
||||||
|
|
||||||
|
if (_transaction.subType == TransactionSubType.join ||
|
||||||
|
_transaction.subType == TransactionSubType.sparkSpend) {
|
||||||
|
fee = _transaction.getAnonFee()!;
|
||||||
|
} else {
|
||||||
fee = _transaction.getFee(coin: coin);
|
fee = _transaction.getFee(coin: coin);
|
||||||
|
}
|
||||||
|
|
||||||
if (_transaction.subType == TransactionSubType.cashFusion ||
|
if (_transaction.subType == TransactionSubType.cashFusion ||
|
||||||
_transaction.type == TransactionType.sentToSelf) {
|
_transaction.type == TransactionType.sentToSelf) {
|
||||||
|
@ -107,7 +112,7 @@ class _TransactionV2DetailsViewState
|
||||||
unit = coin.ticker;
|
unit = coin.ticker;
|
||||||
|
|
||||||
if (_transaction.subType == TransactionSubType.cashFusion) {
|
if (_transaction.subType == TransactionSubType.cashFusion) {
|
||||||
amount = _transaction.getAmountReceivedThisWallet(coin: coin);
|
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
|
||||||
data = _transaction.outputs
|
data = _transaction.outputs
|
||||||
.where((e) => e.walletOwns)
|
.where((e) => e.walletOwns)
|
||||||
.map((e) => (
|
.map((e) => (
|
||||||
|
@ -131,7 +136,11 @@ class _TransactionV2DetailsViewState
|
||||||
|
|
||||||
case TransactionType.incoming:
|
case TransactionType.incoming:
|
||||||
case TransactionType.sentToSelf:
|
case TransactionType.sentToSelf:
|
||||||
amount = _transaction.getAmountReceivedThisWallet(coin: coin);
|
if (_transaction.subType == TransactionSubType.sparkMint) {
|
||||||
|
amount = _transaction.getAmountSparkSelfMinted(coin: coin);
|
||||||
|
} else {
|
||||||
|
amount = _transaction.getAmountReceivedInThisWallet(coin: coin);
|
||||||
|
}
|
||||||
data = _transaction.outputs
|
data = _transaction.outputs
|
||||||
.where((e) => e.walletOwns)
|
.where((e) => e.walletOwns)
|
||||||
.map((e) => (
|
.map((e) => (
|
||||||
|
@ -164,77 +173,10 @@ class _TransactionV2DetailsViewState
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
String whatIsIt(TransactionV2 tx, int height) {
|
String whatIsIt(TransactionV2 tx, int height) => tx.statusLabel(
|
||||||
final type = tx.type;
|
currentChainHeight: height,
|
||||||
if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
minConfirms: minConfirms,
|
||||||
if (tx.subType == TransactionSubType.mint) {
|
);
|
||||||
if (tx.isConfirmed(height, minConfirms)) {
|
|
||||||
return "Minted";
|
|
||||||
} else {
|
|
||||||
return "Minting";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (coin == Coin.epicCash) {
|
|
||||||
// if (_transaction.isCancelled) {
|
|
||||||
// return "Cancelled";
|
|
||||||
// } else if (type == TransactionType.incoming) {
|
|
||||||
// if (tx.isConfirmed(height, minConfirms)) {
|
|
||||||
// return "Received";
|
|
||||||
// } else {
|
|
||||||
// if (_transaction.numberOfMessages == 1) {
|
|
||||||
// return "Receiving (waiting for sender)";
|
|
||||||
// } else if ((_transaction.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 (tx.isConfirmed(height, minConfirms)) {
|
|
||||||
// return "Sent (confirmed)";
|
|
||||||
// } else {
|
|
||||||
// if (_transaction.numberOfMessages == 1) {
|
|
||||||
// return "Sending (waiting for receiver)";
|
|
||||||
// } else if ((_transaction.numberOfMessages ?? 0) > 1) {
|
|
||||||
// return "Sending (waiting for confirmations)";
|
|
||||||
// } else {
|
|
||||||
// return "Sending";
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (tx.subType == TransactionSubType.cashFusion) {
|
|
||||||
if (tx.isConfirmed(height, minConfirms)) {
|
|
||||||
return "Anonymized";
|
|
||||||
} else {
|
|
||||||
return "Anonymizing";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == TransactionType.incoming) {
|
|
||||||
// if (_transaction.isMinting) {
|
|
||||||
// return "Minting";
|
|
||||||
// } else
|
|
||||||
if (tx.isConfirmed(height, minConfirms)) {
|
|
||||||
return "Received";
|
|
||||||
} else {
|
|
||||||
return "Receiving";
|
|
||||||
}
|
|
||||||
} else if (type == TransactionType.outgoing) {
|
|
||||||
if (tx.isConfirmed(height, minConfirms)) {
|
|
||||||
return "Sent";
|
|
||||||
} else {
|
|
||||||
return "Sending";
|
|
||||||
}
|
|
||||||
} else if (type == TransactionType.sentToSelf) {
|
|
||||||
return "Sent to self";
|
|
||||||
} else {
|
|
||||||
return type.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> fetchContactNameFor(String address) async {
|
Future<String> fetchContactNameFor(String address) async {
|
||||||
if (address.isEmpty) {
|
if (address.isEmpty) {
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
@ -40,6 +42,9 @@ class _TransactionsV2ListState extends ConsumerState<TransactionsV2List> {
|
||||||
bool _hasLoaded = false;
|
bool _hasLoaded = false;
|
||||||
List<TransactionV2> _transactions = [];
|
List<TransactionV2> _transactions = [];
|
||||||
|
|
||||||
|
late final StreamSubscription<List<TransactionV2>> _subscription;
|
||||||
|
late final QueryBuilder<TransactionV2, TransactionV2, QAfterSortBy> _query;
|
||||||
|
|
||||||
BorderRadius get _borderRadiusFirst {
|
BorderRadius get _borderRadiusFirst {
|
||||||
return BorderRadius.only(
|
return BorderRadius.only(
|
||||||
topLeft: Radius.circular(
|
topLeft: Radius.circular(
|
||||||
|
@ -63,18 +68,38 @@ class _TransactionsV2ListState extends ConsumerState<TransactionsV2List> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
void initState() {
|
||||||
final coin = ref.watch(pWallets).getWallet(widget.walletId).info.coin;
|
_query = ref
|
||||||
|
.read(mainDBProvider)
|
||||||
return FutureBuilder(
|
|
||||||
future: ref
|
|
||||||
.watch(mainDBProvider)
|
|
||||||
.isar
|
.isar
|
||||||
.transactionV2s
|
.transactionV2s
|
||||||
.where()
|
.where()
|
||||||
.walletIdEqualTo(widget.walletId)
|
.walletIdEqualTo(widget.walletId)
|
||||||
.sortByTimestampDesc()
|
.sortByTimestampDesc();
|
||||||
.findAll(),
|
|
||||||
|
_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 coin = ref.watch(pWallets).getWallet(widget.walletId).info.coin;
|
||||||
|
|
||||||
|
return FutureBuilder(
|
||||||
|
future: _query.findAll(),
|
||||||
builder: (fbContext, AsyncSnapshot<List<TransactionV2>> snapshot) {
|
builder: (fbContext, AsyncSnapshot<List<TransactionV2>> snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done &&
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
snapshot.hasData) {
|
snapshot.hasData) {
|
||||||
|
|
|
@ -46,8 +46,6 @@ import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
|
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
|
||||||
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
|
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
|
||||||
import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart';
|
import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart';
|
||||||
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
|
||||||
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
|
|
||||||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.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/events/global/wallet_sync_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||||
|
@ -63,7 +61,6 @@ import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
|
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
|
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/show_loading.dart';
|
import 'package:stackwallet/utilities/show_loading.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
@ -71,6 +68,7 @@ import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
||||||
import 'package:stackwallet/widgets/background.dart';
|
import 'package:stackwallet/widgets/background.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.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/app_bar_icon_button.dart';
|
||||||
|
@ -118,6 +116,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
late final String walletId;
|
late final String walletId;
|
||||||
late final Coin coin;
|
late final Coin coin;
|
||||||
|
|
||||||
|
late final bool isSparkWallet;
|
||||||
|
|
||||||
late final bool _shouldDisableAutoSyncOnLogOut;
|
late final bool _shouldDisableAutoSyncOnLogOut;
|
||||||
|
|
||||||
late WalletSyncStatus _currentSyncStatus;
|
late WalletSyncStatus _currentSyncStatus;
|
||||||
|
@ -174,6 +174,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
_shouldDisableAutoSyncOnLogOut = false;
|
_shouldDisableAutoSyncOnLogOut = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSparkWallet = wallet is SparkInterface;
|
||||||
|
|
||||||
if (coin == Coin.firo &&
|
if (coin == Coin.firo &&
|
||||||
(wallet as FiroWallet).lelantusCoinIsarRescanRequired) {
|
(wallet as FiroWallet).lelantusCoinIsarRescanRequired) {
|
||||||
_rescanningOnOpen = true;
|
_rescanningOnOpen = true;
|
||||||
|
@ -433,7 +435,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await firoWallet.anonymizeAllPublicFunds();
|
// await firoWallet.anonymizeAllLelantus();
|
||||||
|
await firoWallet.anonymizeAllSpark();
|
||||||
shouldPop = true;
|
shouldPop = true;
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Navigator.of(context).popUntil(
|
Navigator.of(context).popUntil(
|
||||||
|
@ -760,11 +763,11 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (coin == Coin.firo)
|
if (isSparkWallet)
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
if (coin == Coin.firo)
|
if (isSparkWallet)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -951,20 +954,21 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
label: "Send",
|
label: "Send",
|
||||||
icon: const SendNavIcon(),
|
icon: const SendNavIcon(),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
switch (ref
|
// not sure what this is supposed to accomplish?
|
||||||
.read(walletBalanceToggleStateProvider.state)
|
// switch (ref
|
||||||
.state) {
|
// .read(walletBalanceToggleStateProvider.state)
|
||||||
case WalletBalanceToggleState.full:
|
// .state) {
|
||||||
ref
|
// case WalletBalanceToggleState.full:
|
||||||
.read(publicPrivateBalanceStateProvider.state)
|
// ref
|
||||||
.state = "Public";
|
// .read(publicPrivateBalanceStateProvider.state)
|
||||||
break;
|
// .state = "Public";
|
||||||
case WalletBalanceToggleState.available:
|
// break;
|
||||||
ref
|
// case WalletBalanceToggleState.available:
|
||||||
.read(publicPrivateBalanceStateProvider.state)
|
// ref
|
||||||
.state = "Private";
|
// .read(publicPrivateBalanceStateProvider.state)
|
||||||
break;
|
// .state = "Private";
|
||||||
}
|
// break;
|
||||||
|
// }
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
SendView.routeName,
|
SendView.routeName,
|
||||||
arguments: Tuple2(
|
arguments: Tuple2(
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'package:stackwallet/pages/wallets_view/sub_widgets/empty_wallets.dart';
|
||||||
import 'package:stackwallet/pages/wallets_view/sub_widgets/favorite_wallets.dart';
|
import 'package:stackwallet/pages/wallets_view/sub_widgets/favorite_wallets.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/themes/theme_providers.dart';
|
import 'package:stackwallet/themes/theme_providers.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/all_wallets_info_provider.dart';
|
||||||
|
|
||||||
class WalletsView extends ConsumerWidget {
|
class WalletsView extends ConsumerWidget {
|
||||||
const WalletsView({Key? key}) : super(key: key);
|
const WalletsView({Key? key}) : super(key: key);
|
||||||
|
@ -25,7 +26,7 @@ class WalletsView extends ConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
debugPrint("BUILD: $runtimeType");
|
debugPrint("BUILD: $runtimeType");
|
||||||
final hasWallets = ref.watch(pWallets).hasWallets;
|
final hasWallets = ref.watch(pAllWalletsInfo).isNotEmpty;
|
||||||
|
|
||||||
final showFavorites = ref.watch(prefsChangeNotifierProvider
|
final showFavorites = ref.watch(prefsChangeNotifierProvider
|
||||||
.select((value) => value.showFavoriteWallets));
|
.select((value) => value.showFavoriteWallets));
|
||||||
|
|
|
@ -16,9 +16,9 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:stackwallet/pages/settings_views/global_settings_view/hidden_settings.dart';
|
import 'package:stackwallet/pages/settings_views/global_settings_view/hidden_settings.dart';
|
||||||
import 'package:stackwallet/pages/wallets_view/sub_widgets/empty_wallets.dart';
|
import 'package:stackwallet/pages/wallets_view/sub_widgets/empty_wallets.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/my_wallets.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/my_wallets.dart';
|
||||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
|
||||||
import 'package:stackwallet/themes/theme_providers.dart';
|
import 'package:stackwallet/themes/theme_providers.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/providers/all_wallets_info_provider.dart';
|
||||||
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
|
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
|
||||||
import 'package:stackwallet/widgets/background.dart';
|
import 'package:stackwallet/widgets/background.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||||
|
@ -36,7 +36,7 @@ class _MyStackViewState extends ConsumerState<MyStackView> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
debugPrint("BUILD: $runtimeType");
|
debugPrint("BUILD: $runtimeType");
|
||||||
final hasWallets = ref.watch(pWallets).hasWallets;
|
final hasWallets = ref.watch(pAllWalletsInfo).isNotEmpty;
|
||||||
|
|
||||||
return Background(
|
return Background(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||||
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
|
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
|
@ -80,6 +81,8 @@ class DesktopPrivateBalanceToggleButton extends ConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final currentType = ref.watch(publicPrivateBalanceStateProvider);
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 22,
|
height: 22,
|
||||||
width: 22,
|
width: 22,
|
||||||
|
@ -87,13 +90,21 @@ class DesktopPrivateBalanceToggleButton extends ConsumerWidget {
|
||||||
color: Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
|
color: Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
|
||||||
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (ref.read(walletPrivateBalanceToggleStateProvider.state).state ==
|
switch (currentType) {
|
||||||
WalletBalanceToggleState.available) {
|
case FiroType.public:
|
||||||
ref.read(walletPrivateBalanceToggleStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
WalletBalanceToggleState.full;
|
FiroType.lelantus;
|
||||||
} else {
|
break;
|
||||||
ref.read(walletPrivateBalanceToggleStateProvider.state).state =
|
|
||||||
WalletBalanceToggleState.available;
|
case FiroType.lelantus:
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
|
FiroType.spark;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FiroType.spark:
|
||||||
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
|
FiroType.public;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
onPressed?.call();
|
onPressed?.call();
|
||||||
},
|
},
|
||||||
|
@ -110,12 +121,14 @@ class DesktopPrivateBalanceToggleButton extends ConsumerWidget {
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Image(
|
child: Image(
|
||||||
image: AssetImage(
|
image: AssetImage(
|
||||||
ref.watch(walletPrivateBalanceToggleStateProvider.state).state ==
|
currentType == FiroType.public
|
||||||
WalletBalanceToggleState.available
|
? Assets.png.glasses
|
||||||
? Assets.png.glassesHidden
|
: Assets.png.glassesHidden,
|
||||||
: Assets.png.glasses,
|
|
||||||
),
|
),
|
||||||
width: 16,
|
width: 16,
|
||||||
|
color: currentType == FiroType.spark
|
||||||
|
? Theme.of(context).extension<StackColors>()!.accentColorYellow
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -10,14 +10,18 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.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/notifications/show_flush_bar.dart';
|
||||||
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
|
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||||
|
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/route_generator.dart';
|
import 'package:stackwallet/route_generator.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
@ -29,7 +33,9 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.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/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||||
|
@ -57,10 +63,15 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
late final Coin coin;
|
late final Coin coin;
|
||||||
late final String walletId;
|
late final String walletId;
|
||||||
late final ClipboardInterface clipboard;
|
late final ClipboardInterface clipboard;
|
||||||
|
late final bool supportsSpark;
|
||||||
|
|
||||||
|
String? _sparkAddress;
|
||||||
|
String? _qrcodeContent;
|
||||||
|
bool _showSparkAddress = true;
|
||||||
|
|
||||||
Future<void> generateNewAddress() async {
|
Future<void> generateNewAddress() async {
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
if (wallet is Bip39HDWallet) {
|
if (wallet is MultiAddressInterface) {
|
||||||
bool shouldPop = false;
|
bool shouldPop = false;
|
||||||
unawaited(
|
unawaited(
|
||||||
showDialog(
|
showDialog(
|
||||||
|
@ -93,30 +104,273 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
walletId = widget.walletId;
|
walletId = widget.walletId;
|
||||||
coin = ref.read(pWalletInfo(walletId)).coin;
|
coin = ref.read(pWalletInfo(walletId)).coin;
|
||||||
clipboard = widget.clipboard;
|
clipboard = widget.clipboard;
|
||||||
|
supportsSpark = ref.read(pWallets).getWallet(walletId) is SparkInterface;
|
||||||
|
|
||||||
|
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(() {
|
||||||
|
_sparkAddress = event?.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_streamSub?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
debugPrint("BUILD: $runtimeType");
|
debugPrint("BUILD: $runtimeType");
|
||||||
|
|
||||||
final receivingAddress = ref.watch(pWalletReceivingAddress(walletId));
|
if (supportsSpark) {
|
||||||
|
if (_showSparkAddress) {
|
||||||
|
_qrcodeContent = _sparkAddress;
|
||||||
|
} else {
|
||||||
|
_qrcodeContent = ref.watch(pWalletReceivingAddress(walletId));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_qrcodeContent = ref.watch(pWalletReceivingAddress(walletId));
|
||||||
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
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)
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
clipboard.setData(
|
clipboard.setData(
|
||||||
ClipboardData(text: receivingAddress),
|
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 ${widget.contractAddress == null ? coin.ticker : ref.watch(
|
||||||
|
tokenServiceProvider.select(
|
||||||
|
(value) => value!.tokenContract.symbol,
|
||||||
|
),
|
||||||
|
)} 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: MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.click,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
clipboard.setData(
|
||||||
|
ClipboardData(
|
||||||
|
text: ref.watch(pWalletReceivingAddress(walletId))),
|
||||||
);
|
);
|
||||||
showFloatingFlushBar(
|
showFloatingFlushBar(
|
||||||
type: FlushBarType.info,
|
type: FlushBarType.info,
|
||||||
|
@ -179,9 +433,9 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
receivingAddress,
|
ref.watch(pWalletReceivingAddress(walletId)),
|
||||||
style:
|
style: STextStyles.desktopTextExtraExtraSmall(
|
||||||
STextStyles.desktopTextExtraExtraSmall(context)
|
context)
|
||||||
.copyWith(
|
.copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
@ -197,26 +451,23 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (coin != Coin.epicCash &&
|
),
|
||||||
coin != Coin.ethereum &&
|
|
||||||
coin != Coin.banano &&
|
if (ref.watch(pWallets.select((value) => value.getWallet(walletId)))
|
||||||
coin != Coin.nano &&
|
is MultiAddressInterface ||
|
||||||
coin != Coin.stellar &&
|
supportsSpark)
|
||||||
coin != Coin.stellarTestnet &&
|
|
||||||
coin != Coin.tezos)
|
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
if (coin != Coin.epicCash &&
|
|
||||||
coin != Coin.ethereum &&
|
if (ref.watch(pWallets.select((value) => value.getWallet(walletId)))
|
||||||
coin != Coin.banano &&
|
is MultiAddressInterface ||
|
||||||
coin != Coin.nano &&
|
supportsSpark)
|
||||||
coin != Coin.stellar &&
|
|
||||||
coin != Coin.stellarTestnet &&
|
|
||||||
coin != Coin.tezos)
|
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
buttonHeight: ButtonHeight.l,
|
buttonHeight: ButtonHeight.l,
|
||||||
onPressed: generateNewAddress,
|
onPressed: supportsSpark && _showSparkAddress
|
||||||
|
? generateNewSparkAddress
|
||||||
|
: generateNewAddress,
|
||||||
label: "Generate new address",
|
label: "Generate new address",
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
@ -226,7 +477,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
child: QrImageView(
|
child: QrImageView(
|
||||||
data: AddressUtils.buildUriString(
|
data: AddressUtils.buildUriString(
|
||||||
coin,
|
coin,
|
||||||
receivingAddress,
|
_qrcodeContent ?? "",
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
size: 200,
|
size: 200,
|
||||||
|
@ -267,7 +518,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
RouteGenerator.generateRoute(
|
RouteGenerator.generateRoute(
|
||||||
RouteSettings(
|
RouteSettings(
|
||||||
name: GenerateUriQrCodeView.routeName,
|
name: GenerateUriQrCodeView.routeName,
|
||||||
arguments: Tuple2(coin, receivingAddress),
|
arguments: Tuple2(coin, _qrcodeContent ?? ""),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -284,7 +535,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
|
shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
|
||||||
builder: (_) => GenerateUriQrCodeView(
|
builder: (_) => GenerateUriQrCodeView(
|
||||||
coin: coin,
|
coin: coin,
|
||||||
receivingAddress: receivingAddress,
|
receivingAddress: _qrcodeContent ?? "",
|
||||||
),
|
),
|
||||||
settings: const RouteSettings(
|
settings: const RouteSettings(
|
||||||
name: GenerateUriQrCodeView.routeName,
|
name: GenerateUriQrCodeView.routeName,
|
||||||
|
|
|
@ -48,10 +48,12 @@ import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/prefs.dart';
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
||||||
import 'package:stackwallet/widgets/animated_text.dart';
|
import 'package:stackwallet/widgets/animated_text.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
|
@ -112,7 +114,6 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
String? _note;
|
String? _note;
|
||||||
String? _onChainNote;
|
String? _onChainNote;
|
||||||
|
|
||||||
Amount? _amountToSend;
|
|
||||||
Amount? _cachedAmountToSend;
|
Amount? _cachedAmountToSend;
|
||||||
String? _address;
|
String? _address;
|
||||||
|
|
||||||
|
@ -137,20 +138,22 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
Future<void> previewSend() async {
|
Future<void> previewSend() async {
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
|
|
||||||
final Amount amount = _amountToSend!;
|
final Amount amount = ref.read(pSendAmount)!;
|
||||||
final Amount availableBalance;
|
final Amount availableBalance;
|
||||||
if ((coin == Coin.firo || coin == Coin.firoTestNet)) {
|
if ((coin == Coin.firo || coin == Coin.firoTestNet)) {
|
||||||
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
"Private") {
|
case FiroType.public:
|
||||||
availableBalance = wallet.info.cachedBalance.spendable;
|
availableBalance = wallet.info.cachedBalance.spendable;
|
||||||
// (manager.wallet as FiroWallet).availablePrivateBalance();
|
break;
|
||||||
} else {
|
case FiroType.lelantus:
|
||||||
availableBalance = wallet.info.cachedBalanceSecondary.spendable;
|
availableBalance = wallet.info.cachedBalanceSecondary.spendable;
|
||||||
// (manager.wallet as FiroWallet).availablePublicBalance();
|
break;
|
||||||
|
case FiroType.spark:
|
||||||
|
availableBalance = wallet.info.cachedBalanceTertiary.spendable;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
availableBalance = wallet.info.cachedBalance.spendable;
|
availableBalance = wallet.info.cachedBalance.spendable;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final coinControlEnabled =
|
final coinControlEnabled =
|
||||||
|
@ -312,14 +315,71 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (wallet is FiroWallet &&
|
} else if (wallet is FiroWallet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state ==
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
"Private") {
|
case FiroType.public:
|
||||||
|
if (ref.read(pValidSparkSendToAddress)) {
|
||||||
|
txDataFuture = wallet.prepareSparkMintTransaction(
|
||||||
|
txData: TxData(
|
||||||
|
sparkRecipients: [
|
||||||
|
(
|
||||||
|
address: _address!,
|
||||||
|
amount: amount,
|
||||||
|
memo: memoController.text,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
feeRateType: ref.read(feeRateTypeStateProvider),
|
||||||
|
satsPerVByte: isCustomFee ? customFeeRate : null,
|
||||||
|
utxos: (wallet is CoinControlInterface &&
|
||||||
|
coinControlEnabled &&
|
||||||
|
ref.read(desktopUseUTXOs).isNotEmpty)
|
||||||
|
? ref.read(desktopUseUTXOs)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
txDataFuture = wallet.prepareSend(
|
||||||
|
txData: TxData(
|
||||||
|
recipients: [(address: _address!, amount: amount)],
|
||||||
|
feeRateType: ref.read(feeRateTypeStateProvider),
|
||||||
|
satsPerVByte: isCustomFee ? customFeeRate : null,
|
||||||
|
utxos: (wallet is CoinControlInterface &&
|
||||||
|
coinControlEnabled &&
|
||||||
|
ref.read(desktopUseUTXOs).isNotEmpty)
|
||||||
|
? ref.read(desktopUseUTXOs)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FiroType.lelantus:
|
||||||
txDataFuture = wallet.prepareSendLelantus(
|
txDataFuture = wallet.prepareSendLelantus(
|
||||||
txData: TxData(
|
txData: TxData(
|
||||||
recipients: [(address: _address!, amount: amount)],
|
recipients: [(address: _address!, amount: amount)],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FiroType.spark:
|
||||||
|
txDataFuture = wallet.prepareSendSpark(
|
||||||
|
txData: TxData(
|
||||||
|
recipients: ref.read(pValidSparkSendToAddress)
|
||||||
|
? null
|
||||||
|
: [(address: _address!, amount: amount)],
|
||||||
|
sparkRecipients: ref.read(pValidSparkSendToAddress)
|
||||||
|
? [
|
||||||
|
(
|
||||||
|
address: _address!,
|
||||||
|
amount: amount,
|
||||||
|
memo: memoController.text,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
final memo = isStellar ? memoController.text : null;
|
final memo = isStellar ? memoController.text : null;
|
||||||
txDataFuture = wallet.prepareSend(
|
txDataFuture = wallet.prepareSend(
|
||||||
|
@ -382,7 +442,8 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e, s) {
|
||||||
|
Logging.instance.log("Desktop send: $e\n$s", level: LogLevel.Warning);
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
// pop building dialog
|
// pop building dialog
|
||||||
Navigator.of(
|
Navigator.of(
|
||||||
|
@ -469,21 +530,21 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
final cryptoAmount = ref.read(pAmountFormatter(coin)).tryParse(
|
||||||
cryptoAmountController.text,
|
cryptoAmountController.text,
|
||||||
);
|
);
|
||||||
|
final Amount? amount;
|
||||||
if (cryptoAmount != null) {
|
if (cryptoAmount != null) {
|
||||||
_amountToSend = cryptoAmount;
|
amount = cryptoAmount;
|
||||||
if (_cachedAmountToSend != null &&
|
if (_cachedAmountToSend != null && _cachedAmountToSend == amount) {
|
||||||
_cachedAmountToSend == _amountToSend) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend",
|
Logging.instance.log("it changed $amount $_cachedAmountToSend",
|
||||||
level: LogLevel.Info);
|
level: LogLevel.Info);
|
||||||
_cachedAmountToSend = _amountToSend;
|
_cachedAmountToSend = amount;
|
||||||
|
|
||||||
final price =
|
final price =
|
||||||
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
||||||
|
|
||||||
if (price > Decimal.zero) {
|
if (price > Decimal.zero) {
|
||||||
final String fiatAmountString = (_amountToSend!.decimal * price)
|
final String fiatAmountString = (amount!.decimal * price)
|
||||||
.toAmount(fractionDigits: 2)
|
.toAmount(fractionDigits: 2)
|
||||||
.fiatString(
|
.fiatString(
|
||||||
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
||||||
|
@ -492,44 +553,29 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
baseAmountController.text = fiatAmountString;
|
baseAmountController.text = fiatAmountString;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_amountToSend = null;
|
amount = null;
|
||||||
_cachedAmountToSend = null;
|
_cachedAmountToSend = null;
|
||||||
baseAmountController.text = "";
|
baseAmountController.text = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
ref.read(pSendAmount.notifier).state = amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _updateInvalidAddressText(String address) {
|
// String? _updateInvalidAddressText(String address) {
|
||||||
if (_data != null && _data!.contactLabel == address) {
|
// if (_data != null && _data!.contactLabel == address) {
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
if (address.isNotEmpty &&
|
// if (address.isNotEmpty &&
|
||||||
!ref
|
// !ref
|
||||||
.read(pWallets)
|
// .read(pWallets)
|
||||||
.getWallet(walletId)
|
// .getWallet(walletId)
|
||||||
.cryptoCurrency
|
// .cryptoCurrency
|
||||||
.validateAddress(address)) {
|
// .validateAddress(address)) {
|
||||||
return "Invalid address";
|
// return "Invalid address";
|
||||||
}
|
// }
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
void _updatePreviewButtonState(String? address, Amount? amount) {
|
|
||||||
if (isPaynymSend) {
|
|
||||||
ref.read(previewTxButtonStateProvider.state).state =
|
|
||||||
(amount != null && amount > Amount.zero);
|
|
||||||
} else {
|
|
||||||
final isValidAddress = ref
|
|
||||||
.read(pWallets)
|
|
||||||
.getWallet(walletId)
|
|
||||||
.cryptoCurrency
|
|
||||||
.validateAddress(address ?? "");
|
|
||||||
ref.read(previewTxButtonStateProvider.state).state =
|
|
||||||
(isValidAddress && amount != null && amount > Amount.zero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> scanQr() async {
|
Future<void> scanQr() async {
|
||||||
try {
|
try {
|
||||||
|
@ -567,10 +613,9 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
cryptoAmountController.text = ref
|
cryptoAmountController.text = ref
|
||||||
.read(pAmountFormatter(coin))
|
.read(pAmountFormatter(coin))
|
||||||
.format(amount, withUnitName: false);
|
.format(amount, withUnitName: false);
|
||||||
_amountToSend = amount;
|
ref.read(pSendAmount.notifier).state = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = sendToController.text.isNotEmpty;
|
_addressToggleFlag = sendToController.text.isNotEmpty;
|
||||||
});
|
});
|
||||||
|
@ -584,7 +629,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
_address = qrResult.rawContent;
|
_address = qrResult.rawContent;
|
||||||
sendToController.text = _address ?? "";
|
sendToController.text = _address ?? "";
|
||||||
|
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
_setValidAddressProviders(_address);
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = sendToController.text.isNotEmpty;
|
_addressToggleFlag = sendToController.text.isNotEmpty;
|
||||||
});
|
});
|
||||||
|
@ -598,6 +643,25 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _setValidAddressProviders(String? address) {
|
||||||
|
if (isPaynymSend) {
|
||||||
|
ref.read(pValidSendToAddress.notifier).state = true;
|
||||||
|
} else {
|
||||||
|
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||||
|
if (wallet is SparkInterface) {
|
||||||
|
ref.read(pValidSparkSendToAddress.notifier).state =
|
||||||
|
SparkInterface.validateSparkAddress(
|
||||||
|
address: address ?? "",
|
||||||
|
isTestNet:
|
||||||
|
wallet.cryptoCurrency.network == CryptoCurrencyNetwork.test,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.read(pValidSendToAddress.notifier).state =
|
||||||
|
wallet.cryptoCurrency.validateAddress(address ?? "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> pasteAddress() async {
|
Future<void> pasteAddress() async {
|
||||||
final ClipboardData? data = await clipboard.getData(Clipboard.kTextPlain);
|
final ClipboardData? data = await clipboard.getData(Clipboard.kTextPlain);
|
||||||
if (data?.text != null && data!.text!.isNotEmpty) {
|
if (data?.text != null && data!.text!.isNotEmpty) {
|
||||||
|
@ -614,7 +678,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
sendToController.text = content;
|
sendToController.text = content;
|
||||||
_address = content;
|
_address = content;
|
||||||
|
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
_setValidAddressProviders(_address);
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = sendToController.text.isNotEmpty;
|
_addressToggleFlag = sendToController.text.isNotEmpty;
|
||||||
});
|
});
|
||||||
|
@ -643,28 +707,29 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
baseAmountString,
|
baseAmountString,
|
||||||
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
locale: ref.read(localeServiceChangeNotifierProvider).locale,
|
||||||
);
|
);
|
||||||
|
final Amount? amount;
|
||||||
if (baseAmount != null) {
|
if (baseAmount != null) {
|
||||||
final _price =
|
final _price =
|
||||||
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
|
||||||
|
|
||||||
if (_price == Decimal.zero) {
|
if (_price == Decimal.zero) {
|
||||||
_amountToSend = Decimal.zero.toAmount(fractionDigits: coin.decimals);
|
amount = Decimal.zero.toAmount(fractionDigits: coin.decimals);
|
||||||
} else {
|
} else {
|
||||||
_amountToSend = baseAmount <= Amount.zero
|
amount = baseAmount <= Amount.zero
|
||||||
? Decimal.zero.toAmount(fractionDigits: coin.decimals)
|
? Decimal.zero.toAmount(fractionDigits: coin.decimals)
|
||||||
: (baseAmount.decimal / _price)
|
: (baseAmount.decimal / _price)
|
||||||
.toDecimal(scaleOnInfinitePrecision: coin.decimals)
|
.toDecimal(scaleOnInfinitePrecision: coin.decimals)
|
||||||
.toAmount(fractionDigits: coin.decimals);
|
.toAmount(fractionDigits: coin.decimals);
|
||||||
}
|
}
|
||||||
if (_cachedAmountToSend != null && _cachedAmountToSend == _amountToSend) {
|
if (_cachedAmountToSend != null && _cachedAmountToSend == amount) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_cachedAmountToSend = _amountToSend;
|
_cachedAmountToSend = amount;
|
||||||
Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend",
|
Logging.instance
|
||||||
level: LogLevel.Info);
|
.log("it changed $amount $_cachedAmountToSend", level: LogLevel.Info);
|
||||||
|
|
||||||
final amountString = ref.read(pAmountFormatter(coin)).format(
|
final amountString = ref.read(pAmountFormatter(coin)).format(
|
||||||
_amountToSend!,
|
amount!,
|
||||||
withUnitName: false,
|
withUnitName: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -672,7 +737,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
cryptoAmountController.text = amountString;
|
cryptoAmountController.text = amountString;
|
||||||
_cryptoAmountChangeLock = false;
|
_cryptoAmountChangeLock = false;
|
||||||
} else {
|
} else {
|
||||||
_amountToSend = Decimal.zero.toAmount(fractionDigits: coin.decimals);
|
amount = Decimal.zero.toAmount(fractionDigits: coin.decimals);
|
||||||
_cryptoAmountChangeLock = true;
|
_cryptoAmountChangeLock = true;
|
||||||
cryptoAmountController.text = "";
|
cryptoAmountController.text = "";
|
||||||
_cryptoAmountChangeLock = false;
|
_cryptoAmountChangeLock = false;
|
||||||
|
@ -682,17 +747,29 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
// Format.decimalAmountToSatoshis(
|
// Format.decimalAmountToSatoshis(
|
||||||
// _amountToSend!));
|
// _amountToSend!));
|
||||||
// });
|
// });
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
ref.read(pSendAmount.notifier).state = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendAllTapped() async {
|
Future<void> sendAllTapped() async {
|
||||||
final info = ref.read(pWalletInfo(walletId));
|
final info = ref.read(pWalletInfo(walletId));
|
||||||
|
|
||||||
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state == "Private") {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
|
case FiroType.public:
|
||||||
|
cryptoAmountController.text = info.cachedBalance.spendable.decimal
|
||||||
|
.toStringAsFixed(coin.decimals);
|
||||||
|
break;
|
||||||
|
case FiroType.lelantus:
|
||||||
cryptoAmountController.text = info
|
cryptoAmountController.text = info
|
||||||
.cachedBalanceSecondary.spendable.decimal
|
.cachedBalanceSecondary.spendable.decimal
|
||||||
.toStringAsFixed(coin.decimals);
|
.toStringAsFixed(coin.decimals);
|
||||||
|
break;
|
||||||
|
case FiroType.spark:
|
||||||
|
cryptoAmountController.text = info
|
||||||
|
.cachedBalanceTertiary.spendable.decimal
|
||||||
|
.toStringAsFixed(coin.decimals);
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cryptoAmountController.text =
|
cryptoAmountController.text =
|
||||||
info.cachedBalance.spendable.decimal.toStringAsFixed(coin.decimals);
|
info.cachedBalance.spendable.decimal.toStringAsFixed(coin.decimals);
|
||||||
|
@ -700,11 +777,12 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showDesktopCoinControl() async {
|
void _showDesktopCoinControl() async {
|
||||||
|
final amount = ref.read(pSendAmount);
|
||||||
await showDialog<void>(
|
await showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => DesktopCoinControlUseDialog(
|
builder: (context) => DesktopCoinControlUseDialog(
|
||||||
walletId: widget.walletId,
|
walletId: widget.walletId,
|
||||||
amountToSend: _amountToSend,
|
amountToSend: amount,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -713,7 +791,8 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
void initState() {
|
void initState() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
ref.refresh(feeSheetSessionCacheProvider);
|
ref.refresh(feeSheetSessionCacheProvider);
|
||||||
ref.read(previewTxButtonStateProvider.state).state = false;
|
ref.read(pValidSendToAddress.state).state = false;
|
||||||
|
ref.read(pValidSparkSendToAddress.state).state = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// _calculateFeesFuture = calculateFees(0);
|
// _calculateFeesFuture = calculateFees(0);
|
||||||
|
@ -748,20 +827,20 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
|
|
||||||
_cryptoFocus.addListener(() {
|
_cryptoFocus.addListener(() {
|
||||||
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
||||||
if (_amountToSend == null) {
|
if (ref.read(pSendAmount) == null) {
|
||||||
ref.refresh(sendAmountProvider);
|
ref.refresh(sendAmountProvider);
|
||||||
} else {
|
} else {
|
||||||
ref.read(sendAmountProvider.state).state = _amountToSend!;
|
ref.read(sendAmountProvider.state).state = ref.read(pSendAmount)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_baseFocus.addListener(() {
|
_baseFocus.addListener(() {
|
||||||
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
||||||
if (_amountToSend == null) {
|
if (ref.read(pSendAmount) == null) {
|
||||||
ref.refresh(sendAmountProvider);
|
ref.refresh(sendAmountProvider);
|
||||||
} else {
|
} else {
|
||||||
ref.read(sendAmountProvider.state).state = _amountToSend!;
|
ref.read(sendAmountProvider.state).state = ref.read(pSendAmount)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -821,7 +900,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 4,
|
height: 4,
|
||||||
),
|
),
|
||||||
if (coin == Coin.firo)
|
if (coin == Coin.firo || coin == Coin.firoTestNet)
|
||||||
Text(
|
Text(
|
||||||
"Send from",
|
"Send from",
|
||||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
@ -831,22 +910,42 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
if (coin == Coin.firo)
|
if (coin == Coin.firo || coin == Coin.firoTestNet)
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
if (coin == Coin.firo)
|
if (coin == Coin.firo || coin == Coin.firoTestNet)
|
||||||
DropdownButtonHideUnderline(
|
DropdownButtonHideUnderline(
|
||||||
child: DropdownButton2(
|
child: DropdownButton2(
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
value: ref.watch(publicPrivateBalanceStateProvider.state).state,
|
value: ref.watch(publicPrivateBalanceStateProvider.state).state,
|
||||||
items: [
|
items: [
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: "Private",
|
value: FiroType.spark,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Private balance",
|
"Spark balance",
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
ref.watch(pAmountFormatter(coin)).format(ref
|
||||||
|
.watch(pWalletBalanceTertiary(walletId))
|
||||||
|
.spendable),
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: FiroType.lelantus,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Lelantus balance",
|
||||||
style: STextStyles.itemSubtitle12(context),
|
style: STextStyles.itemSubtitle12(context),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
@ -862,7 +961,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: "Public",
|
value: FiroType.public,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
|
@ -882,9 +981,9 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value is String) {
|
if (value is FiroType) {
|
||||||
setState(() {
|
setState(() {
|
||||||
ref.watch(publicPrivateBalanceStateProvider.state).state =
|
ref.read(publicPrivateBalanceStateProvider.state).state =
|
||||||
value;
|
value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -917,7 +1016,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (coin == Coin.firo)
|
if (coin == Coin.firo || coin == Coin.firoTestNet)
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
|
@ -1159,7 +1258,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
onChanged: (newValue) {
|
onChanged: (newValue) {
|
||||||
_address = newValue;
|
_address = newValue;
|
||||||
_updatePreviewButtonState(_address, _amountToSend);
|
_setValidAddressProviders(_address);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = newValue.isNotEmpty;
|
_addressToggleFlag = newValue.isNotEmpty;
|
||||||
|
@ -1199,8 +1298,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
onTap: () {
|
onTap: () {
|
||||||
sendToController.text = "";
|
sendToController.text = "";
|
||||||
_address = "";
|
_address = "";
|
||||||
_updatePreviewButtonState(
|
_setValidAddressProviders(_address);
|
||||||
_address, _amountToSend);
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = false;
|
_addressToggleFlag = false;
|
||||||
});
|
});
|
||||||
|
@ -1261,10 +1359,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
|
|
||||||
_address = entry.address;
|
_address = entry.address;
|
||||||
|
|
||||||
_updatePreviewButtonState(
|
_setValidAddressProviders(_address);
|
||||||
_address,
|
|
||||||
_amountToSend,
|
|
||||||
);
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = true;
|
_addressToggleFlag = true;
|
||||||
|
@ -1289,9 +1384,44 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
if (!isPaynymSend)
|
if (!isPaynymSend)
|
||||||
Builder(
|
Builder(
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
final error = _updateInvalidAddressText(
|
final String? error;
|
||||||
_address ?? "",
|
|
||||||
);
|
if (_address == null || _address!.isEmpty) {
|
||||||
|
error = null;
|
||||||
|
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
|
if (ref.watch(publicPrivateBalanceStateProvider) ==
|
||||||
|
FiroType.lelantus) {
|
||||||
|
if (_data != null && _data!.contactLabel == _address) {
|
||||||
|
error = SparkInterface.validateSparkAddress(
|
||||||
|
address: _data!.address, isTestNet: coin.isTestNet)
|
||||||
|
? "Lelantus to Spark not supported"
|
||||||
|
: null;
|
||||||
|
} else if (ref.watch(pValidSparkSendToAddress)) {
|
||||||
|
error = "Lelantus to Spark not supported";
|
||||||
|
} else {
|
||||||
|
error = ref.watch(pValidSendToAddress)
|
||||||
|
? null
|
||||||
|
: "Invalid address";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_data != null && _data!.contactLabel == _address) {
|
||||||
|
error = null;
|
||||||
|
} else if (!ref.watch(pValidSendToAddress) &&
|
||||||
|
!ref.watch(pValidSparkSendToAddress)) {
|
||||||
|
error = "Invalid address";
|
||||||
|
} else {
|
||||||
|
error = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_data != null && _data!.contactLabel == _address) {
|
||||||
|
error = null;
|
||||||
|
} else if (!ref.watch(pValidSendToAddress)) {
|
||||||
|
error = "Invalid address";
|
||||||
|
} else {
|
||||||
|
error = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (error == null || error.isEmpty) {
|
if (error == null || error.isEmpty) {
|
||||||
return Container();
|
return Container();
|
||||||
|
@ -1317,11 +1447,17 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (isStellar)
|
if (isStellar ||
|
||||||
|
(ref.watch(pValidSparkSendToAddress) &&
|
||||||
|
ref.watch(publicPrivateBalanceStateProvider) !=
|
||||||
|
FiroType.lelantus))
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
if (isStellar)
|
if (isStellar ||
|
||||||
|
(ref.watch(pValidSparkSendToAddress) &&
|
||||||
|
ref.watch(publicPrivateBalanceStateProvider) !=
|
||||||
|
FiroType.lelantus))
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
Constants.size.circularBorderRadius,
|
Constants.size.circularBorderRadius,
|
||||||
|
@ -1387,8 +1523,12 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
ConditionalParent(
|
ConditionalParent(
|
||||||
condition: coin.isElectrumXCoin &&
|
condition: coin.isElectrumXCoin &&
|
||||||
!(((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
!(((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||||
ref.read(publicPrivateBalanceStateProvider.state).state ==
|
(ref.watch(publicPrivateBalanceStateProvider.state).state ==
|
||||||
"Private")),
|
FiroType.lelantus ||
|
||||||
|
ref
|
||||||
|
.watch(publicPrivateBalanceStateProvider.state)
|
||||||
|
.state ==
|
||||||
|
FiroType.spark))),
|
||||||
builder: (child) => Row(
|
builder: (child) => Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
@ -1487,14 +1627,32 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
publicPrivateBalanceStateProvider
|
publicPrivateBalanceStateProvider
|
||||||
.state)
|
.state)
|
||||||
.state !=
|
.state !=
|
||||||
"Private") {
|
FiroType.public) {
|
||||||
throw UnimplementedError("FIXME");
|
final firoWallet = wallet as FiroWallet;
|
||||||
// TODO: [prio=high] firo fee fix
|
|
||||||
// ref
|
if (ref
|
||||||
// .read(feeSheetSessionCacheProvider)
|
.read(
|
||||||
// .average[amount] = await (manager.wallet
|
publicPrivateBalanceStateProvider
|
||||||
// as FiroWallet)
|
.state)
|
||||||
// .estimateFeeForPublic(amount, feeRate);
|
.state ==
|
||||||
|
FiroType.lelantus) {
|
||||||
|
ref
|
||||||
|
.read(feeSheetSessionCacheProvider)
|
||||||
|
.average[amount] =
|
||||||
|
await firoWallet
|
||||||
|
.estimateFeeForLelantus(amount);
|
||||||
|
} else if (ref
|
||||||
|
.read(
|
||||||
|
publicPrivateBalanceStateProvider
|
||||||
|
.state)
|
||||||
|
.state ==
|
||||||
|
FiroType.spark) {
|
||||||
|
ref
|
||||||
|
.read(feeSheetSessionCacheProvider)
|
||||||
|
.average[amount] =
|
||||||
|
await firoWallet
|
||||||
|
.estimateFeeForSpark(amount);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ref
|
ref
|
||||||
.read(feeSheetSessionCacheProvider)
|
.read(feeSheetSessionCacheProvider)
|
||||||
|
@ -1532,7 +1690,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
.watch(
|
.watch(
|
||||||
publicPrivateBalanceStateProvider.state)
|
publicPrivateBalanceStateProvider.state)
|
||||||
.state ==
|
.state ==
|
||||||
"Private"
|
FiroType.lelantus
|
||||||
? Text(
|
? Text(
|
||||||
"~${ref.watch(pAmountFormatter(coin)).format(
|
"~${ref.watch(pAmountFormatter(coin)).format(
|
||||||
Amount(
|
Amount(
|
||||||
|
@ -1595,10 +1753,9 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
buttonHeight: ButtonHeight.l,
|
buttonHeight: ButtonHeight.l,
|
||||||
label: "Preview send",
|
label: "Preview send",
|
||||||
enabled: ref.watch(previewTxButtonStateProvider.state).state,
|
enabled: ref.watch(pPreviewTxButtonEnabled(coin)),
|
||||||
onPressed: ref.watch(previewTxButtonStateProvider.state).state
|
onPressed:
|
||||||
? previewSend
|
ref.watch(pPreviewTxButtonEnabled(coin)) ? previewSend : null,
|
||||||
: null,
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -198,7 +198,8 @@ class _DesktopWalletFeaturesState extends ConsumerState<DesktopWalletFeatures> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await firoWallet.anonymizeAllPublicFunds();
|
// await firoWallet.anonymizeAllLelantus();
|
||||||
|
await firoWallet.anonymizeAllSpark();
|
||||||
shouldPop = true;
|
shouldPop = true;
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
Navigator.of(context, rootNavigator: true).pop();
|
Navigator.of(context, rootNavigator: true).pop();
|
||||||
|
|
|
@ -15,6 +15,7 @@ import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
|
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
|
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||||
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
|
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.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/stack_colors.dart';
|
||||||
|
@ -61,6 +62,7 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final coin = ref.watch(pWalletCoin(widget.walletId));
|
final coin = ref.watch(pWalletCoin(widget.walletId));
|
||||||
|
final isFiro = coin == Coin.firo || coin == Coin.firoTestNet;
|
||||||
final locale = ref.watch(
|
final locale = ref.watch(
|
||||||
localeServiceChangeNotifierProvider.select((value) => value.locale));
|
localeServiceChangeNotifierProvider.select((value) => value.locale));
|
||||||
|
|
||||||
|
@ -82,29 +84,30 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
|
||||||
ref.watch(walletBalanceToggleStateProvider.state).state ==
|
ref.watch(walletBalanceToggleStateProvider.state).state ==
|
||||||
WalletBalanceToggleState.available;
|
WalletBalanceToggleState.available;
|
||||||
|
|
||||||
|
final Amount balanceToShow;
|
||||||
|
if (isFiro) {
|
||||||
|
switch (ref.watch(publicPrivateBalanceStateProvider.state).state) {
|
||||||
|
case FiroType.spark:
|
||||||
|
final balance = ref.watch(pWalletBalanceTertiary(walletId));
|
||||||
|
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FiroType.lelantus:
|
||||||
|
final balance = ref.watch(pWalletBalanceSecondary(walletId));
|
||||||
|
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FiroType.public:
|
||||||
|
final balance = ref.watch(pWalletBalance(walletId));
|
||||||
|
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Balance balance = widget.isToken
|
Balance balance = widget.isToken
|
||||||
? ref.watch(tokenServiceProvider.select((value) => value!.balance))
|
? ref.watch(tokenServiceProvider.select((value) => value!.balance))
|
||||||
: ref.watch(pWalletBalance(walletId));
|
: ref.watch(pWalletBalance(walletId));
|
||||||
|
|
||||||
Amount balanceToShow;
|
balanceToShow = _showAvailable ? balance.spendable : balance.total;
|
||||||
if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
|
||||||
final balanceSecondary = ref.watch(pWalletBalanceSecondary(walletId));
|
|
||||||
final showPrivate =
|
|
||||||
ref.watch(walletPrivateBalanceToggleStateProvider.state).state ==
|
|
||||||
WalletBalanceToggleState.available;
|
|
||||||
|
|
||||||
if (_showAvailable) {
|
|
||||||
balanceToShow =
|
|
||||||
showPrivate ? balanceSecondary.spendable : balance.spendable;
|
|
||||||
} else {
|
|
||||||
balanceToShow = showPrivate ? balanceSecondary.total : balance.total;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_showAvailable) {
|
|
||||||
balanceToShow = balance.spendable;
|
|
||||||
} else {
|
|
||||||
balanceToShow = balance.total;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Consumer(
|
return Consumer(
|
||||||
|
|
|
@ -19,6 +19,7 @@ import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_set
|
||||||
import 'package:stackwallet/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart';
|
import 'package:stackwallet/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/lelantus_coins/lelantus_coins_view.dart';
|
import 'package:stackwallet/pages_desktop_specific/lelantus_coins/lelantus_coins_view.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart';
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart';
|
||||||
|
import 'package:stackwallet/pages_desktop_specific/spark_coins/spark_coins_view.dart';
|
||||||
import 'package:stackwallet/route_generator.dart';
|
import 'package:stackwallet/route_generator.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
|
@ -32,7 +33,8 @@ enum _WalletOptions {
|
||||||
deleteWallet,
|
deleteWallet,
|
||||||
changeRepresentative,
|
changeRepresentative,
|
||||||
showXpub,
|
showXpub,
|
||||||
lelantusCoins;
|
lelantusCoins,
|
||||||
|
sparkCoins;
|
||||||
|
|
||||||
String get prettyName {
|
String get prettyName {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
@ -46,6 +48,8 @@ enum _WalletOptions {
|
||||||
return "Show xPub";
|
return "Show xPub";
|
||||||
case _WalletOptions.lelantusCoins:
|
case _WalletOptions.lelantusCoins:
|
||||||
return "Lelantus Coins";
|
return "Lelantus Coins";
|
||||||
|
case _WalletOptions.sparkCoins:
|
||||||
|
return "Spark Coins";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,6 +93,9 @@ class WalletOptionsButton extends StatelessWidget {
|
||||||
onFiroShowLelantusCoins: () async {
|
onFiroShowLelantusCoins: () async {
|
||||||
Navigator.of(context).pop(_WalletOptions.lelantusCoins);
|
Navigator.of(context).pop(_WalletOptions.lelantusCoins);
|
||||||
},
|
},
|
||||||
|
onFiroShowSparkCoins: () async {
|
||||||
|
Navigator.of(context).pop(_WalletOptions.sparkCoins);
|
||||||
|
},
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -191,6 +198,15 @@ class WalletOptionsButton extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case _WalletOptions.sparkCoins:
|
||||||
|
unawaited(
|
||||||
|
Navigator.of(context).pushNamed(
|
||||||
|
SparkCoinsView.routeName,
|
||||||
|
arguments: walletId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -224,6 +240,7 @@ class WalletOptionsPopupMenu extends ConsumerWidget {
|
||||||
required this.onShowXpubPressed,
|
required this.onShowXpubPressed,
|
||||||
required this.onChangeRepPressed,
|
required this.onChangeRepPressed,
|
||||||
required this.onFiroShowLelantusCoins,
|
required this.onFiroShowLelantusCoins,
|
||||||
|
required this.onFiroShowSparkCoins,
|
||||||
required this.walletId,
|
required this.walletId,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@ -232,6 +249,7 @@ class WalletOptionsPopupMenu extends ConsumerWidget {
|
||||||
final VoidCallback onShowXpubPressed;
|
final VoidCallback onShowXpubPressed;
|
||||||
final VoidCallback onChangeRepPressed;
|
final VoidCallback onChangeRepPressed;
|
||||||
final VoidCallback onFiroShowLelantusCoins;
|
final VoidCallback onFiroShowLelantusCoins;
|
||||||
|
final VoidCallback onFiroShowSparkCoins;
|
||||||
final String walletId;
|
final String walletId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -374,6 +392,43 @@ class WalletOptionsPopupMenu extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (firoDebug)
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
if (firoDebug)
|
||||||
|
TransparentButton(
|
||||||
|
onPressed: onFiroShowSparkCoins,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(
|
||||||
|
Assets.svg.eye,
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveSearchIconLeft,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 14),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_WalletOptions.sparkCoins.prettyName,
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(
|
||||||
|
context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textDark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
if (xpubEnabled)
|
if (xpubEnabled)
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
|
|
|
@ -65,7 +65,14 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
|
||||||
bool get fieldsMatch =>
|
bool get fieldsMatch =>
|
||||||
passwordController.text == passwordRepeatController.text;
|
passwordController.text == passwordRepeatController.text;
|
||||||
|
|
||||||
|
bool _nextLock = false;
|
||||||
|
|
||||||
void onNextPressed() async {
|
void onNextPressed() async {
|
||||||
|
if (_nextLock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_nextLock = true;
|
||||||
|
|
||||||
final String passphrase = passwordController.text;
|
final String passphrase = passwordController.text;
|
||||||
final String repeatPassphrase = passwordRepeatController.text;
|
final String repeatPassphrase = passwordRepeatController.text;
|
||||||
|
|
||||||
|
@ -75,6 +82,7 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
|
||||||
message: "A password is required",
|
message: "A password is required",
|
||||||
context: context,
|
context: context,
|
||||||
));
|
));
|
||||||
|
_nextLock = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (passphrase != repeatPassphrase) {
|
if (passphrase != repeatPassphrase) {
|
||||||
|
@ -83,6 +91,7 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
|
||||||
message: "Password does not match",
|
message: "Password does not match",
|
||||||
context: context,
|
context: context,
|
||||||
));
|
));
|
||||||
|
_nextLock = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +115,7 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
|
||||||
message: "Error: $e",
|
message: "Error: $e",
|
||||||
context: context,
|
context: context,
|
||||||
));
|
));
|
||||||
|
_nextLock = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +142,7 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
|
||||||
context: context,
|
context: context,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
_nextLock = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -79,11 +79,18 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _loginLock = false;
|
||||||
Future<void> login() async {
|
Future<void> login() async {
|
||||||
|
if (_loginLock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_loginLock = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
unawaited(
|
unawaited(
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
builder: (context) => const Column(
|
builder: (context) => const Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
@ -138,6 +145,8 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
|
||||||
context: context,
|
context: context,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
_loginLock = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
267
lib/pages_desktop_specific/spark_coins/spark_coins_view.dart
Normal file
267
lib/pages_desktop_specific/spark_coins/spark_coins_view.dart
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Stack Wallet.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Cypher Stack
|
||||||
|
* All Rights Reserved.
|
||||||
|
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
||||||
|
* Generated by Cypher Stack on 2023-05-26
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||||
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/models/spark_coin.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';
|
||||||
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
|
||||||
|
class SparkCoinsView extends ConsumerStatefulWidget {
|
||||||
|
const SparkCoinsView({
|
||||||
|
Key? key,
|
||||||
|
required this.walletId,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
static const String routeName = "/sparkCoinsView";
|
||||||
|
|
||||||
|
final String walletId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<SparkCoinsView> createState() => _SparkCoinsViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SparkCoinsViewState extends ConsumerState<SparkCoinsView> {
|
||||||
|
List<SparkCoin> _coins = [];
|
||||||
|
|
||||||
|
Stream<List<SparkCoin>>? sparkCoinsCollectionWatcher;
|
||||||
|
|
||||||
|
void _onSparkCoinsCollectionWatcherEvent(List<SparkCoin> coins) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_coins = coins;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
sparkCoinsCollectionWatcher = ref
|
||||||
|
.read(mainDBProvider)
|
||||||
|
.isar
|
||||||
|
.sparkCoins
|
||||||
|
.where()
|
||||||
|
.walletIdEqualToAnyLTagHash(widget.walletId)
|
||||||
|
.sortByHeightDesc()
|
||||||
|
.watch(fireImmediately: true);
|
||||||
|
sparkCoinsCollectionWatcher!
|
||||||
|
.listen((data) => _onSparkCoinsCollectionWatcherEvent(data));
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
sparkCoinsCollectionWatcher = null;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DesktopScaffold(
|
||||||
|
appBar: DesktopAppBar(
|
||||||
|
background: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||||
|
leading: Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
width: 32,
|
||||||
|
),
|
||||||
|
AppBarIconButton(
|
||||||
|
size: 32,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldDefaultBG,
|
||||||
|
shadows: const [],
|
||||||
|
icon: SvgPicture.asset(
|
||||||
|
Assets.svg.arrowLeft,
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.topNavIconPrimary,
|
||||||
|
),
|
||||||
|
onPressed: Navigator.of(context).pop,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Spark Coins",
|
||||||
|
style: STextStyles.desktopH3(context),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
useSpacers: false,
|
||||||
|
isCompactHeight: true,
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: RoundedWhiteContainer(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 9,
|
||||||
|
child: Text(
|
||||||
|
"TXID",
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 9,
|
||||||
|
child: Text(
|
||||||
|
"LTag Hash",
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Text(
|
||||||
|
"Value (sats)",
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Text(
|
||||||
|
"Height",
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Text(
|
||||||
|
"Group Id",
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Text(
|
||||||
|
"Type",
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Text(
|
||||||
|
"Used",
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _coins.length,
|
||||||
|
separatorBuilder: (_, __) => Container(
|
||||||
|
height: 1,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.backgroundAppBar,
|
||||||
|
),
|
||||||
|
itemBuilder: (_, index) => Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: RoundedWhiteContainer(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 9,
|
||||||
|
child: SelectableText(
|
||||||
|
_coins[index].txHash,
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 9,
|
||||||
|
child: SelectableText(
|
||||||
|
_coins[index].lTagHash,
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: SelectableText(
|
||||||
|
_coins[index].value.toString(),
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: SelectableText(
|
||||||
|
_coins[index].height.toString(),
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: SelectableText(
|
||||||
|
_coins[index].groupId.toString(),
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: SelectableText(
|
||||||
|
_coins[index].type.name,
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: SelectableText(
|
||||||
|
_coins[index].isUsed.toString(),
|
||||||
|
style: STextStyles.itemSubtitle12(context),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,9 +9,32 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||||
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
|
||||||
final previewTxButtonStateProvider = StateProvider.autoDispose<bool>((_) {
|
final pSendAmount = StateProvider.autoDispose<Amount?>((_) => null);
|
||||||
return false;
|
final pValidSendToAddress = StateProvider.autoDispose<bool>((_) => false);
|
||||||
|
final pValidSparkSendToAddress = StateProvider.autoDispose<bool>((_) => false);
|
||||||
|
|
||||||
|
final pPreviewTxButtonEnabled =
|
||||||
|
Provider.autoDispose.family<bool, Coin>((ref, coin) {
|
||||||
|
final amount = ref.watch(pSendAmount) ?? Amount.zero;
|
||||||
|
|
||||||
|
// TODO [prio=low]: move away from Coin
|
||||||
|
if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||||
|
if (ref.watch(publicPrivateBalanceStateProvider) == FiroType.lelantus) {
|
||||||
|
return ref.watch(pValidSendToAddress) &&
|
||||||
|
!ref.watch(pValidSparkSendToAddress) &&
|
||||||
|
amount > Amount.zero;
|
||||||
|
} else {
|
||||||
|
return (ref.watch(pValidSendToAddress) ||
|
||||||
|
ref.watch(pValidSparkSendToAddress)) &&
|
||||||
|
amount > Amount.zero;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ref.watch(pValidSendToAddress) && amount > Amount.zero;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final previewTokenTxButtonStateProvider = StateProvider.autoDispose<bool>((_) {
|
final previewTokenTxButtonStateProvider = StateProvider.autoDispose<bool>((_) {
|
||||||
|
|
|
@ -10,5 +10,11 @@
|
||||||
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
enum FiroType {
|
||||||
|
public,
|
||||||
|
lelantus,
|
||||||
|
spark;
|
||||||
|
}
|
||||||
|
|
||||||
final publicPrivateBalanceStateProvider =
|
final publicPrivateBalanceStateProvider =
|
||||||
StateProvider<String>((_) => "Private");
|
StateProvider<FiroType>((_) => FiroType.lelantus);
|
||||||
|
|
|
@ -14,7 +14,3 @@ import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
|
||||||
final walletBalanceToggleStateProvider =
|
final walletBalanceToggleStateProvider =
|
||||||
StateProvider.autoDispose<WalletBalanceToggleState>(
|
StateProvider.autoDispose<WalletBalanceToggleState>(
|
||||||
(ref) => WalletBalanceToggleState.full);
|
(ref) => WalletBalanceToggleState.full);
|
||||||
|
|
||||||
final walletPrivateBalanceToggleStateProvider =
|
|
||||||
StateProvider.autoDispose<WalletBalanceToggleState>(
|
|
||||||
(ref) => WalletBalanceToggleState.full);
|
|
||||||
|
|
|
@ -175,6 +175,7 @@ import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/nodes_
|
||||||
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/security_settings.dart';
|
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/security_settings.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/syncing_preferences_settings.dart';
|
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/syncing_preferences_settings.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart';
|
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart';
|
||||||
|
import 'package:stackwallet/pages_desktop_specific/spark_coins/spark_coins_view.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.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/events/global/wallet_sync_status_changed_event.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
@ -1858,6 +1859,20 @@ class RouteGenerator {
|
||||||
}
|
}
|
||||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
|
case SparkCoinsView.routeName:
|
||||||
|
if (args is String) {
|
||||||
|
return getRoute(
|
||||||
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
|
builder: (_) => SparkCoinsView(
|
||||||
|
walletId: args,
|
||||||
|
),
|
||||||
|
settings: RouteSettings(
|
||||||
|
name: settings.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
case DesktopCoinControlView.routeName:
|
case DesktopCoinControlView.routeName:
|
||||||
if (args is String) {
|
if (args is String) {
|
||||||
return getRoute(
|
return getRoute(
|
||||||
|
|
|
@ -611,7 +611,8 @@ abstract class EthereumAPI {
|
||||||
try {
|
try {
|
||||||
final response = await client.get(
|
final response = await client.get(
|
||||||
url: Uri.parse(
|
url: Uri.parse(
|
||||||
"$stackBaseServer/tokens?addrs=$contractAddress&parts=all",
|
// "$stackBaseServer/tokens?addrs=$contractAddress&parts=all",
|
||||||
|
"$stackBaseServer/names?terms=$contractAddress",
|
||||||
),
|
),
|
||||||
proxyInfo: Prefs.instance.useTor
|
proxyInfo: Prefs.instance.useTor
|
||||||
? TorService.sharedInstance.getProxyInfo()
|
? TorService.sharedInstance.getProxyInfo()
|
||||||
|
@ -621,6 +622,10 @@ abstract class EthereumAPI {
|
||||||
if (response.code == 200) {
|
if (response.code == 200) {
|
||||||
final json = jsonDecode(response.body) as Map;
|
final json = jsonDecode(response.body) as Map;
|
||||||
if (json["data"] is List) {
|
if (json["data"] is List) {
|
||||||
|
if ((json["data"] as List).isEmpty) {
|
||||||
|
throw EthApiException("Unknown token");
|
||||||
|
}
|
||||||
|
|
||||||
final map = Map<String, dynamic>.from(json["data"].first as Map);
|
final map = Map<String, dynamic>.from(json["data"].first as Map);
|
||||||
EthContract? token;
|
EthContract? token;
|
||||||
if (map["isErc20"] == true) {
|
if (map["isErc20"] == true) {
|
||||||
|
|
|
@ -113,6 +113,7 @@ mixin ElectrumXParsing {
|
||||||
outputs: List.unmodifiable(outputs),
|
outputs: List.unmodifiable(outputs),
|
||||||
subType: TransactionSubType.none,
|
subType: TransactionSubType.none,
|
||||||
type: TransactionType.unknown,
|
type: TransactionType.unknown,
|
||||||
|
otherData: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,6 @@ class Wallets {
|
||||||
late NodeService nodeService;
|
late NodeService nodeService;
|
||||||
late MainDB mainDB;
|
late MainDB mainDB;
|
||||||
|
|
||||||
bool get hasWallets => _wallets.isNotEmpty;
|
|
||||||
|
|
||||||
List<Wallet> get wallets => _wallets.values.toList();
|
List<Wallet> get wallets => _wallets.values.toList();
|
||||||
|
|
||||||
static bool hasLoaded = false;
|
static bool hasLoaded = false;
|
||||||
|
|
|
@ -26,6 +26,10 @@ class Amount {
|
||||||
fractionDigits: 0,
|
fractionDigits: 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Amount.zeroWith({required this.fractionDigits})
|
||||||
|
: assert(fractionDigits >= 0),
|
||||||
|
_value = BigInt.zero;
|
||||||
|
|
||||||
/// truncate decimal value to [fractionDigits] places
|
/// truncate decimal value to [fractionDigits] places
|
||||||
Amount.fromDecimal(Decimal amount, {required this.fractionDigits})
|
Amount.fromDecimal(Decimal amount, {required this.fractionDigits})
|
||||||
: assert(fractionDigits >= 0),
|
: assert(fractionDigits >= 0),
|
||||||
|
|
|
@ -42,7 +42,7 @@ enum Coin {
|
||||||
stellarTestnet,
|
stellarTestnet,
|
||||||
}
|
}
|
||||||
|
|
||||||
final int kTestNetCoinCount = 5; // Util.isDesktop ? 5 : 4;
|
final int kTestNetCoinCount = 6; // Util.isDesktop ? 5 : 4;
|
||||||
// remove firotestnet for now
|
// remove firotestnet for now
|
||||||
|
|
||||||
extension CoinExt on Coin {
|
extension CoinExt on Coin {
|
||||||
|
|
|
@ -20,16 +20,18 @@ abstract class StackFileSystem {
|
||||||
static Future<Directory> applicationRootDirectory() async {
|
static Future<Directory> applicationRootDirectory() async {
|
||||||
Directory appDirectory;
|
Directory appDirectory;
|
||||||
|
|
||||||
|
// if this is changed, the directories in libmonero must also be changed!!!!!
|
||||||
|
const dirName = "stackwallet";
|
||||||
|
|
||||||
// todo: can merge and do same as regular linux home dir?
|
// todo: can merge and do same as regular linux home dir?
|
||||||
if (Logging.isArmLinux) {
|
if (Logging.isArmLinux) {
|
||||||
appDirectory = await getApplicationDocumentsDirectory();
|
appDirectory = await getApplicationDocumentsDirectory();
|
||||||
appDirectory = Directory("${appDirectory.path}/.stackwallet");
|
appDirectory = Directory("${appDirectory.path}/.$dirName");
|
||||||
} else if (Platform.isLinux) {
|
} else if (Platform.isLinux) {
|
||||||
if (overrideDir != null) {
|
if (overrideDir != null) {
|
||||||
appDirectory = Directory(overrideDir!);
|
appDirectory = Directory(overrideDir!);
|
||||||
} else {
|
} else {
|
||||||
appDirectory =
|
appDirectory = Directory("${Platform.environment['HOME']}/.$dirName");
|
||||||
Directory("${Platform.environment['HOME']}/.stackwallet");
|
|
||||||
}
|
}
|
||||||
} else if (Platform.isWindows) {
|
} else if (Platform.isWindows) {
|
||||||
if (overrideDir != null) {
|
if (overrideDir != null) {
|
||||||
|
@ -42,7 +44,7 @@ abstract class StackFileSystem {
|
||||||
appDirectory = Directory(overrideDir!);
|
appDirectory = Directory(overrideDir!);
|
||||||
} else {
|
} else {
|
||||||
appDirectory = await getLibraryDirectory();
|
appDirectory = await getLibraryDirectory();
|
||||||
appDirectory = Directory("${appDirectory.path}/stackwallet");
|
appDirectory = Directory("${appDirectory.path}/$dirName");
|
||||||
}
|
}
|
||||||
} else if (Platform.isIOS) {
|
} else if (Platform.isIOS) {
|
||||||
// todo: check if we need different behaviour here
|
// todo: check if we need different behaviour here
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
|
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart';
|
import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
||||||
|
|
||||||
class Firo extends Bip39HDCurrency {
|
class Firo extends Bip39HDCurrency {
|
||||||
Firo(super.network) {
|
Firo(super.network) {
|
||||||
|
@ -132,9 +133,15 @@ class Firo extends Bip39HDCurrency {
|
||||||
coinlib.Address.fromString(address, networkParams);
|
coinlib.Address.fromString(address, networkParams);
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return false;
|
return validateSparkAddress(address);
|
||||||
}
|
}
|
||||||
// TODO: implement validateAddress for spark addresses?
|
}
|
||||||
|
|
||||||
|
bool validateSparkAddress(String address) {
|
||||||
|
return SparkInterface.validateSparkAddress(
|
||||||
|
address: address,
|
||||||
|
isTestNet: network == CryptoCurrencyNetwork.test,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
148
lib/wallets/isar/models/spark_coin.dart
Normal file
148
lib/wallets/isar/models/spark_coin.dart
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
|
part 'spark_coin.g.dart';
|
||||||
|
|
||||||
|
enum SparkCoinType {
|
||||||
|
mint(0),
|
||||||
|
spend(1);
|
||||||
|
|
||||||
|
const SparkCoinType(this.value);
|
||||||
|
|
||||||
|
final int value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Collection()
|
||||||
|
class SparkCoin {
|
||||||
|
Id id = Isar.autoIncrement;
|
||||||
|
|
||||||
|
@Index(
|
||||||
|
unique: true,
|
||||||
|
replace: true,
|
||||||
|
composite: [
|
||||||
|
CompositeIndex("lTagHash"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
final String walletId;
|
||||||
|
|
||||||
|
@enumerated
|
||||||
|
final SparkCoinType type;
|
||||||
|
|
||||||
|
final bool isUsed;
|
||||||
|
final int groupId;
|
||||||
|
|
||||||
|
final List<int>? nonce;
|
||||||
|
|
||||||
|
final String address;
|
||||||
|
final String txHash;
|
||||||
|
|
||||||
|
final String valueIntString;
|
||||||
|
|
||||||
|
final String? memo;
|
||||||
|
final List<int>? serialContext;
|
||||||
|
|
||||||
|
final String diversifierIntString;
|
||||||
|
final List<int>? encryptedDiversifier;
|
||||||
|
|
||||||
|
final List<int>? serial;
|
||||||
|
final List<int>? tag;
|
||||||
|
|
||||||
|
final String lTagHash;
|
||||||
|
|
||||||
|
final int? height;
|
||||||
|
|
||||||
|
final String? serializedCoinB64;
|
||||||
|
final String? contextB64;
|
||||||
|
|
||||||
|
@ignore
|
||||||
|
BigInt get value => BigInt.parse(valueIntString);
|
||||||
|
|
||||||
|
@ignore
|
||||||
|
BigInt get diversifier => BigInt.parse(diversifierIntString);
|
||||||
|
|
||||||
|
SparkCoin({
|
||||||
|
required this.walletId,
|
||||||
|
required this.type,
|
||||||
|
required this.isUsed,
|
||||||
|
required this.groupId,
|
||||||
|
this.nonce,
|
||||||
|
required this.address,
|
||||||
|
required this.txHash,
|
||||||
|
required this.valueIntString,
|
||||||
|
this.memo,
|
||||||
|
this.serialContext,
|
||||||
|
required this.diversifierIntString,
|
||||||
|
this.encryptedDiversifier,
|
||||||
|
this.serial,
|
||||||
|
this.tag,
|
||||||
|
required this.lTagHash,
|
||||||
|
this.height,
|
||||||
|
this.serializedCoinB64,
|
||||||
|
this.contextB64,
|
||||||
|
});
|
||||||
|
|
||||||
|
SparkCoin copyWith({
|
||||||
|
SparkCoinType? type,
|
||||||
|
bool? isUsed,
|
||||||
|
int? groupId,
|
||||||
|
List<int>? nonce,
|
||||||
|
String? address,
|
||||||
|
String? txHash,
|
||||||
|
BigInt? value,
|
||||||
|
String? memo,
|
||||||
|
List<int>? serialContext,
|
||||||
|
BigInt? diversifier,
|
||||||
|
List<int>? encryptedDiversifier,
|
||||||
|
List<int>? serial,
|
||||||
|
List<int>? tag,
|
||||||
|
String? lTagHash,
|
||||||
|
int? height,
|
||||||
|
String? serializedCoinB64,
|
||||||
|
String? contextB64,
|
||||||
|
}) {
|
||||||
|
return SparkCoin(
|
||||||
|
walletId: walletId,
|
||||||
|
type: type ?? this.type,
|
||||||
|
isUsed: isUsed ?? this.isUsed,
|
||||||
|
groupId: groupId ?? this.groupId,
|
||||||
|
nonce: nonce ?? this.nonce,
|
||||||
|
address: address ?? this.address,
|
||||||
|
txHash: txHash ?? this.txHash,
|
||||||
|
valueIntString: value?.toString() ?? this.value.toString(),
|
||||||
|
memo: memo ?? this.memo,
|
||||||
|
serialContext: serialContext ?? this.serialContext,
|
||||||
|
diversifierIntString:
|
||||||
|
diversifier?.toString() ?? this.diversifier.toString(),
|
||||||
|
encryptedDiversifier: encryptedDiversifier ?? this.encryptedDiversifier,
|
||||||
|
serial: serial ?? this.serial,
|
||||||
|
tag: tag ?? this.tag,
|
||||||
|
lTagHash: lTagHash ?? this.lTagHash,
|
||||||
|
height: height ?? this.height,
|
||||||
|
serializedCoinB64: serializedCoinB64 ?? this.serializedCoinB64,
|
||||||
|
contextB64: contextB64 ?? this.contextB64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SparkCoin('
|
||||||
|
'walletId: $walletId'
|
||||||
|
', type: $type'
|
||||||
|
', isUsed: $isUsed'
|
||||||
|
', groupId: $groupId'
|
||||||
|
', k: $nonce'
|
||||||
|
', address: $address'
|
||||||
|
', txHash: $txHash'
|
||||||
|
', value: $value'
|
||||||
|
', memo: $memo'
|
||||||
|
', serialContext: $serialContext'
|
||||||
|
', diversifier: $diversifier'
|
||||||
|
', encryptedDiversifier: $encryptedDiversifier'
|
||||||
|
', serial: $serial'
|
||||||
|
', tag: $tag'
|
||||||
|
', lTagHash: $lTagHash'
|
||||||
|
', height: $height'
|
||||||
|
', serializedCoinB64: $serializedCoinB64'
|
||||||
|
', contextB64: $contextB64'
|
||||||
|
')';
|
||||||
|
}
|
||||||
|
}
|
3456
lib/wallets/isar/models/spark_coin.g.dart
Normal file
3456
lib/wallets/isar/models/spark_coin.g.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -55,6 +55,15 @@ class TxData {
|
||||||
// tezos specific
|
// tezos specific
|
||||||
final tezart.OperationsList? tezosOperationsList;
|
final tezart.OperationsList? tezosOperationsList;
|
||||||
|
|
||||||
|
// firo spark specific
|
||||||
|
final List<
|
||||||
|
({
|
||||||
|
String address,
|
||||||
|
Amount amount,
|
||||||
|
String memo,
|
||||||
|
})>? sparkRecipients;
|
||||||
|
final List<TxData>? sparkMints;
|
||||||
|
|
||||||
TxData({
|
TxData({
|
||||||
this.feeRateType,
|
this.feeRateType,
|
||||||
this.feeRateAmount,
|
this.feeRateAmount,
|
||||||
|
@ -85,6 +94,8 @@ class TxData {
|
||||||
this.txSubType,
|
this.txSubType,
|
||||||
this.mintsMapLelantus,
|
this.mintsMapLelantus,
|
||||||
this.tezosOperationsList,
|
this.tezosOperationsList,
|
||||||
|
this.sparkRecipients,
|
||||||
|
this.sparkMints,
|
||||||
});
|
});
|
||||||
|
|
||||||
Amount? get amount => recipients != null && recipients!.isNotEmpty
|
Amount? get amount => recipients != null && recipients!.isNotEmpty
|
||||||
|
@ -93,6 +104,13 @@ class TxData {
|
||||||
.reduce((total, amount) => total += amount)
|
.reduce((total, amount) => total += amount)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
Amount? get amountSpark =>
|
||||||
|
sparkRecipients != null && sparkRecipients!.isNotEmpty
|
||||||
|
? sparkRecipients!
|
||||||
|
.map((e) => e.amount)
|
||||||
|
.reduce((total, amount) => total += amount)
|
||||||
|
: null;
|
||||||
|
|
||||||
int? get estimatedSatsPerVByte => fee != null && vSize != null
|
int? get estimatedSatsPerVByte => fee != null && vSize != null
|
||||||
? (fee!.raw ~/ BigInt.from(vSize!)).toInt()
|
? (fee!.raw ~/ BigInt.from(vSize!)).toInt()
|
||||||
: null;
|
: null;
|
||||||
|
@ -127,6 +145,14 @@ class TxData {
|
||||||
TransactionSubType? txSubType,
|
TransactionSubType? txSubType,
|
||||||
List<Map<String, dynamic>>? mintsMapLelantus,
|
List<Map<String, dynamic>>? mintsMapLelantus,
|
||||||
tezart.OperationsList? tezosOperationsList,
|
tezart.OperationsList? tezosOperationsList,
|
||||||
|
List<
|
||||||
|
({
|
||||||
|
String address,
|
||||||
|
Amount amount,
|
||||||
|
String memo,
|
||||||
|
})>?
|
||||||
|
sparkRecipients,
|
||||||
|
List<TxData>? sparkMints,
|
||||||
}) {
|
}) {
|
||||||
return TxData(
|
return TxData(
|
||||||
feeRateType: feeRateType ?? this.feeRateType,
|
feeRateType: feeRateType ?? this.feeRateType,
|
||||||
|
@ -159,6 +185,8 @@ class TxData {
|
||||||
txSubType: txSubType ?? this.txSubType,
|
txSubType: txSubType ?? this.txSubType,
|
||||||
mintsMapLelantus: mintsMapLelantus ?? this.mintsMapLelantus,
|
mintsMapLelantus: mintsMapLelantus ?? this.mintsMapLelantus,
|
||||||
tezosOperationsList: tezosOperationsList ?? this.tezosOperationsList,
|
tezosOperationsList: tezosOperationsList ?? this.tezosOperationsList,
|
||||||
|
sparkRecipients: sparkRecipients ?? this.sparkRecipients,
|
||||||
|
sparkMints: sparkMints ?? this.sparkMints,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,5 +221,7 @@ class TxData {
|
||||||
'txSubType: $txSubType, '
|
'txSubType: $txSubType, '
|
||||||
'mintsMapLelantus: $mintsMapLelantus, '
|
'mintsMapLelantus: $mintsMapLelantus, '
|
||||||
'tezosOperationsList: $tezosOperationsList, '
|
'tezosOperationsList: $tezosOperationsList, '
|
||||||
|
'sparkRecipients: $sparkRecipients, '
|
||||||
|
'sparkMints: $sparkMints, '
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ class BitcoinWallet extends Bip39HDWallet
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Address>> fetchAllOwnAddresses() async {
|
Future<List<Address>> fetchAddressesForElectrumXScan() async {
|
||||||
final allAddresses = await mainDB
|
final allAddresses = await mainDB
|
||||||
.getAddresses(walletId)
|
.getAddresses(walletId)
|
||||||
.filter()
|
.filter()
|
||||||
|
@ -51,7 +51,7 @@ class BitcoinWallet extends Bip39HDWallet
|
||||||
|
|
||||||
// TODO: [prio=med] switch to V2 transactions
|
// TODO: [prio=med] switch to V2 transactions
|
||||||
final data = await fetchTransactionsV1(
|
final data = await fetchTransactionsV1(
|
||||||
addresses: await fetchAllOwnAddresses(),
|
addresses: await fetchAddressesForElectrumXScan(),
|
||||||
currentChainHeight: currentChainHeight,
|
currentChainHeight: currentChainHeight,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ class BitcoincashWallet extends Bip39HDWallet
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Address>> fetchAllOwnAddresses() async {
|
Future<List<Address>> fetchAddressesForElectrumXScan() async {
|
||||||
final allAddresses = await mainDB
|
final allAddresses = await mainDB
|
||||||
.getAddresses(walletId)
|
.getAddresses(walletId)
|
||||||
.filter()
|
.filter()
|
||||||
|
@ -94,7 +94,7 @@ class BitcoincashWallet extends Bip39HDWallet
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateTransactions() async {
|
Future<void> updateTransactions() async {
|
||||||
List<Address> allAddressesOld = await fetchAllOwnAddresses();
|
List<Address> allAddressesOld = await fetchAddressesForElectrumXScan();
|
||||||
|
|
||||||
Set<String> receivingAddresses = allAddressesOld
|
Set<String> receivingAddresses = allAddressesOld
|
||||||
.where((e) => e.subType == AddressSubType.receiving)
|
.where((e) => e.subType == AddressSubType.receiving)
|
||||||
|
@ -293,6 +293,7 @@ class BitcoincashWallet extends Bip39HDWallet
|
||||||
outputs: List.unmodifiable(outputs),
|
outputs: List.unmodifiable(outputs),
|
||||||
type: type,
|
type: type,
|
||||||
subType: subType,
|
subType: subType,
|
||||||
|
otherData: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
txns.add(tx);
|
txns.add(tx);
|
||||||
|
|
|
@ -24,7 +24,7 @@ class DogecoinWallet extends Bip39HDWallet
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Address>> fetchAllOwnAddresses() async {
|
Future<List<Address>> fetchAddressesForElectrumXScan() async {
|
||||||
final allAddresses = await mainDB
|
final allAddresses = await mainDB
|
||||||
.getAddresses(walletId)
|
.getAddresses(walletId)
|
||||||
.filter()
|
.filter()
|
||||||
|
@ -47,7 +47,7 @@ class DogecoinWallet extends Bip39HDWallet
|
||||||
|
|
||||||
// TODO: [prio=med] switch to V2 transactions
|
// TODO: [prio=med] switch to V2 transactions
|
||||||
final data = await fetchTransactionsV1(
|
final data = await fetchTransactionsV1(
|
||||||
addresses: await fetchAllOwnAddresses(),
|
addresses: await fetchAddressesForElectrumXScan(),
|
||||||
currentChainHeight: currentChainHeight,
|
currentChainHeight: currentChainHeight,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ class EcashWallet extends Bip39HDWallet
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Address>> fetchAllOwnAddresses() async {
|
Future<List<Address>> fetchAddressesForElectrumXScan() async {
|
||||||
final allAddresses = await mainDB
|
final allAddresses = await mainDB
|
||||||
.getAddresses(walletId)
|
.getAddresses(walletId)
|
||||||
.filter()
|
.filter()
|
||||||
|
@ -87,7 +87,7 @@ class EcashWallet extends Bip39HDWallet
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateTransactions() async {
|
Future<void> updateTransactions() async {
|
||||||
List<Address> allAddressesOld = await fetchAllOwnAddresses();
|
List<Address> allAddressesOld = await fetchAddressesForElectrumXScan();
|
||||||
|
|
||||||
Set<String> receivingAddresses = allAddressesOld
|
Set<String> receivingAddresses = allAddressesOld
|
||||||
.where((e) => e.subType == AddressSubType.receiving)
|
.where((e) => e.subType == AddressSubType.receiving)
|
||||||
|
@ -169,7 +169,7 @@ class EcashWallet extends Bip39HDWallet
|
||||||
final prevOut = OutputV2.fromElectrumXJson(
|
final prevOut = OutputV2.fromElectrumXJson(
|
||||||
prevOutJson,
|
prevOutJson,
|
||||||
decimalPlaces: cryptoCurrency.fractionDigits,
|
decimalPlaces: cryptoCurrency.fractionDigits,
|
||||||
isECashFullAmountNotSats: true,
|
isFullAmountNotSats: true,
|
||||||
walletOwns: false, // doesn't matter here as this is not saved
|
walletOwns: false, // doesn't matter here as this is not saved
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ class EcashWallet extends Bip39HDWallet
|
||||||
OutputV2 output = OutputV2.fromElectrumXJson(
|
OutputV2 output = OutputV2.fromElectrumXJson(
|
||||||
Map<String, dynamic>.from(outputJson as Map),
|
Map<String, dynamic>.from(outputJson as Map),
|
||||||
decimalPlaces: cryptoCurrency.fractionDigits,
|
decimalPlaces: cryptoCurrency.fractionDigits,
|
||||||
isECashFullAmountNotSats: true,
|
isFullAmountNotSats: true,
|
||||||
// don't know yet if wallet owns. Need addresses first
|
// don't know yet if wallet owns. Need addresses first
|
||||||
walletOwns: false,
|
walletOwns: false,
|
||||||
);
|
);
|
||||||
|
@ -288,6 +288,7 @@ class EcashWallet extends Bip39HDWallet
|
||||||
outputs: List.unmodifiable(outputs),
|
outputs: List.unmodifiable(outputs),
|
||||||
type: type,
|
type: type,
|
||||||
subType: subType,
|
subType: subType,
|
||||||
|
otherData: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
txns.add(tx);
|
txns.add(tx);
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
|
import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:stackwallet/db/hive/db.dart';
|
import 'package:stackwallet/db/hive/db.dart';
|
||||||
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart';
|
||||||
import 'package:stackwallet/models/isar/models/blockchain_data/input.dart';
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart';
|
||||||
import 'package:stackwallet/models/isar/models/blockchain_data/output.dart';
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||||
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
|
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||||
import 'package:stackwallet/models/isar/models/firo_specific/lelantus_coin.dart';
|
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
import 'package:stackwallet/utilities/extensions/extensions.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart';
|
import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||||
|
import 'package:stackwallet/wallets/isar/models/spark_coin.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
const sparkStartBlock = 819300; // (approx 18 Jan 2024)
|
const sparkStartBlock = 819300; // (approx 18 Jan 2024)
|
||||||
|
|
||||||
|
@ -27,6 +30,9 @@ class FiroWallet extends Bip39HDWallet
|
||||||
|
|
||||||
FiroWallet(CryptoCurrencyNetwork network) : super(Firo(network));
|
FiroWallet(CryptoCurrencyNetwork network) : super(Firo(network));
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get isarTransactionVersion => 2;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FilterOperation? get changeAddressFilterOperation =>
|
FilterOperation? get changeAddressFilterOperation =>
|
||||||
FilterGroup.and(standardChangeAddressFilters);
|
FilterGroup.and(standardChangeAddressFilters);
|
||||||
|
@ -37,49 +43,43 @@ class FiroWallet extends Bip39HDWallet
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<Address>> fetchAllOwnAddresses() async {
|
|
||||||
final allAddresses = await mainDB
|
|
||||||
.getAddresses(walletId)
|
|
||||||
.filter()
|
|
||||||
.not()
|
|
||||||
.group(
|
|
||||||
(q) => q
|
|
||||||
.typeEqualTo(AddressType.nonWallet)
|
|
||||||
.or()
|
|
||||||
.subTypeEqualTo(AddressSubType.nonWallet),
|
|
||||||
)
|
|
||||||
.findAll();
|
|
||||||
return allAddresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
bool _duplicateTxCheck(
|
|
||||||
List<Map<String, dynamic>> allTransactions, String txid) {
|
|
||||||
for (int i = 0; i < allTransactions.length; i++) {
|
|
||||||
if (allTransactions[i]["txid"] == txid) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateTransactions() async {
|
Future<void> updateTransactions() async {
|
||||||
final allAddresses = await fetchAllOwnAddresses();
|
List<Address> allAddressesOld = await fetchAddressesForElectrumXScan();
|
||||||
|
|
||||||
Set<String> receivingAddresses = allAddresses
|
Set<String> receivingAddresses = allAddressesOld
|
||||||
.where((e) => e.subType == AddressSubType.receiving)
|
.where((e) => e.subType == AddressSubType.receiving)
|
||||||
.map((e) => e.value)
|
.map((e) => convertAddressString(e.value))
|
||||||
.toSet();
|
.toSet();
|
||||||
Set<String> changeAddresses = allAddresses
|
|
||||||
|
Set<String> changeAddresses = allAddressesOld
|
||||||
.where((e) => e.subType == AddressSubType.change)
|
.where((e) => e.subType == AddressSubType.change)
|
||||||
.map((e) => e.value)
|
.map((e) => convertAddressString(e.value))
|
||||||
.toSet();
|
.toSet();
|
||||||
|
|
||||||
|
final allAddressesSet = {...receivingAddresses, ...changeAddresses};
|
||||||
|
|
||||||
final List<Map<String, dynamic>> allTxHashes =
|
final List<Map<String, dynamic>> allTxHashes =
|
||||||
await fetchHistory(allAddresses.map((e) => e.value).toList());
|
await fetchHistory(allAddressesSet);
|
||||||
|
|
||||||
|
final sparkCoins = await mainDB.isar.sparkCoins
|
||||||
|
.where()
|
||||||
|
.walletIdEqualToAnyLTagHash(walletId)
|
||||||
|
.findAll();
|
||||||
|
|
||||||
|
final Set<String> sparkTxids = {};
|
||||||
|
|
||||||
|
for (final coin in sparkCoins) {
|
||||||
|
sparkTxids.add(coin.txHash);
|
||||||
|
// check for duplicates before adding to list
|
||||||
|
if (allTxHashes.indexWhere((e) => e["tx_hash"] == coin.txHash) == -1) {
|
||||||
|
final info = {
|
||||||
|
"tx_hash": coin.txHash,
|
||||||
|
"height": coin.height,
|
||||||
|
};
|
||||||
|
allTxHashes.add(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<Map<String, dynamic>> allTransactions = [];
|
List<Map<String, dynamic>> allTransactions = [];
|
||||||
|
|
||||||
|
@ -110,8 +110,6 @@ class FiroWallet extends Bip39HDWallet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// final currentHeight = await chainHeight;
|
|
||||||
|
|
||||||
for (final txHash in allTxHashes) {
|
for (final txHash in allTxHashes) {
|
||||||
// final storedTx = await db
|
// final storedTx = await db
|
||||||
// .getTransactions(walletId)
|
// .getTransactions(walletId)
|
||||||
|
@ -127,508 +125,372 @@ class FiroWallet extends Bip39HDWallet
|
||||||
coin: info.coin,
|
coin: info.coin,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
|
// check for duplicates before adding to list
|
||||||
tx["address"] = await mainDB
|
if (allTransactions
|
||||||
.getAddresses(walletId)
|
.indexWhere((e) => e["txid"] == tx["txid"] as String) ==
|
||||||
.filter()
|
-1) {
|
||||||
.valueEqualTo(txHash["address"] as String)
|
tx["height"] ??= txHash["height"];
|
||||||
.findFirst();
|
|
||||||
tx["height"] = txHash["height"];
|
|
||||||
allTransactions.add(tx);
|
allTransactions.add(tx);
|
||||||
}
|
}
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Tuple2<Transaction, Address?>> txnsData = [];
|
final List<TransactionV2> txns = [];
|
||||||
|
|
||||||
for (final txObject in allTransactions) {
|
for (final txData in allTransactions) {
|
||||||
final inputList = txObject["vin"] as List;
|
// set to true if any inputs were detected as owned by this wallet
|
||||||
final outputList = txObject["vout"] as List;
|
bool wasSentFromThisWallet = false;
|
||||||
|
|
||||||
|
// set to true if any outputs were detected as owned by this wallet
|
||||||
|
bool wasReceivedInThisWallet = false;
|
||||||
|
BigInt amountReceivedInThisWallet = BigInt.zero;
|
||||||
|
BigInt changeAmountReceivedInThisWallet = BigInt.zero;
|
||||||
|
|
||||||
|
Amount? anonFees;
|
||||||
|
|
||||||
bool isMint = false;
|
bool isMint = false;
|
||||||
bool isJMint = false;
|
bool isJMint = false;
|
||||||
|
bool isSparkMint = false;
|
||||||
|
bool isMasterNodePayment = false;
|
||||||
|
final bool isSparkSpend = txData["type"] == 9 && txData["version"] == 3;
|
||||||
|
final bool isMySpark = sparkTxids.contains(txData["txid"] as String);
|
||||||
|
|
||||||
// check if tx is Mint or jMint
|
final sparkCoinsInvolved =
|
||||||
for (final output in outputList) {
|
sparkCoins.where((e) => e.txHash == txData["txid"]);
|
||||||
if (output["scriptPubKey"]?["type"] == "lelantusmint") {
|
if (isMySpark && sparkCoinsInvolved.isEmpty) {
|
||||||
final asm = output["scriptPubKey"]?["asm"] as String?;
|
Logging.instance.log(
|
||||||
|
"sparkCoinsInvolved is empty and should not be! (ignoring tx parsing)",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse outputs
|
||||||
|
final List<OutputV2> outputs = [];
|
||||||
|
for (final outputJson in txData["vout"] as List) {
|
||||||
|
final outMap = Map<String, dynamic>.from(outputJson as Map);
|
||||||
|
if (outMap["scriptPubKey"]?["type"] == "lelantusmint") {
|
||||||
|
final asm = outMap["scriptPubKey"]?["asm"] as String?;
|
||||||
if (asm != null) {
|
if (asm != null) {
|
||||||
if (asm.startsWith("OP_LELANTUSJMINT")) {
|
if (asm.startsWith("OP_LELANTUSJMINT")) {
|
||||||
isJMint = true;
|
isJMint = true;
|
||||||
break;
|
|
||||||
} else if (asm.startsWith("OP_LELANTUSMINT")) {
|
} else if (asm.startsWith("OP_LELANTUSMINT")) {
|
||||||
isMint = true;
|
isMint = true;
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Unknown mint op code found for lelantusmint tx: ${txObject["txid"]}",
|
"Unknown mint op code found for lelantusmint tx: ${txData["txid"]}",
|
||||||
level: LogLevel.Error,
|
level: LogLevel.Error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"ASM for lelantusmint tx: ${txObject["txid"]} is null!",
|
"ASM for lelantusmint tx: ${txData["txid"]} is null!",
|
||||||
level: LogLevel.Error,
|
level: LogLevel.Error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (outMap["scriptPubKey"]?["type"] == "sparkmint" ||
|
||||||
|
outMap["scriptPubKey"]?["type"] == "sparksmint") {
|
||||||
|
final asm = outMap["scriptPubKey"]?["asm"] as String?;
|
||||||
|
if (asm != null) {
|
||||||
|
if (asm.startsWith("OP_SPARKMINT") ||
|
||||||
|
asm.startsWith("OP_SPARKSMINT")) {
|
||||||
|
isSparkMint = true;
|
||||||
|
} else {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Unknown mint op code found for sparkmint tx: ${txData["txid"]}",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logging.instance.log(
|
||||||
|
"ASM for sparkmint tx: ${txData["txid"]} is null!",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> inputAddresses = {};
|
OutputV2 output = OutputV2.fromElectrumXJson(
|
||||||
Set<String> outputAddresses = {};
|
outMap,
|
||||||
|
decimalPlaces: cryptoCurrency.fractionDigits,
|
||||||
Amount totalInputValue = Amount(
|
isFullAmountNotSats: true,
|
||||||
rawValue: BigInt.zero,
|
// don't know yet if wallet owns. Need addresses first
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
walletOwns: false,
|
||||||
);
|
|
||||||
Amount totalOutputValue = Amount(
|
|
||||||
rawValue: BigInt.zero,
|
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Amount amountSentFromWallet = Amount(
|
// if (isSparkSpend) {
|
||||||
rawValue: BigInt.zero,
|
// // TODO?
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
// } else
|
||||||
);
|
if (isSparkMint) {
|
||||||
Amount amountReceivedInWallet = Amount(
|
if (isMySpark) {
|
||||||
rawValue: BigInt.zero,
|
if (output.addresses.isEmpty &&
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
output.scriptPubKeyHex.length >= 488) {
|
||||||
);
|
// likely spark related
|
||||||
Amount changeAmount = Amount(
|
final opByte = output.scriptPubKeyHex
|
||||||
rawValue: BigInt.zero,
|
.substring(0, 2)
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
.toUint8ListFromHex
|
||||||
);
|
.first;
|
||||||
|
if (opByte == OP_SPARKMINT || opByte == OP_SPARKSMINT) {
|
||||||
|
final serCoin = base64Encode(output.scriptPubKeyHex
|
||||||
|
.substring(2, 488)
|
||||||
|
.toUint8ListFromHex);
|
||||||
|
final coin = sparkCoinsInvolved
|
||||||
|
.where((e) => e.serializedCoinB64!.startsWith(serCoin))
|
||||||
|
.firstOrNull;
|
||||||
|
|
||||||
// Parse mint transaction ================================================
|
if (coin == null) {
|
||||||
// We should be able to assume this belongs to this wallet
|
// not ours
|
||||||
if (isMint) {
|
} else {
|
||||||
List<Input> ins = [];
|
output = output.copyWith(
|
||||||
|
walletOwns: true,
|
||||||
|
valueStringSats: coin.value.toString(),
|
||||||
|
addresses: [
|
||||||
|
coin.address,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (isMint || isJMint) {
|
||||||
|
// do nothing extra ?
|
||||||
|
} else {
|
||||||
|
// TODO?
|
||||||
|
}
|
||||||
|
|
||||||
// Parse inputs
|
// if output was to my wallet, add value to amount received
|
||||||
for (final input in inputList) {
|
if (receivingAddresses
|
||||||
// Both value and address should not be null for a mint
|
.intersection(output.addresses.toSet())
|
||||||
final address = input["address"] as String?;
|
.isNotEmpty) {
|
||||||
final value = input["valueSat"] as int?;
|
wasReceivedInThisWallet = true;
|
||||||
|
amountReceivedInThisWallet += output.value;
|
||||||
|
output = output.copyWith(walletOwns: true);
|
||||||
|
} else if (changeAddresses
|
||||||
|
.intersection(output.addresses.toSet())
|
||||||
|
.isNotEmpty) {
|
||||||
|
wasReceivedInThisWallet = true;
|
||||||
|
changeAmountReceivedInThisWallet += output.value;
|
||||||
|
output = output.copyWith(walletOwns: true);
|
||||||
|
} else if (isSparkMint && isMySpark) {
|
||||||
|
wasReceivedInThisWallet = true;
|
||||||
|
if (output.addresses.contains(sparkChangeAddress)) {
|
||||||
|
changeAmountReceivedInThisWallet += output.value;
|
||||||
|
} else {
|
||||||
|
amountReceivedInThisWallet += output.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We should not need to check whether the mint belongs to this
|
outputs.add(output);
|
||||||
// wallet as any tx we look up will be looked up by one of this
|
}
|
||||||
// wallet's addresses
|
|
||||||
if (address != null && value != null) {
|
if (isJMint || isSparkSpend) {
|
||||||
totalInputValue += value.toAmountAsRaw(
|
anonFees = Amount(
|
||||||
|
rawValue: BigInt.zero,
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ins.add(
|
// parse inputs
|
||||||
Input(
|
final List<InputV2> inputs = [];
|
||||||
txid: input['txid'] as String? ?? "",
|
for (final jsonInput in txData["vin"] as List) {
|
||||||
vout: input['vout'] as int? ?? -1,
|
final map = Map<String, dynamic>.from(jsonInput as Map);
|
||||||
scriptSig: input['scriptSig']?['hex'] as String?,
|
|
||||||
scriptSigAsm: input['scriptSig']?['asm'] as String?,
|
final List<String> addresses = [];
|
||||||
isCoinbase: input['is_coinbase'] as bool?,
|
String valueStringSats = "0";
|
||||||
sequence: input['sequence'] as int?,
|
OutpointV2? outpoint;
|
||||||
innerRedeemScriptAsm: input['innerRedeemscriptAsm'] as String?,
|
|
||||||
),
|
final coinbase = map["coinbase"] as String?;
|
||||||
|
|
||||||
|
final txid = map["txid"] as String?;
|
||||||
|
final vout = map["vout"] as int?;
|
||||||
|
if (txid != null && vout != null) {
|
||||||
|
outpoint = OutpointV2.isarCantDoRequiredInDefaultConstructor(
|
||||||
|
txid: txid,
|
||||||
|
vout: vout,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse outputs
|
if (isSparkSpend) {
|
||||||
for (final output in outputList) {
|
// anon fees
|
||||||
// get value
|
final nFee = Decimal.tryParse(map["nFees"].toString());
|
||||||
final value = Amount.fromDecimal(
|
|
||||||
Decimal.parse(output["value"].toString()),
|
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
|
||||||
);
|
|
||||||
|
|
||||||
// add value to total
|
|
||||||
totalOutputValue += value;
|
|
||||||
}
|
|
||||||
|
|
||||||
final fee = totalInputValue - totalOutputValue;
|
|
||||||
final tx = Transaction(
|
|
||||||
walletId: walletId,
|
|
||||||
txid: txObject["txid"] as String,
|
|
||||||
timestamp: txObject["blocktime"] as int? ??
|
|
||||||
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
|
||||||
type: TransactionType.sentToSelf,
|
|
||||||
subType: TransactionSubType.mint,
|
|
||||||
amount: totalOutputValue.raw.toInt(),
|
|
||||||
amountString: totalOutputValue.toJsonString(),
|
|
||||||
fee: fee.raw.toInt(),
|
|
||||||
height: txObject["height"] as int?,
|
|
||||||
isCancelled: false,
|
|
||||||
isLelantus: true,
|
|
||||||
slateId: null,
|
|
||||||
otherData: null,
|
|
||||||
nonce: null,
|
|
||||||
inputs: ins,
|
|
||||||
outputs: [],
|
|
||||||
numberOfMessages: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
txnsData.add(Tuple2(tx, null));
|
|
||||||
|
|
||||||
// Otherwise parse JMint transaction ===================================
|
|
||||||
} else if (isJMint) {
|
|
||||||
Amount jMintFees = Amount(
|
|
||||||
rawValue: BigInt.zero,
|
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Parse inputs
|
|
||||||
List<Input> ins = [];
|
|
||||||
for (final input in inputList) {
|
|
||||||
// JMint fee
|
|
||||||
final nFee = Decimal.tryParse(input["nFees"].toString());
|
|
||||||
if (nFee != null) {
|
if (nFee != null) {
|
||||||
final fees = Amount.fromDecimal(
|
final fees = Amount.fromDecimal(
|
||||||
nFee,
|
nFee,
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
);
|
);
|
||||||
|
|
||||||
jMintFees += fees;
|
anonFees = anonFees! + fees;
|
||||||
}
|
}
|
||||||
|
} else if (isSparkMint) {
|
||||||
|
final address = map["address"] as String?;
|
||||||
|
final value = map["valueSat"] as int?;
|
||||||
|
|
||||||
ins.add(
|
if (address != null && value != null) {
|
||||||
Input(
|
valueStringSats = value.toString();
|
||||||
txid: input['txid'] as String? ?? "",
|
addresses.add(address);
|
||||||
vout: input['vout'] as int? ?? -1,
|
|
||||||
scriptSig: input['scriptSig']?['hex'] as String?,
|
|
||||||
scriptSigAsm: input['scriptSig']?['asm'] as String?,
|
|
||||||
isCoinbase: input['is_coinbase'] as bool?,
|
|
||||||
sequence: input['sequence'] as int?,
|
|
||||||
innerRedeemScriptAsm: input['innerRedeemscriptAsm'] as String?,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
} else if (isMint) {
|
||||||
|
// We should be able to assume this belongs to this wallet
|
||||||
|
final address = map["address"] as String?;
|
||||||
|
final value = map["valueSat"] as int?;
|
||||||
|
|
||||||
bool nonWalletAddressFoundInOutputs = false;
|
if (address != null && value != null) {
|
||||||
|
valueStringSats = value.toString();
|
||||||
// Parse outputs
|
addresses.add(address);
|
||||||
List<Output> outs = [];
|
}
|
||||||
for (final output in outputList) {
|
} else if (isJMint) {
|
||||||
// get value
|
// anon fees
|
||||||
final value = Amount.fromDecimal(
|
final nFee = Decimal.tryParse(map["nFees"].toString());
|
||||||
Decimal.parse(output["value"].toString()),
|
if (nFee != null) {
|
||||||
|
final fees = Amount.fromDecimal(
|
||||||
|
nFee,
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
);
|
);
|
||||||
|
|
||||||
// add value to total
|
anonFees = anonFees! + fees;
|
||||||
totalOutputValue += value;
|
|
||||||
|
|
||||||
final address = output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
|
||||||
output['scriptPubKey']?['address'] as String?;
|
|
||||||
|
|
||||||
if (address != null) {
|
|
||||||
outputAddresses.add(address);
|
|
||||||
if (receivingAddresses.contains(address) ||
|
|
||||||
changeAddresses.contains(address)) {
|
|
||||||
amountReceivedInWallet += value;
|
|
||||||
} else {
|
|
||||||
nonWalletAddressFoundInOutputs = true;
|
|
||||||
}
|
}
|
||||||
}
|
} else if (coinbase == null && txid != null && vout != null) {
|
||||||
|
final inputTx = await electrumXCachedClient.getTransaction(
|
||||||
outs.add(
|
txHash: txid,
|
||||||
Output(
|
coin: cryptoCurrency.coin,
|
||||||
scriptPubKey: output['scriptPubKey']?['hex'] as String?,
|
|
||||||
scriptPubKeyAsm: output['scriptPubKey']?['asm'] as String?,
|
|
||||||
scriptPubKeyType: output['scriptPubKey']?['type'] as String?,
|
|
||||||
scriptPubKeyAddress: address ?? "jmint",
|
|
||||||
value: value.raw.toInt(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final txid = txObject["txid"] as String;
|
|
||||||
|
|
||||||
const subType = TransactionSubType.join;
|
|
||||||
|
|
||||||
final type = nonWalletAddressFoundInOutputs
|
|
||||||
? TransactionType.outgoing
|
|
||||||
: (await mainDB.isar.lelantusCoins
|
|
||||||
.where()
|
|
||||||
.walletIdEqualTo(walletId)
|
|
||||||
.filter()
|
|
||||||
.txidEqualTo(txid)
|
|
||||||
.findFirst()) ==
|
|
||||||
null
|
|
||||||
? TransactionType.incoming
|
|
||||||
: TransactionType.sentToSelf;
|
|
||||||
|
|
||||||
final amount = nonWalletAddressFoundInOutputs
|
|
||||||
? totalOutputValue
|
|
||||||
: amountReceivedInWallet;
|
|
||||||
|
|
||||||
final possibleNonWalletAddresses =
|
|
||||||
receivingAddresses.difference(outputAddresses);
|
|
||||||
final possibleReceivingAddresses =
|
|
||||||
receivingAddresses.intersection(outputAddresses);
|
|
||||||
|
|
||||||
final transactionAddress = nonWalletAddressFoundInOutputs
|
|
||||||
? Address(
|
|
||||||
walletId: walletId,
|
|
||||||
value: possibleNonWalletAddresses.first,
|
|
||||||
derivationIndex: -1,
|
|
||||||
derivationPath: null,
|
|
||||||
type: AddressType.nonWallet,
|
|
||||||
subType: AddressSubType.nonWallet,
|
|
||||||
publicKey: [],
|
|
||||||
)
|
|
||||||
: allAddresses.firstWhere(
|
|
||||||
(e) => e.value == possibleReceivingAddresses.first,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final tx = Transaction(
|
final prevOutJson = Map<String, dynamic>.from(
|
||||||
walletId: walletId,
|
(inputTx["vout"] as List).firstWhere((e) => e["n"] == vout)
|
||||||
txid: txid,
|
as Map);
|
||||||
timestamp: txObject["blocktime"] as int? ??
|
|
||||||
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
final prevOut = OutputV2.fromElectrumXJson(
|
||||||
type: type,
|
prevOutJson,
|
||||||
subType: subType,
|
decimalPlaces: cryptoCurrency.fractionDigits,
|
||||||
amount: amount.raw.toInt(),
|
isFullAmountNotSats: true,
|
||||||
amountString: amount.toJsonString(),
|
walletOwns: false, // doesn't matter here as this is not saved
|
||||||
fee: jMintFees.raw.toInt(),
|
|
||||||
height: txObject["height"] as int?,
|
|
||||||
isCancelled: false,
|
|
||||||
isLelantus: true,
|
|
||||||
slateId: null,
|
|
||||||
otherData: null,
|
|
||||||
nonce: null,
|
|
||||||
inputs: ins,
|
|
||||||
outputs: outs,
|
|
||||||
numberOfMessages: null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
txnsData.add(Tuple2(tx, transactionAddress));
|
valueStringSats = prevOut.valueStringSats;
|
||||||
|
addresses.addAll(prevOut.addresses);
|
||||||
|
} else if (coinbase == null) {
|
||||||
|
Util.printJson(map, "NON TXID INPUT");
|
||||||
|
}
|
||||||
|
|
||||||
// Master node payment =====================================
|
InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor(
|
||||||
} else if (inputList.length == 1 &&
|
scriptSigHex: map["scriptSig"]?["hex"] as String?,
|
||||||
inputList.first["coinbase"] is String) {
|
sequence: map["sequence"] as int?,
|
||||||
List<Input> ins = [
|
outpoint: outpoint,
|
||||||
Input(
|
valueStringSats: valueStringSats,
|
||||||
txid: inputList.first["coinbase"] as String,
|
addresses: addresses,
|
||||||
vout: -1,
|
witness: map["witness"] as String?,
|
||||||
scriptSig: null,
|
coinbase: coinbase,
|
||||||
scriptSigAsm: null,
|
innerRedeemScriptAsm: map["innerRedeemscriptAsm"] as String?,
|
||||||
isCoinbase: true,
|
// don't know yet if wallet owns. Need addresses first
|
||||||
sequence: inputList.first['sequence'] as int?,
|
walletOwns: false,
|
||||||
innerRedeemScriptAsm: null,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
// parse outputs
|
|
||||||
List<Output> outs = [];
|
|
||||||
for (final output in outputList) {
|
|
||||||
// get value
|
|
||||||
final value = Amount.fromDecimal(
|
|
||||||
Decimal.parse(output["value"].toString()),
|
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// get output address
|
if (allAddressesSet.intersection(input.addresses.toSet()).isNotEmpty) {
|
||||||
final address = output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
wasSentFromThisWallet = true;
|
||||||
output["scriptPubKey"]?["address"] as String?;
|
input = input.copyWith(walletOwns: true);
|
||||||
if (address != null) {
|
} else if (isMySpark) {
|
||||||
outputAddresses.add(address);
|
final lTags = map["lTags"] as List?;
|
||||||
|
|
||||||
// if output was to my wallet, add value to amount received
|
if (lTags?.isNotEmpty == true) {
|
||||||
if (receivingAddresses.contains(address)) {
|
final List<SparkCoin> usedCoins = [];
|
||||||
amountReceivedInWallet += value;
|
for (final tag in lTags!) {
|
||||||
}
|
final components = (tag as String).split(",");
|
||||||
|
final x = components[0].substring(1);
|
||||||
|
final y = components[1].substring(0, components[1].length - 1);
|
||||||
|
|
||||||
|
final hash = LibSpark.hashTag(x, y);
|
||||||
|
usedCoins.addAll(sparkCoins.where((e) => e.lTagHash == hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
outs.add(
|
if (usedCoins.isNotEmpty) {
|
||||||
Output(
|
input = input.copyWith(
|
||||||
scriptPubKey: output['scriptPubKey']?['hex'] as String?,
|
addresses: usedCoins.map((e) => e.address).toList(),
|
||||||
scriptPubKeyAsm: output['scriptPubKey']?['asm'] as String?,
|
valueStringSats: usedCoins
|
||||||
scriptPubKeyType: output['scriptPubKey']?['type'] as String?,
|
.map((e) => e.value)
|
||||||
scriptPubKeyAddress: address ?? "",
|
.reduce((value, element) => value += element)
|
||||||
value: value.raw.toInt(),
|
.toString(),
|
||||||
),
|
walletOwns: true,
|
||||||
);
|
);
|
||||||
|
wasSentFromThisWallet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is the address initially used to fetch the txid
|
|
||||||
Address transactionAddress = txObject["address"] as Address;
|
|
||||||
|
|
||||||
final tx = Transaction(
|
|
||||||
walletId: walletId,
|
|
||||||
txid: txObject["txid"] as String,
|
|
||||||
timestamp: txObject["blocktime"] as int? ??
|
|
||||||
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
|
||||||
type: TransactionType.incoming,
|
|
||||||
subType: TransactionSubType.none,
|
|
||||||
// amount may overflow. Deprecated. Use amountString
|
|
||||||
amount: amountReceivedInWallet.raw.toInt(),
|
|
||||||
amountString: amountReceivedInWallet.toJsonString(),
|
|
||||||
fee: 0,
|
|
||||||
height: txObject["height"] as int?,
|
|
||||||
isCancelled: false,
|
|
||||||
isLelantus: false,
|
|
||||||
slateId: null,
|
|
||||||
otherData: null,
|
|
||||||
nonce: null,
|
|
||||||
inputs: ins,
|
|
||||||
outputs: outs,
|
|
||||||
numberOfMessages: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
txnsData.add(Tuple2(tx, transactionAddress));
|
|
||||||
|
|
||||||
// Assume non lelantus transaction =====================================
|
|
||||||
} else {
|
|
||||||
// parse inputs
|
|
||||||
List<Input> ins = [];
|
|
||||||
for (final input in inputList) {
|
|
||||||
final valueSat = input["valueSat"] as int?;
|
|
||||||
final address = input["address"] as String? ??
|
|
||||||
input["scriptPubKey"]?["address"] as String? ??
|
|
||||||
input["scriptPubKey"]?["addresses"]?[0] as String?;
|
|
||||||
|
|
||||||
if (address != null && valueSat != null) {
|
|
||||||
final value = valueSat.toAmountAsRaw(
|
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
|
||||||
);
|
|
||||||
|
|
||||||
// add value to total
|
|
||||||
totalInputValue += value;
|
|
||||||
inputAddresses.add(address);
|
|
||||||
|
|
||||||
// if input was from my wallet, add value to amount sent
|
|
||||||
if (receivingAddresses.contains(address) ||
|
|
||||||
changeAddresses.contains(address)) {
|
|
||||||
amountSentFromWallet += value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ins.add(
|
inputs.add(input);
|
||||||
Input(
|
|
||||||
txid: input['txid'] as String,
|
|
||||||
vout: input['vout'] as int? ?? -1,
|
|
||||||
scriptSig: input['scriptSig']?['hex'] as String?,
|
|
||||||
scriptSigAsm: input['scriptSig']?['asm'] as String?,
|
|
||||||
isCoinbase: input['is_coinbase'] as bool?,
|
|
||||||
sequence: input['sequence'] as int?,
|
|
||||||
innerRedeemScriptAsm: input['innerRedeemscriptAsm'] as String?,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse outputs
|
final totalOut = outputs
|
||||||
List<Output> outs = [];
|
.map((e) => e.value)
|
||||||
for (final output in outputList) {
|
.fold(BigInt.zero, (value, element) => value + element);
|
||||||
// get value
|
|
||||||
final value = Amount.fromDecimal(
|
|
||||||
Decimal.parse(output["value"].toString()),
|
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
|
||||||
);
|
|
||||||
|
|
||||||
// add value to total
|
|
||||||
totalOutputValue += value;
|
|
||||||
|
|
||||||
// get output address
|
|
||||||
final address = output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
|
||||||
output["scriptPubKey"]?["address"] as String?;
|
|
||||||
if (address != null) {
|
|
||||||
outputAddresses.add(address);
|
|
||||||
|
|
||||||
// if output was to my wallet, add value to amount received
|
|
||||||
if (receivingAddresses.contains(address)) {
|
|
||||||
amountReceivedInWallet += value;
|
|
||||||
} else if (changeAddresses.contains(address)) {
|
|
||||||
changeAmount += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outs.add(
|
|
||||||
Output(
|
|
||||||
scriptPubKey: output['scriptPubKey']?['hex'] as String?,
|
|
||||||
scriptPubKeyAsm: output['scriptPubKey']?['asm'] as String?,
|
|
||||||
scriptPubKeyType: output['scriptPubKey']?['type'] as String?,
|
|
||||||
scriptPubKeyAddress: address ?? "",
|
|
||||||
value: value.raw.toInt(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final mySentFromAddresses = [
|
|
||||||
...receivingAddresses.intersection(inputAddresses),
|
|
||||||
...changeAddresses.intersection(inputAddresses)
|
|
||||||
];
|
|
||||||
final myReceivedOnAddresses =
|
|
||||||
receivingAddresses.intersection(outputAddresses);
|
|
||||||
final myChangeReceivedOnAddresses =
|
|
||||||
changeAddresses.intersection(outputAddresses);
|
|
||||||
|
|
||||||
final fee = totalInputValue - totalOutputValue;
|
|
||||||
|
|
||||||
// this is the address initially used to fetch the txid
|
|
||||||
Address transactionAddress = txObject["address"] as Address;
|
|
||||||
|
|
||||||
TransactionType type;
|
TransactionType type;
|
||||||
Amount amount;
|
TransactionSubType subType = TransactionSubType.none;
|
||||||
if (mySentFromAddresses.isNotEmpty &&
|
|
||||||
myReceivedOnAddresses.isNotEmpty) {
|
|
||||||
// tx is sent to self
|
|
||||||
type = TransactionType.sentToSelf;
|
|
||||||
|
|
||||||
// should be 0
|
// TODO integrate the following with the next bit
|
||||||
amount = amountSentFromWallet -
|
if (isSparkSpend) {
|
||||||
amountReceivedInWallet -
|
subType = TransactionSubType.sparkSpend;
|
||||||
fee -
|
} else if (isSparkMint) {
|
||||||
changeAmount;
|
subType = TransactionSubType.sparkMint;
|
||||||
} else if (mySentFromAddresses.isNotEmpty) {
|
} else if (isMint) {
|
||||||
// outgoing tx
|
subType = TransactionSubType.mint;
|
||||||
|
} else if (isJMint) {
|
||||||
|
subType = TransactionSubType.join;
|
||||||
|
}
|
||||||
|
|
||||||
|
// at least one input was owned by this wallet
|
||||||
|
if (wasSentFromThisWallet) {
|
||||||
type = TransactionType.outgoing;
|
type = TransactionType.outgoing;
|
||||||
amount = amountSentFromWallet - changeAmount - fee;
|
|
||||||
|
|
||||||
final possible =
|
if (wasReceivedInThisWallet) {
|
||||||
outputAddresses.difference(myChangeReceivedOnAddresses).first;
|
if (changeAmountReceivedInThisWallet + amountReceivedInThisWallet ==
|
||||||
|
totalOut) {
|
||||||
if (transactionAddress.value != possible) {
|
// definitely sent all to self
|
||||||
transactionAddress = Address(
|
type = TransactionType.sentToSelf;
|
||||||
walletId: walletId,
|
} else if (amountReceivedInThisWallet == BigInt.zero) {
|
||||||
value: possible,
|
// most likely just a typical send
|
||||||
derivationIndex: -1,
|
// do nothing here yet
|
||||||
derivationPath: null,
|
|
||||||
subType: AddressSubType.nonWallet,
|
|
||||||
type: AddressType.nonWallet,
|
|
||||||
publicKey: [],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// incoming tx
|
} else if (wasReceivedInThisWallet) {
|
||||||
|
// only found outputs owned by this wallet
|
||||||
type = TransactionType.incoming;
|
type = TransactionType.incoming;
|
||||||
amount = amountReceivedInWallet;
|
} else {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Unexpected tx found (ignoring it): $txData",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final tx = Transaction(
|
String? otherData;
|
||||||
|
if (anonFees != null) {
|
||||||
|
otherData = jsonEncode(
|
||||||
|
{
|
||||||
|
"anonFees": anonFees.toJsonString(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final tx = TransactionV2(
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
txid: txObject["txid"] as String,
|
blockHash: txData["blockhash"] as String?,
|
||||||
timestamp: txObject["blocktime"] as int? ??
|
hash: txData["hash"] as String,
|
||||||
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
txid: txData["txid"] as String,
|
||||||
|
height: txData["height"] as int?,
|
||||||
|
version: txData["version"] as int,
|
||||||
|
timestamp: txData["blocktime"] as int? ??
|
||||||
|
DateTime.timestamp().millisecondsSinceEpoch ~/ 1000,
|
||||||
|
inputs: List.unmodifiable(inputs),
|
||||||
|
outputs: List.unmodifiable(outputs),
|
||||||
type: type,
|
type: type,
|
||||||
subType: TransactionSubType.none,
|
subType: subType,
|
||||||
// amount may overflow. Deprecated. Use amountString
|
otherData: otherData,
|
||||||
amount: amount.raw.toInt(),
|
|
||||||
amountString: amount.toJsonString(),
|
|
||||||
fee: fee.raw.toInt(),
|
|
||||||
height: txObject["height"] as int?,
|
|
||||||
isCancelled: false,
|
|
||||||
isLelantus: false,
|
|
||||||
slateId: null,
|
|
||||||
otherData: null,
|
|
||||||
nonce: null,
|
|
||||||
inputs: ins,
|
|
||||||
outputs: outs,
|
|
||||||
numberOfMessages: null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
txnsData.add(Tuple2(tx, transactionAddress));
|
txns.add(tx);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await mainDB.addNewTransactionData(txnsData, walletId);
|
await mainDB.updateOrPutTransactionV2s(txns);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -689,6 +551,7 @@ class FiroWallet extends Bip39HDWallet
|
||||||
await mainDB.deleteWalletBlockchainData(walletId);
|
await mainDB.deleteWalletBlockchainData(walletId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lelantus
|
||||||
final latestSetId = await electrumXClient.getLelantusLatestCoinId();
|
final latestSetId = await electrumXClient.getLelantusLatestCoinId();
|
||||||
final setDataMapFuture = getSetDataMap(latestSetId);
|
final setDataMapFuture = getSetDataMap(latestSetId);
|
||||||
final usedSerialNumbersFuture =
|
final usedSerialNumbersFuture =
|
||||||
|
@ -696,6 +559,17 @@ class FiroWallet extends Bip39HDWallet
|
||||||
coin: info.coin,
|
coin: info.coin,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// spark
|
||||||
|
final latestSparkCoinId = await electrumXClient.getSparkLatestCoinId();
|
||||||
|
final sparkAnonSetFuture = electrumXCachedClient.getSparkAnonymitySet(
|
||||||
|
groupId: latestSparkCoinId.toString(),
|
||||||
|
coin: info.coin,
|
||||||
|
);
|
||||||
|
final sparkUsedCoinTagsFuture =
|
||||||
|
electrumXCachedClient.getSparkUsedCoinsTags(
|
||||||
|
coin: info.coin,
|
||||||
|
);
|
||||||
|
|
||||||
// receiving addresses
|
// receiving addresses
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"checking receiving addresses...",
|
"checking receiving addresses...",
|
||||||
|
@ -799,16 +673,29 @@ class FiroWallet extends Bip39HDWallet
|
||||||
final futureResults = await Future.wait([
|
final futureResults = await Future.wait([
|
||||||
usedSerialNumbersFuture,
|
usedSerialNumbersFuture,
|
||||||
setDataMapFuture,
|
setDataMapFuture,
|
||||||
|
sparkAnonSetFuture,
|
||||||
|
sparkUsedCoinTagsFuture,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// lelantus
|
||||||
final usedSerialsSet = (futureResults[0] as List<String>).toSet();
|
final usedSerialsSet = (futureResults[0] as List<String>).toSet();
|
||||||
final setDataMap = futureResults[1] as Map<dynamic, dynamic>;
|
final setDataMap = futureResults[1] as Map<dynamic, dynamic>;
|
||||||
|
|
||||||
await recoverLelantusWallet(
|
// spark
|
||||||
|
final sparkAnonymitySet = futureResults[2] as Map<String, dynamic>;
|
||||||
|
final sparkSpentCoinTags = futureResults[3] as Set<String>;
|
||||||
|
|
||||||
|
await Future.wait([
|
||||||
|
recoverLelantusWallet(
|
||||||
latestSetId: latestSetId,
|
latestSetId: latestSetId,
|
||||||
usedSerialNumbers: usedSerialsSet,
|
usedSerialNumbers: usedSerialsSet,
|
||||||
setDataMap: setDataMap,
|
setDataMap: setDataMap,
|
||||||
);
|
),
|
||||||
|
recoverSparkWallet(
|
||||||
|
anonymitySet: sparkAnonymitySet,
|
||||||
|
spentCoinTags: sparkSpentCoinTags,
|
||||||
|
),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
await refresh();
|
await refresh();
|
||||||
|
|
|
@ -37,6 +37,7 @@ import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_int
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
||||||
|
|
||||||
abstract class Wallet<T extends CryptoCurrency> {
|
abstract class Wallet<T extends CryptoCurrency> {
|
||||||
// default to Transaction class. For TransactionV2 set to 2
|
// default to Transaction class. For TransactionV2 set to 2
|
||||||
|
@ -270,7 +271,7 @@ abstract class Wallet<T extends CryptoCurrency> {
|
||||||
case Coin.firo:
|
case Coin.firo:
|
||||||
return FiroWallet(CryptoCurrencyNetwork.main);
|
return FiroWallet(CryptoCurrencyNetwork.main);
|
||||||
case Coin.firoTestNet:
|
case Coin.firoTestNet:
|
||||||
return FiroWallet(CryptoCurrencyNetwork.main);
|
return FiroWallet(CryptoCurrencyNetwork.test);
|
||||||
|
|
||||||
case Coin.nano:
|
case Coin.nano:
|
||||||
return NanoWallet(CryptoCurrencyNetwork.main);
|
return NanoWallet(CryptoCurrencyNetwork.main);
|
||||||
|
@ -289,7 +290,10 @@ abstract class Wallet<T extends CryptoCurrency> {
|
||||||
|
|
||||||
// listen to changes in db and updated wallet info property as required
|
// listen to changes in db and updated wallet info property as required
|
||||||
void _watchWalletInfo() {
|
void _watchWalletInfo() {
|
||||||
_walletInfoStream = mainDB.isar.walletInfo.watchObject(_walletInfo.id);
|
_walletInfoStream = mainDB.isar.walletInfo.watchObject(
|
||||||
|
_walletInfo.id,
|
||||||
|
fireImmediately: true,
|
||||||
|
);
|
||||||
_walletInfoStream.forEach((element) {
|
_walletInfoStream.forEach((element) {
|
||||||
if (element != null) {
|
if (element != null) {
|
||||||
_walletInfo = element;
|
_walletInfo = element;
|
||||||
|
@ -417,6 +421,10 @@ abstract class Wallet<T extends CryptoCurrency> {
|
||||||
.checkChangeAddressForTransactions();
|
.checkChangeAddressForTransactions();
|
||||||
}
|
}
|
||||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.3, walletId));
|
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.3, walletId));
|
||||||
|
if (this is SparkInterface) {
|
||||||
|
// this should be called before updateTransactions()
|
||||||
|
await (this as SparkInterface).refreshSparkData();
|
||||||
|
}
|
||||||
|
|
||||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.50, walletId));
|
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.50, walletId));
|
||||||
final fetchFuture = updateTransactions();
|
final fetchFuture = updateTransactions();
|
||||||
|
|
|
@ -625,9 +625,19 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
||||||
// TODO: use coinlib
|
// TODO: use coinlib
|
||||||
|
|
||||||
final txb = bitcoindart.TransactionBuilder(
|
final txb = bitcoindart.TransactionBuilder(
|
||||||
network: bitcoindart.testnet,
|
network: bitcoindart.NetworkType(
|
||||||
|
messagePrefix: cryptoCurrency.networkParams.messagePrefix,
|
||||||
|
bech32: cryptoCurrency.networkParams.bech32Hrp,
|
||||||
|
bip32: bitcoindart.Bip32Type(
|
||||||
|
public: cryptoCurrency.networkParams.pubHDPrefix,
|
||||||
|
private: cryptoCurrency.networkParams.privHDPrefix,
|
||||||
|
),
|
||||||
|
pubKeyHash: cryptoCurrency.networkParams.p2pkhPrefix,
|
||||||
|
scriptHash: cryptoCurrency.networkParams.p2shPrefix,
|
||||||
|
wif: cryptoCurrency.networkParams.wifPrefix,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
txb.setVersion(1);
|
txb.setVersion(1); // TODO possibly override this for certain coins?
|
||||||
|
|
||||||
// Add transaction inputs
|
// Add transaction inputs
|
||||||
for (var i = 0; i < utxoSigningData.length; i++) {
|
for (var i = 0; i < utxoSigningData.length; i++) {
|
||||||
|
@ -1641,7 +1651,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateUTXOs() async {
|
Future<void> updateUTXOs() async {
|
||||||
final allAddresses = await fetchAllOwnAddresses();
|
final allAddresses = await fetchAddressesForElectrumXScan();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final fetchedUtxoList = <List<Map<String, dynamic>>>[];
|
final fetchedUtxoList = <List<Map<String, dynamic>>>[];
|
||||||
|
@ -1856,7 +1866,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
||||||
int estimateTxFee({required int vSize, required int feeRatePerKB});
|
int estimateTxFee({required int vSize, required int feeRatePerKB});
|
||||||
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB);
|
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB);
|
||||||
|
|
||||||
Future<List<Address>> fetchAllOwnAddresses();
|
Future<List<Address>> fetchAddressesForElectrumXScan();
|
||||||
|
|
||||||
/// Certain coins need to check if the utxo should be marked
|
/// Certain coins need to check if the utxo should be marked
|
||||||
/// as blocked as well as give a reason.
|
/// as blocked as well as give a reason.
|
||||||
|
|
|
@ -1047,7 +1047,7 @@ mixin LelantusInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
return mints;
|
return mints;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> anonymizeAllPublicFunds() async {
|
Future<void> anonymizeAllLelantus() async {
|
||||||
try {
|
try {
|
||||||
final mintResult = await _mintSelection();
|
final mintResult = await _mintSelection();
|
||||||
|
|
||||||
|
@ -1056,7 +1056,7 @@ mixin LelantusInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
unawaited(refresh());
|
unawaited(refresh());
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Exception caught in anonymizeAllPublicFunds(): $e\n$s",
|
"Exception caught in anonymizeAllLelantus(): $e\n$s",
|
||||||
level: LogLevel.Warning,
|
level: LogLevel.Warning,
|
||||||
);
|
);
|
||||||
rethrow;
|
rethrow;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,6 +16,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
coinlib_flutter
|
coinlib_flutter
|
||||||
|
flutter_libsparkmobile
|
||||||
tor_ffi_plugin
|
tor_ffi_plugin
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
PODS:
|
PODS:
|
||||||
|
- coinlib_flutter (0.3.2):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
- connectivity_plus (0.0.1):
|
- connectivity_plus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
|
@ -22,14 +25,11 @@ PODS:
|
||||||
- cw_shared_external (0.0.1):
|
- cw_shared_external (0.0.1):
|
||||||
- cw_shared_external/Boost (= 0.0.1)
|
- cw_shared_external/Boost (= 0.0.1)
|
||||||
- cw_shared_external/OpenSSL (= 0.0.1)
|
- cw_shared_external/OpenSSL (= 0.0.1)
|
||||||
- cw_shared_external/Sodium (= 0.0.1)
|
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- cw_shared_external/Boost (0.0.1):
|
- cw_shared_external/Boost (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- cw_shared_external/OpenSSL (0.0.1):
|
- cw_shared_external/OpenSSL (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- cw_shared_external/Sodium (0.0.1):
|
|
||||||
- FlutterMacOS
|
|
||||||
- cw_wownero (0.0.1):
|
- cw_wownero (0.0.1):
|
||||||
- cw_wownero/Boost (= 0.0.1)
|
- cw_wownero/Boost (= 0.0.1)
|
||||||
- cw_wownero/OpenSSL (= 0.0.1)
|
- cw_wownero/OpenSSL (= 0.0.1)
|
||||||
|
@ -55,6 +55,8 @@ PODS:
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- flutter_libepiccash (0.0.1):
|
- flutter_libepiccash (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- flutter_libsparkmobile (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- flutter_local_notifications (0.0.1):
|
- flutter_local_notifications (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- flutter_secure_storage_macos (6.1.1):
|
- flutter_secure_storage_macos (6.1.1):
|
||||||
|
@ -74,6 +76,7 @@ PODS:
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- stack_wallet_backup (0.0.1):
|
- stack_wallet_backup (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- tor_ffi_plugin (0.0.1)
|
||||||
- url_launcher_macos (0.0.1):
|
- url_launcher_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- wakelock_macos (0.0.1):
|
- wakelock_macos (0.0.1):
|
||||||
|
@ -82,6 +85,7 @@ PODS:
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
- coinlib_flutter (from `Flutter/ephemeral/.symlinks/plugins/coinlib_flutter/darwin`)
|
||||||
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
|
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
|
||||||
- cw_monero (from `Flutter/ephemeral/.symlinks/plugins/cw_monero/macos`)
|
- cw_monero (from `Flutter/ephemeral/.symlinks/plugins/cw_monero/macos`)
|
||||||
- cw_shared_external (from `Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos`)
|
- cw_shared_external (from `Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos`)
|
||||||
|
@ -90,6 +94,7 @@ DEPENDENCIES:
|
||||||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||||
- devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`)
|
- devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`)
|
||||||
- flutter_libepiccash (from `Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos`)
|
- flutter_libepiccash (from `Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos`)
|
||||||
|
- flutter_libsparkmobile (from `Flutter/ephemeral/.symlinks/plugins/flutter_libsparkmobile/macos`)
|
||||||
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
|
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
|
||||||
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
|
@ -99,6 +104,7 @@ DEPENDENCIES:
|
||||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
||||||
- stack_wallet_backup (from `Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos`)
|
- stack_wallet_backup (from `Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos`)
|
||||||
|
- tor_ffi_plugin (from `Flutter/ephemeral/.symlinks/plugins/tor_ffi_plugin/macos`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
|
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
|
||||||
- window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`)
|
- window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`)
|
||||||
|
@ -108,6 +114,8 @@ SPEC REPOS:
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
|
coinlib_flutter:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/coinlib_flutter/darwin
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
|
||||||
cw_monero:
|
cw_monero:
|
||||||
|
@ -124,6 +132,8 @@ EXTERNAL SOURCES:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/devicelocale/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/devicelocale/macos
|
||||||
flutter_libepiccash:
|
flutter_libepiccash:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos
|
||||||
|
flutter_libsparkmobile:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_libsparkmobile/macos
|
||||||
flutter_local_notifications:
|
flutter_local_notifications:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos
|
||||||
flutter_secure_storage_macos:
|
flutter_secure_storage_macos:
|
||||||
|
@ -142,6 +152,8 @@ EXTERNAL SOURCES:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
|
||||||
stack_wallet_backup:
|
stack_wallet_backup:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos
|
||||||
|
tor_ffi_plugin:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/tor_ffi_plugin/macos
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||||
wakelock_macos:
|
wakelock_macos:
|
||||||
|
@ -150,25 +162,28 @@ EXTERNAL SOURCES:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/window_size/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/window_size/macos
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
|
coinlib_flutter: 6abec900d67762a6e7ccfd567a3cd3ae00bbee35
|
||||||
connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747
|
connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747
|
||||||
cw_monero: a3442556ad3c06365c912735e4a23942a28692b1
|
cw_monero: 7acce7238d217e3993ecac6ec2dec07be728769a
|
||||||
cw_shared_external: 1f631d1132521baac5f4caed43176fa10d4e0d8b
|
cw_shared_external: c6adfd29c9be4d64f84e1fa9c541ccbcbdb6b457
|
||||||
cw_wownero: b4adb1e701fc363de27fa222fcaf4eff6f5fa63a
|
cw_wownero: bcd7f2ad6c0a3e8e2a51756fb14f0579b6f8b4ff
|
||||||
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
|
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
|
||||||
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
|
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
|
||||||
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
|
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
|
||||||
flutter_libepiccash: 9113ac75dd325f8bcf00bc3ab583c7fc2780cf3c
|
flutter_libepiccash: be1560a04150c5cc85bcf08d236ec2b3d1f5d8da
|
||||||
|
flutter_libsparkmobile: 8ae86b0ccc7e52c9db6b53e258ee2977deb184ab
|
||||||
flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4
|
flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4
|
||||||
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
|
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
|
||||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
isar_flutter_libs: 43385c99864c168fadba7c9adeddc5d38838ca6a
|
isar_flutter_libs: 43385c99864c168fadba7c9adeddc5d38838ca6a
|
||||||
lelantus: 3dfbf92b1e66b3573494dfe3d6a21c4988b5361b
|
lelantus: 308e42c5a648598936a07a234471dd8cf8e687a0
|
||||||
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
|
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
|
||||||
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
|
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||||
share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7
|
share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7
|
||||||
stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c
|
stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c
|
||||||
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
|
tor_ffi_plugin: 2566c1ed174688cca560fa0c64b7a799c66f07cb
|
||||||
|
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
|
||||||
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
|
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
|
||||||
window_size: 339dafa0b27a95a62a843042038fa6c3c48de195
|
window_size: 339dafa0b27a95a62a843042038fa6c3c48de195
|
||||||
|
|
||||||
|
|
|
@ -659,6 +659,15 @@ packages:
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
flutter_libsparkmobile:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: "6e2b2650a84fc5832dc676f8d016e530ae77851f"
|
||||||
|
resolved-ref: "6e2b2650a84fc5832dc676f8d016e530ae77851f"
|
||||||
|
url: "https://github.com/cypherstack/flutter_libsparkmobile.git"
|
||||||
|
source: git
|
||||||
|
version: "0.0.1"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
|
11
pubspec.yaml
11
pubspec.yaml
|
@ -27,6 +27,11 @@ dependencies:
|
||||||
lelantus:
|
lelantus:
|
||||||
path: ./crypto_plugins/flutter_liblelantus
|
path: ./crypto_plugins/flutter_liblelantus
|
||||||
|
|
||||||
|
flutter_libsparkmobile:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cypherstack/flutter_libsparkmobile.git
|
||||||
|
ref: d54b4a1f492e48696c3df27eb8c31131a681cbc2
|
||||||
|
|
||||||
flutter_libmonero:
|
flutter_libmonero:
|
||||||
path: ./crypto_plugins/flutter_libmonero
|
path: ./crypto_plugins/flutter_libmonero
|
||||||
|
|
||||||
|
@ -160,7 +165,11 @@ dependencies:
|
||||||
url: https://github.com/cypherstack/tezart.git
|
url: https://github.com/cypherstack/tezart.git
|
||||||
ref: 8a7070f533e63dd150edae99476f6853bfb25913
|
ref: 8a7070f533e63dd150edae99476f6853bfb25913
|
||||||
socks5_proxy: ^1.0.3+dev.3
|
socks5_proxy: ^1.0.3+dev.3
|
||||||
coinlib_flutter: ^1.0.0
|
coinlib_flutter:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cypherstack/coinlib.git
|
||||||
|
path: coinlib_flutter
|
||||||
|
ref: 4f549b8b511a63fdc1f44796ab43b10f586635cd
|
||||||
convert: ^3.1.1
|
convert: ^3.1.1
|
||||||
flutter_hooks: ^0.20.3
|
flutter_hooks: ^0.20.3
|
||||||
meta: ^1.9.1
|
meta: ^1.9.1
|
||||||
|
|
|
@ -384,7 +384,7 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient {
|
||||||
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i5.Future<Map<String, dynamic>>);
|
) as _i5.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
_i5.Future<Map<String, dynamic>> getSparkUsedCoinsTags({
|
_i5.Future<Set<String>> getSparkUsedCoinsTags({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
required int? startNumber,
|
required int? startNumber,
|
||||||
}) =>
|
}) =>
|
||||||
|
@ -397,9 +397,8 @@ class MockElectrumXClient extends _i1.Mock implements _i4.ElectrumXClient {
|
||||||
#startNumber: startNumber,
|
#startNumber: startNumber,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
returnValue:
|
returnValue: _i5.Future<Set<String>>.value(<String>{}),
|
||||||
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
) as _i5.Future<Set<String>>);
|
||||||
) as _i5.Future<Map<String, dynamic>>);
|
|
||||||
@override
|
@override
|
||||||
_i5.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
_i5.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
|
|
|
@ -74,6 +74,25 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Map<String, dynamic>> getSparkAnonymitySet({
|
||||||
|
required String? groupId,
|
||||||
|
String? blockhash = r'',
|
||||||
|
required _i5.Coin? coin,
|
||||||
|
}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkAnonymitySet,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
#groupId: groupId,
|
||||||
|
#blockhash: blockhash,
|
||||||
|
#coin: coin,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
returnValue:
|
||||||
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
|
@override
|
||||||
String base64ToHex(String? source) => (super.noSuchMethod(
|
String base64ToHex(String? source) => (super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#base64ToHex,
|
#base64ToHex,
|
||||||
|
@ -125,6 +144,16 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
||||||
) as _i4.Future<List<String>>);
|
) as _i4.Future<List<String>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({required _i5.Coin? coin}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkUsedCoinsTags,
|
||||||
|
[],
|
||||||
|
{#coin: coin},
|
||||||
|
),
|
||||||
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
|
) as _i4.Future<Set<String>>);
|
||||||
|
@override
|
||||||
_i4.Future<void> clearSharedTransactionCache({required _i5.Coin? coin}) =>
|
_i4.Future<void> clearSharedTransactionCache({required _i5.Coin? coin}) =>
|
||||||
(super.noSuchMethod(
|
(super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
|
|
|
@ -381,7 +381,7 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
_i4.Future<Map<String, dynamic>> getSparkUsedCoinsTags({
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
required int? startNumber,
|
required int? startNumber,
|
||||||
}) =>
|
}) =>
|
||||||
|
@ -394,9 +394,8 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
#startNumber: startNumber,
|
#startNumber: startNumber,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
returnValue:
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
) as _i4.Future<Set<String>>);
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
|
||||||
@override
|
@override
|
||||||
_i4.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
_i4.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
|
@ -516,6 +515,25 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Map<String, dynamic>> getSparkAnonymitySet({
|
||||||
|
required String? groupId,
|
||||||
|
String? blockhash = r'',
|
||||||
|
required _i6.Coin? coin,
|
||||||
|
}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkAnonymitySet,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
#groupId: groupId,
|
||||||
|
#blockhash: blockhash,
|
||||||
|
#coin: coin,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
returnValue:
|
||||||
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
|
@override
|
||||||
String base64ToHex(String? source) => (super.noSuchMethod(
|
String base64ToHex(String? source) => (super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#base64ToHex,
|
#base64ToHex,
|
||||||
|
@ -567,6 +585,16 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
||||||
) as _i4.Future<List<String>>);
|
) as _i4.Future<List<String>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({required _i6.Coin? coin}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkUsedCoinsTags,
|
||||||
|
[],
|
||||||
|
{#coin: coin},
|
||||||
|
),
|
||||||
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
|
) as _i4.Future<Set<String>>);
|
||||||
|
@override
|
||||||
_i4.Future<void> clearSharedTransactionCache({required _i6.Coin? coin}) =>
|
_i4.Future<void> clearSharedTransactionCache({required _i6.Coin? coin}) =>
|
||||||
(super.noSuchMethod(
|
(super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
|
|
|
@ -381,7 +381,7 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
_i4.Future<Map<String, dynamic>> getSparkUsedCoinsTags({
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
required int? startNumber,
|
required int? startNumber,
|
||||||
}) =>
|
}) =>
|
||||||
|
@ -394,9 +394,8 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
#startNumber: startNumber,
|
#startNumber: startNumber,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
returnValue:
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
) as _i4.Future<Set<String>>);
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
|
||||||
@override
|
@override
|
||||||
_i4.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
_i4.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
|
@ -516,6 +515,25 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Map<String, dynamic>> getSparkAnonymitySet({
|
||||||
|
required String? groupId,
|
||||||
|
String? blockhash = r'',
|
||||||
|
required _i6.Coin? coin,
|
||||||
|
}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkAnonymitySet,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
#groupId: groupId,
|
||||||
|
#blockhash: blockhash,
|
||||||
|
#coin: coin,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
returnValue:
|
||||||
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
|
@override
|
||||||
String base64ToHex(String? source) => (super.noSuchMethod(
|
String base64ToHex(String? source) => (super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#base64ToHex,
|
#base64ToHex,
|
||||||
|
@ -567,6 +585,16 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
||||||
) as _i4.Future<List<String>>);
|
) as _i4.Future<List<String>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({required _i6.Coin? coin}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkUsedCoinsTags,
|
||||||
|
[],
|
||||||
|
{#coin: coin},
|
||||||
|
),
|
||||||
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
|
) as _i4.Future<Set<String>>);
|
||||||
|
@override
|
||||||
_i4.Future<void> clearSharedTransactionCache({required _i6.Coin? coin}) =>
|
_i4.Future<void> clearSharedTransactionCache({required _i6.Coin? coin}) =>
|
||||||
(super.noSuchMethod(
|
(super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
|
|
|
@ -381,7 +381,7 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
_i4.Future<Map<String, dynamic>> getSparkUsedCoinsTags({
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
required int? startNumber,
|
required int? startNumber,
|
||||||
}) =>
|
}) =>
|
||||||
|
@ -394,9 +394,8 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
#startNumber: startNumber,
|
#startNumber: startNumber,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
returnValue:
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
) as _i4.Future<Set<String>>);
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
|
||||||
@override
|
@override
|
||||||
_i4.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
_i4.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
|
@ -516,6 +515,25 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Map<String, dynamic>> getSparkAnonymitySet({
|
||||||
|
required String? groupId,
|
||||||
|
String? blockhash = r'',
|
||||||
|
required _i6.Coin? coin,
|
||||||
|
}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkAnonymitySet,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
#groupId: groupId,
|
||||||
|
#blockhash: blockhash,
|
||||||
|
#coin: coin,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
returnValue:
|
||||||
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
|
@override
|
||||||
String base64ToHex(String? source) => (super.noSuchMethod(
|
String base64ToHex(String? source) => (super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#base64ToHex,
|
#base64ToHex,
|
||||||
|
@ -567,6 +585,16 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
||||||
) as _i4.Future<List<String>>);
|
) as _i4.Future<List<String>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({required _i6.Coin? coin}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkUsedCoinsTags,
|
||||||
|
[],
|
||||||
|
{#coin: coin},
|
||||||
|
),
|
||||||
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
|
) as _i4.Future<Set<String>>);
|
||||||
|
@override
|
||||||
_i4.Future<void> clearSharedTransactionCache({required _i6.Coin? coin}) =>
|
_i4.Future<void> clearSharedTransactionCache({required _i6.Coin? coin}) =>
|
||||||
(super.noSuchMethod(
|
(super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
|
|
|
@ -411,7 +411,7 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i5.Future<Map<String, dynamic>>);
|
) as _i5.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
_i5.Future<Map<String, dynamic>> getSparkUsedCoinsTags({
|
_i5.Future<Set<String>> getSparkUsedCoinsTags({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
required int? startNumber,
|
required int? startNumber,
|
||||||
}) =>
|
}) =>
|
||||||
|
@ -424,9 +424,8 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
#startNumber: startNumber,
|
#startNumber: startNumber,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
returnValue:
|
returnValue: _i5.Future<Set<String>>.value(<String>{}),
|
||||||
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
) as _i5.Future<Set<String>>);
|
||||||
) as _i5.Future<Map<String, dynamic>>);
|
|
||||||
@override
|
@override
|
||||||
_i5.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
_i5.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
|
@ -546,6 +545,25 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i5.Future<Map<String, dynamic>>);
|
) as _i5.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
|
_i5.Future<Map<String, dynamic>> getSparkAnonymitySet({
|
||||||
|
required String? groupId,
|
||||||
|
String? blockhash = r'',
|
||||||
|
required _i7.Coin? coin,
|
||||||
|
}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkAnonymitySet,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
#groupId: groupId,
|
||||||
|
#blockhash: blockhash,
|
||||||
|
#coin: coin,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
returnValue:
|
||||||
|
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
|
) as _i5.Future<Map<String, dynamic>>);
|
||||||
|
@override
|
||||||
String base64ToHex(String? source) => (super.noSuchMethod(
|
String base64ToHex(String? source) => (super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#base64ToHex,
|
#base64ToHex,
|
||||||
|
@ -597,6 +615,16 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
returnValue: _i5.Future<List<String>>.value(<String>[]),
|
returnValue: _i5.Future<List<String>>.value(<String>[]),
|
||||||
) as _i5.Future<List<String>>);
|
) as _i5.Future<List<String>>);
|
||||||
@override
|
@override
|
||||||
|
_i5.Future<Set<String>> getSparkUsedCoinsTags({required _i7.Coin? coin}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkUsedCoinsTags,
|
||||||
|
[],
|
||||||
|
{#coin: coin},
|
||||||
|
),
|
||||||
|
returnValue: _i5.Future<Set<String>>.value(<String>{}),
|
||||||
|
) as _i5.Future<Set<String>>);
|
||||||
|
@override
|
||||||
_i5.Future<void> clearSharedTransactionCache({required _i7.Coin? coin}) =>
|
_i5.Future<void> clearSharedTransactionCache({required _i7.Coin? coin}) =>
|
||||||
(super.noSuchMethod(
|
(super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
|
|
|
@ -381,7 +381,7 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
_i4.Future<Map<String, dynamic>> getSparkUsedCoinsTags({
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
required int? startNumber,
|
required int? startNumber,
|
||||||
}) =>
|
}) =>
|
||||||
|
@ -394,9 +394,8 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
#startNumber: startNumber,
|
#startNumber: startNumber,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
returnValue:
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
) as _i4.Future<Set<String>>);
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
|
||||||
@override
|
@override
|
||||||
_i4.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
_i4.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
|
@ -516,6 +515,25 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Map<String, dynamic>> getSparkAnonymitySet({
|
||||||
|
required String? groupId,
|
||||||
|
String? blockhash = r'',
|
||||||
|
required _i6.Coin? coin,
|
||||||
|
}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkAnonymitySet,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
#groupId: groupId,
|
||||||
|
#blockhash: blockhash,
|
||||||
|
#coin: coin,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
returnValue:
|
||||||
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
|
@override
|
||||||
String base64ToHex(String? source) => (super.noSuchMethod(
|
String base64ToHex(String? source) => (super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#base64ToHex,
|
#base64ToHex,
|
||||||
|
@ -567,6 +585,16 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
||||||
) as _i4.Future<List<String>>);
|
) as _i4.Future<List<String>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({required _i6.Coin? coin}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkUsedCoinsTags,
|
||||||
|
[],
|
||||||
|
{#coin: coin},
|
||||||
|
),
|
||||||
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
|
) as _i4.Future<Set<String>>);
|
||||||
|
@override
|
||||||
_i4.Future<void> clearSharedTransactionCache({required _i6.Coin? coin}) =>
|
_i4.Future<void> clearSharedTransactionCache({required _i6.Coin? coin}) =>
|
||||||
(super.noSuchMethod(
|
(super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
|
|
|
@ -381,7 +381,7 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
_i4.Future<Map<String, dynamic>> getSparkUsedCoinsTags({
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
required int? startNumber,
|
required int? startNumber,
|
||||||
}) =>
|
}) =>
|
||||||
|
@ -394,9 +394,8 @@ class MockElectrumXClient extends _i1.Mock implements _i3.ElectrumXClient {
|
||||||
#startNumber: startNumber,
|
#startNumber: startNumber,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
returnValue:
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
) as _i4.Future<Set<String>>);
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
|
||||||
@override
|
@override
|
||||||
_i4.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
_i4.Future<List<Map<String, dynamic>>> getSparkMintMetaData({
|
||||||
String? requestID,
|
String? requestID,
|
||||||
|
@ -516,6 +515,25 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
) as _i4.Future<Map<String, dynamic>>);
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Map<String, dynamic>> getSparkAnonymitySet({
|
||||||
|
required String? groupId,
|
||||||
|
String? blockhash = r'',
|
||||||
|
required _i6.Coin? coin,
|
||||||
|
}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkAnonymitySet,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
#groupId: groupId,
|
||||||
|
#blockhash: blockhash,
|
||||||
|
#coin: coin,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
returnValue:
|
||||||
|
_i4.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
|
||||||
|
) as _i4.Future<Map<String, dynamic>>);
|
||||||
|
@override
|
||||||
String base64ToHex(String? source) => (super.noSuchMethod(
|
String base64ToHex(String? source) => (super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#base64ToHex,
|
#base64ToHex,
|
||||||
|
@ -567,6 +585,16 @@ class MockCachedElectrumXClient extends _i1.Mock
|
||||||
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
returnValue: _i4.Future<List<String>>.value(<String>[]),
|
||||||
) as _i4.Future<List<String>>);
|
) as _i4.Future<List<String>>);
|
||||||
@override
|
@override
|
||||||
|
_i4.Future<Set<String>> getSparkUsedCoinsTags({required _i6.Coin? coin}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#getSparkUsedCoinsTags,
|
||||||
|
[],
|
||||||
|
{#coin: coin},
|
||||||
|
),
|
||||||
|
returnValue: _i4.Future<Set<String>>.value(<String>{}),
|
||||||
|
) as _i4.Future<Set<String>>);
|
||||||
|
@override
|
||||||
_i4.Future<void> clearSharedTransactionCache({required _i6.Coin? coin}) =>
|
_i4.Future<void> clearSharedTransactionCache({required _i6.Coin? coin}) =>
|
||||||
(super.noSuchMethod(
|
(super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
|
|
|
@ -16,6 +16,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
flutter_libsparkmobile
|
||||||
tor_ffi_plugin
|
tor_ffi_plugin
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue