mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-16 17:27:39 +00:00
Merge pull request #898 from cypherstack/firo_exchange_addresses
Firo exchange addresses and bug fixes
This commit is contained in:
commit
a4d30e17b7
48 changed files with 809 additions and 455 deletions
|
@ -1 +1 @@
|
|||
Subproject commit f1cfc0cbb675c5e2d03c30152514fcbb28a465f4
|
||||
Subproject commit dd2d493199dd9c697abc8bc6bc94f466005bf70a
|
|
@ -12,6 +12,7 @@ import '../../electrumx_rpc/electrumx_client.dart';
|
|||
import '../../utilities/extensions/extensions.dart';
|
||||
import '../../utilities/logger.dart';
|
||||
import '../../utilities/stack_file_system.dart';
|
||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||
|
||||
part 'firo_cache_coordinator.dart';
|
||||
part 'firo_cache_reader.dart';
|
||||
|
@ -31,29 +32,39 @@ void _debugLog(Object? object) {
|
|||
abstract class _FiroCache {
|
||||
static const int _setCacheVersion = 1;
|
||||
static const int _tagsCacheVersion = 2;
|
||||
static const String sparkSetCacheFileName =
|
||||
"spark_set_v$_setCacheVersion.sqlite3";
|
||||
static const String sparkUsedTagsCacheFileName =
|
||||
"spark_tags_v$_tagsCacheVersion.sqlite3";
|
||||
|
||||
static Database? _setCacheDB;
|
||||
static Database? _usedTagsCacheDB;
|
||||
static Database get setCacheDB {
|
||||
if (_setCacheDB == null) {
|
||||
static final networks = [
|
||||
CryptoCurrencyNetwork.main,
|
||||
CryptoCurrencyNetwork.test,
|
||||
];
|
||||
|
||||
static String sparkSetCacheFileName(CryptoCurrencyNetwork network) =>
|
||||
network == CryptoCurrencyNetwork.main
|
||||
? "spark_set_v$_setCacheVersion.sqlite3"
|
||||
: "spark_set_v${_setCacheVersion}_${network.name}.sqlite3";
|
||||
static String sparkUsedTagsCacheFileName(CryptoCurrencyNetwork network) =>
|
||||
network == CryptoCurrencyNetwork.main
|
||||
? "spark_tags_v$_tagsCacheVersion.sqlite3"
|
||||
: "spark_tags_v${_tagsCacheVersion}_${network.name}.sqlite3";
|
||||
|
||||
static final Map<CryptoCurrencyNetwork, Database> _setCacheDB = {};
|
||||
static final Map<CryptoCurrencyNetwork, Database> _usedTagsCacheDB = {};
|
||||
static Database setCacheDB(CryptoCurrencyNetwork network) {
|
||||
if (_setCacheDB[network] == null) {
|
||||
throw Exception(
|
||||
"FiroCache.init() must be called before accessing FiroCache.db!",
|
||||
);
|
||||
}
|
||||
return _setCacheDB!;
|
||||
return _setCacheDB[network]!;
|
||||
}
|
||||
|
||||
static Database get usedTagsCacheDB {
|
||||
if (_usedTagsCacheDB == null) {
|
||||
static Database usedTagsCacheDB(CryptoCurrencyNetwork network) {
|
||||
if (_usedTagsCacheDB[network] == null) {
|
||||
throw Exception(
|
||||
"FiroCache.init() must be called before accessing FiroCache.db!",
|
||||
);
|
||||
}
|
||||
return _usedTagsCacheDB!;
|
||||
return _usedTagsCacheDB[network]!;
|
||||
}
|
||||
|
||||
static Future<void>? _initFuture;
|
||||
|
@ -63,30 +74,34 @@ abstract class _FiroCache {
|
|||
final sqliteDir =
|
||||
await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
||||
|
||||
final sparkSetCacheFile = File("${sqliteDir.path}/$sparkSetCacheFileName");
|
||||
final sparkUsedTagsCacheFile =
|
||||
File("${sqliteDir.path}/$sparkUsedTagsCacheFileName");
|
||||
for (final network in networks) {
|
||||
final sparkSetCacheFile =
|
||||
File("${sqliteDir.path}/${sparkSetCacheFileName(network)}");
|
||||
|
||||
if (!(await sparkSetCacheFile.exists())) {
|
||||
await _createSparkSetCacheDb(sparkSetCacheFile.path);
|
||||
}
|
||||
if (!(await sparkUsedTagsCacheFile.exists())) {
|
||||
await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path);
|
||||
}
|
||||
final sparkUsedTagsCacheFile =
|
||||
File("${sqliteDir.path}/${sparkUsedTagsCacheFileName(network)}");
|
||||
|
||||
_setCacheDB = sqlite3.open(
|
||||
sparkSetCacheFile.path,
|
||||
mode: OpenMode.readWrite,
|
||||
);
|
||||
_usedTagsCacheDB = sqlite3.open(
|
||||
sparkUsedTagsCacheFile.path,
|
||||
mode: OpenMode.readWrite,
|
||||
);
|
||||
if (!(await sparkSetCacheFile.exists())) {
|
||||
await _createSparkSetCacheDb(sparkSetCacheFile.path);
|
||||
}
|
||||
if (!(await sparkUsedTagsCacheFile.exists())) {
|
||||
await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path);
|
||||
}
|
||||
|
||||
_setCacheDB[network] = sqlite3.open(
|
||||
sparkSetCacheFile.path,
|
||||
mode: OpenMode.readWrite,
|
||||
);
|
||||
_usedTagsCacheDB[network] = sqlite3.open(
|
||||
sparkUsedTagsCacheFile.path,
|
||||
mode: OpenMode.readWrite,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _deleteAllCache() async {
|
||||
static Future<void> _deleteAllCache(CryptoCurrencyNetwork network) async {
|
||||
final start = DateTime.now();
|
||||
setCacheDB.execute(
|
||||
setCacheDB(network).execute(
|
||||
"""
|
||||
DELETE FROM SparkSet;
|
||||
DELETE FROM SparkCoin;
|
||||
|
@ -94,7 +109,7 @@ abstract class _FiroCache {
|
|||
VACUUM;
|
||||
""",
|
||||
);
|
||||
usedTagsCacheDB.execute(
|
||||
usedTagsCacheDB(network).execute(
|
||||
"""
|
||||
DELETE FROM SparkUsedCoinTags;
|
||||
VACUUM;
|
||||
|
|
|
@ -5,7 +5,7 @@ typedef LTagPair = ({String tag, String txid});
|
|||
/// Wrapper class for [_FiroCache] as [_FiroCache] should eventually be handled in a
|
||||
/// background isolate and [FiroCacheCoordinator] should manage that isolate
|
||||
abstract class FiroCacheCoordinator {
|
||||
static _FiroCacheWorker? _worker;
|
||||
static final Map<CryptoCurrencyNetwork, _FiroCacheWorker> _workers = {};
|
||||
|
||||
static bool _init = false;
|
||||
static Future<void> init() async {
|
||||
|
@ -14,20 +14,22 @@ abstract class FiroCacheCoordinator {
|
|||
}
|
||||
_init = true;
|
||||
await _FiroCache.init();
|
||||
_worker = await _FiroCacheWorker.spawn();
|
||||
for (final network in _FiroCache.networks) {
|
||||
_workers[network] = await _FiroCacheWorker.spawn(network);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> clearSharedCache() async {
|
||||
return await _FiroCache._deleteAllCache();
|
||||
static Future<void> clearSharedCache(CryptoCurrencyNetwork network) async {
|
||||
return await _FiroCache._deleteAllCache(network);
|
||||
}
|
||||
|
||||
static Future<String> getSparkCacheSize() async {
|
||||
static Future<String> getSparkCacheSize(CryptoCurrencyNetwork network) async {
|
||||
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
||||
final setCacheFile = File(
|
||||
"${dir.path}/${_FiroCache.sparkSetCacheFileName}",
|
||||
"${dir.path}/${_FiroCache.sparkSetCacheFileName(network)}",
|
||||
);
|
||||
final usedTagsCacheFile = File(
|
||||
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}",
|
||||
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName(network)}",
|
||||
);
|
||||
final int bytes =
|
||||
((await setCacheFile.exists()) ? await setCacheFile.length() : 0) +
|
||||
|
@ -51,13 +53,14 @@ abstract class FiroCacheCoordinator {
|
|||
|
||||
static Future<void> runFetchAndUpdateSparkUsedCoinTags(
|
||||
ElectrumXClient client,
|
||||
CryptoCurrencyNetwork network,
|
||||
) async {
|
||||
final count = await FiroCacheCoordinator.getUsedCoinTagsCount();
|
||||
final count = await FiroCacheCoordinator.getUsedCoinTagsCount(network);
|
||||
final unhashedTags = await client.getSparkUnhashedUsedCoinsTagsWithTxHashes(
|
||||
startNumber: count,
|
||||
);
|
||||
if (unhashedTags.isNotEmpty) {
|
||||
await _worker!.runTask(
|
||||
await _workers[network]!.runTask(
|
||||
FCTask(
|
||||
func: FCFuncName._updateSparkUsedTagsWith,
|
||||
data: unhashedTags,
|
||||
|
@ -69,10 +72,12 @@ abstract class FiroCacheCoordinator {
|
|||
static Future<void> runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
||||
int groupId,
|
||||
ElectrumXClient client,
|
||||
CryptoCurrencyNetwork network,
|
||||
) async {
|
||||
final blockhashResult =
|
||||
await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
||||
groupId,
|
||||
network,
|
||||
);
|
||||
final blockHash = blockhashResult?.blockHash ?? "";
|
||||
|
||||
|
@ -81,7 +86,7 @@ abstract class FiroCacheCoordinator {
|
|||
startBlockHash: blockHash.toHexReversedFromBase64,
|
||||
);
|
||||
|
||||
await _worker!.runTask(
|
||||
await _workers[network]!.runTask(
|
||||
FCTask(
|
||||
func: FCFuncName._updateSparkAnonSetCoinsWith,
|
||||
data: (groupId, json),
|
||||
|
@ -91,17 +96,22 @@ abstract class FiroCacheCoordinator {
|
|||
|
||||
// ===========================================================================
|
||||
|
||||
static Future<Set<String>> getUsedCoinTags(int startNumber) async {
|
||||
static Future<Set<String>> getUsedCoinTags(
|
||||
int startNumber,
|
||||
CryptoCurrencyNetwork network,
|
||||
) async {
|
||||
final result = await _Reader._getSparkUsedCoinTags(
|
||||
startNumber,
|
||||
db: _FiroCache.usedTagsCacheDB,
|
||||
db: _FiroCache.usedTagsCacheDB(network),
|
||||
);
|
||||
return result.map((e) => e["tag"] as String).toSet();
|
||||
}
|
||||
|
||||
static Future<int> getUsedCoinTagsCount() async {
|
||||
static Future<int> getUsedCoinTagsCount(
|
||||
CryptoCurrencyNetwork network,
|
||||
) async {
|
||||
final result = await _Reader._getUsedCoinTagsCount(
|
||||
db: _FiroCache.usedTagsCacheDB,
|
||||
db: _FiroCache.usedTagsCacheDB(network),
|
||||
);
|
||||
if (result.isEmpty) {
|
||||
return 0;
|
||||
|
@ -111,13 +121,14 @@ abstract class FiroCacheCoordinator {
|
|||
|
||||
static Future<List<LTagPair>> getUsedCoinTxidsFor({
|
||||
required List<String> tags,
|
||||
required CryptoCurrencyNetwork network,
|
||||
}) async {
|
||||
if (tags.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
final result = await _Reader._getUsedCoinTxidsFor(
|
||||
tags,
|
||||
db: _FiroCache.usedTagsCacheDB,
|
||||
db: _FiroCache.usedTagsCacheDB(network),
|
||||
);
|
||||
|
||||
if (result.isEmpty) {
|
||||
|
@ -135,20 +146,22 @@ abstract class FiroCacheCoordinator {
|
|||
|
||||
static Future<Set<String>> getUsedCoinTagsFor({
|
||||
required String txid,
|
||||
required CryptoCurrencyNetwork network,
|
||||
}) async {
|
||||
final result = await _Reader._getUsedCoinTagsFor(
|
||||
txid,
|
||||
db: _FiroCache.usedTagsCacheDB,
|
||||
db: _FiroCache.usedTagsCacheDB(network),
|
||||
);
|
||||
return result.map((e) => e["tag"] as String).toSet();
|
||||
}
|
||||
|
||||
static Future<bool> checkTagIsUsed(
|
||||
String tag,
|
||||
CryptoCurrencyNetwork network,
|
||||
) async {
|
||||
return await _Reader._checkTagIsUsed(
|
||||
tag,
|
||||
db: _FiroCache.usedTagsCacheDB,
|
||||
db: _FiroCache.usedTagsCacheDB(network),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -161,10 +174,11 @@ abstract class FiroCacheCoordinator {
|
|||
})>> getSetCoinsForGroupId(
|
||||
int groupId, {
|
||||
int? newerThanTimeStamp,
|
||||
required CryptoCurrencyNetwork network,
|
||||
}) async {
|
||||
final resultSet = await _Reader._getSetCoinsForGroupId(
|
||||
groupId,
|
||||
db: _FiroCache.setCacheDB,
|
||||
db: _FiroCache.setCacheDB(network),
|
||||
newerThanTimeStamp: newerThanTimeStamp,
|
||||
);
|
||||
return resultSet
|
||||
|
@ -187,10 +201,11 @@ abstract class FiroCacheCoordinator {
|
|||
int timestampUTC,
|
||||
})?> getLatestSetInfoForGroupId(
|
||||
int groupId,
|
||||
CryptoCurrencyNetwork network,
|
||||
) async {
|
||||
final result = await _Reader._getLatestSetInfoForGroupId(
|
||||
groupId,
|
||||
db: _FiroCache.setCacheDB,
|
||||
db: _FiroCache.setCacheDB(network),
|
||||
);
|
||||
|
||||
if (result.isEmpty) {
|
||||
|
@ -206,10 +221,11 @@ abstract class FiroCacheCoordinator {
|
|||
|
||||
static Future<bool> checkSetInfoForGroupIdExists(
|
||||
int groupId,
|
||||
CryptoCurrencyNetwork network,
|
||||
) async {
|
||||
return await _Reader._checkSetInfoForGroupIdExists(
|
||||
groupId,
|
||||
db: _FiroCache.setCacheDB,
|
||||
db: _FiroCache.setCacheDB(network),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,11 +25,12 @@ class _FiroCacheWorker {
|
|||
return await completer.future;
|
||||
}
|
||||
|
||||
static Future<_FiroCacheWorker> spawn() async {
|
||||
static Future<_FiroCacheWorker> spawn(CryptoCurrencyNetwork network) async {
|
||||
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
||||
final setCacheFilePath = "${dir.path}/${_FiroCache.sparkSetCacheFileName}";
|
||||
final setCacheFilePath =
|
||||
"${dir.path}/${_FiroCache.sparkSetCacheFileName(network)}";
|
||||
final usedTagsCacheFilePath =
|
||||
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}";
|
||||
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName(network)}";
|
||||
|
||||
final initPort = RawReceivePort();
|
||||
final connection = Completer<(ReceivePort, SendPort)>.sync();
|
||||
|
|
|
@ -1043,7 +1043,7 @@ class ElectrumXClient {
|
|||
final start = DateTime.now();
|
||||
final response = await request(
|
||||
requestID: requestID,
|
||||
command: "spark.getmempooltxids",
|
||||
command: "spark.getmempoolsparktxids",
|
||||
);
|
||||
|
||||
final txids = List<String>.from(response as List)
|
||||
|
@ -1072,7 +1072,7 @@ class ElectrumXClient {
|
|||
final start = DateTime.now();
|
||||
final response = await request(
|
||||
requestID: requestID,
|
||||
command: "spark.getmempooltxs",
|
||||
command: "spark.getmempoolsparktxs",
|
||||
args: [
|
||||
{
|
||||
"txids": txids,
|
||||
|
@ -1087,10 +1087,10 @@ class ElectrumXClient {
|
|||
(
|
||||
txid: entry.key,
|
||||
serialContext:
|
||||
List<String>.from(entry.value["Serial_context"] as List),
|
||||
List<String>.from(entry.value["serial_context"] as List),
|
||||
// the space after lTags is required lol
|
||||
lTags: List<String>.from(entry.value["lTags "] as List),
|
||||
coins: List<String>.from(entry.value["Coins"] as List),
|
||||
coins: List<String>.from(entry.value["coins"] as List),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1142,6 +1142,38 @@ class ElectrumXClient {
|
|||
}
|
||||
// ===========================================================================
|
||||
|
||||
Future<bool> isMasterNodeCollateral({
|
||||
String? requestID,
|
||||
required String txid,
|
||||
required int index,
|
||||
}) async {
|
||||
try {
|
||||
final start = DateTime.now();
|
||||
final response = await request(
|
||||
requestID: requestID,
|
||||
command: "blockchain.checkifmncollateral",
|
||||
args: [
|
||||
txid,
|
||||
index.toString(),
|
||||
],
|
||||
);
|
||||
|
||||
Logging.instance.log(
|
||||
"Finished ElectrumXClient.isMasterNodeCollateral, "
|
||||
"response: $response, "
|
||||
"Duration=${DateTime.now().difference(start)}",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
|
||||
return response as bool;
|
||||
} catch (e) {
|
||||
Logging.instance.log(e, level: LogLevel.Error);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
/// Get the current fee rate.
|
||||
///
|
||||
/// Returns a map with the kay "rate" that corresponds to the free rate in satoshis
|
||||
|
|
87
lib/models/coinlib/exp2pkh_address.dart
Normal file
87
lib/models/coinlib/exp2pkh_address.dart
Normal file
|
@ -0,0 +1,87 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
||||
|
||||
const OP_EXCHANGEADDR = 0xe0;
|
||||
|
||||
class EXP2PKHAddress implements coinlib.Address {
|
||||
/// The 160bit public key or redeemScript hash for the base58 address
|
||||
final Uint8List _hash;
|
||||
|
||||
/// The network and address type version of the address
|
||||
final Uint8List version;
|
||||
|
||||
String? _encodedCache;
|
||||
|
||||
EXP2PKHAddress._(Uint8List hash, this.version) : _hash = hash {
|
||||
if (version.length != 3) {
|
||||
throw ArgumentError(
|
||||
"version bytes length must be 3",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
factory EXP2PKHAddress.fromString(String encoded, Uint8List versionBytes) {
|
||||
if (versionBytes.length != 3) {
|
||||
throw ArgumentError(
|
||||
"version bytes length must be 3",
|
||||
);
|
||||
}
|
||||
|
||||
final data = coinlib.base58Decode(encoded);
|
||||
if (data.length != 23) throw coinlib.InvalidAddress();
|
||||
|
||||
final version = data.sublist(0, 3);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (version[i] != versionBytes[i]) {
|
||||
throw Exception("EX address version bytes do not match");
|
||||
}
|
||||
}
|
||||
|
||||
final payload = data.sublist(3);
|
||||
|
||||
final addr = EXP2PKHAddress._(payload, version);
|
||||
|
||||
addr._encodedCache = encoded;
|
||||
return addr;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => _encodedCache.toString();
|
||||
|
||||
@override
|
||||
coinlib.Program get program => EXP2PKH.fromHash(_hash);
|
||||
}
|
||||
|
||||
class EXP2PKH implements coinlib.Program {
|
||||
static const template =
|
||||
"OP_EXCHANGEADDR OP_DUP OP_HASH160 <20-bytes> OP_EQUALVERIFY OP_CHECKSIG";
|
||||
|
||||
@override
|
||||
final coinlib.Script script;
|
||||
|
||||
EXP2PKH.fromScript(this.script);
|
||||
|
||||
factory EXP2PKH.fromHash(Uint8List pkHash) {
|
||||
final List<coinlib.ScriptOp> ops = [
|
||||
coinlib.ScriptOpCode(OP_EXCHANGEADDR),
|
||||
];
|
||||
final parts = template.split(" ").sublist(1);
|
||||
for (final name in parts) {
|
||||
if (name.startsWith("OP_")) {
|
||||
ops.add(
|
||||
coinlib.ScriptOpCode(
|
||||
coinlib.scriptOpNameToCode[name.substring(3)]!,
|
||||
),
|
||||
);
|
||||
} else if (name == "<20-bytes>") {
|
||||
ops.add(coinlib.ScriptPushData(pkHash));
|
||||
} else {
|
||||
throw Exception("Something went wrong in this hacked code");
|
||||
}
|
||||
}
|
||||
|
||||
return EXP2PKH.fromScript(coinlib.Script(ops));
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../../../../frost_route_generator.dart';
|
||||
import '../../../../wallet_view/transaction_views/transaction_details_view.dart';
|
||||
import '../../../../../providers/frost_wallet/frost_wallet_providers.dart';
|
||||
import '../../../../../services/frost.dart';
|
||||
import '../../../../../themes/stack_colors.dart';
|
||||
|
@ -17,6 +16,8 @@ import '../../../../../widgets/desktop/secondary_button.dart';
|
|||
import '../../../../../widgets/detail_item.dart';
|
||||
import '../../../../../widgets/dialogs/simple_mobile_dialog.dart';
|
||||
import '../../../../../widgets/frost_step_user_steps.dart';
|
||||
import '../../../../../widgets/qr.dart';
|
||||
import '../../../../wallet_view/transaction_views/transaction_details_view.dart';
|
||||
|
||||
class FrostCreateStep1a extends ConsumerStatefulWidget {
|
||||
const FrostCreateStep1a({super.key});
|
||||
|
@ -162,14 +163,9 @@ class _FrostCreateStep1aState extends ConsumerState<FrostCreateStep1a> {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
QrImageView(
|
||||
QR(
|
||||
data: ref.watch(pFrostMultisigConfig.state).state ?? "Error",
|
||||
size: 220,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../../../frost_route_generator.dart';
|
||||
import '../../../../providers/db/main_db_provider.dart';
|
||||
|
@ -23,6 +22,7 @@ import '../../../../widgets/detail_item.dart';
|
|||
import '../../../../widgets/dialogs/frost/frost_error_dialog.dart';
|
||||
import '../../../../widgets/dialogs/simple_mobile_dialog.dart';
|
||||
import '../../../../widgets/frost_step_user_steps.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
import '../../../wallet_view/transaction_views/transaction_details_view.dart';
|
||||
|
||||
class FrostReshareStep1a extends ConsumerStatefulWidget {
|
||||
|
@ -239,14 +239,9 @@ class _FrostReshareStep1aState extends ConsumerState<FrostReshareStep1a> {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
QrImageView(
|
||||
QR(
|
||||
data: ref.watch(pFrostResharingData).resharerRConfig!,
|
||||
size: 220,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -14,7 +14,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../app_config.dart';
|
||||
|
@ -37,6 +36,7 @@ import '../../../wallets/wallet/impl/firo_wallet.dart';
|
|||
import '../../../widgets/background.dart';
|
||||
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../widgets/qr.dart';
|
||||
import '../../../widgets/rounded_container.dart';
|
||||
import '../../../widgets/rounded_white_container.dart';
|
||||
import '../../../widgets/stack_dialog.dart';
|
||||
|
@ -751,7 +751,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
height: 24,
|
||||
),
|
||||
Center(
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
// TODO: grab coin uri scheme from somewhere
|
||||
// data: "${coin.uriScheme}:$receivingAddress",
|
||||
data: model.trade!.payInAddress,
|
||||
|
@ -759,9 +759,6 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
.size
|
||||
.width /
|
||||
2,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -16,7 +16,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
|
@ -50,6 +49,7 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart';
|
|||
import '../../widgets/custom_buttons/blue_text_button.dart';
|
||||
import '../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../widgets/desktop/secondary_button.dart';
|
||||
import '../../widgets/qr.dart';
|
||||
import '../../widgets/rounded_container.dart';
|
||||
import '../../widgets/rounded_white_container.dart';
|
||||
import '../../widgets/stack_dialog.dart';
|
||||
|
@ -808,15 +808,9 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
child: SizedBox(
|
||||
width: width + 20,
|
||||
height: width + 20,
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: trade.payInAddress,
|
||||
size: width,
|
||||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.popupBG,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -14,15 +14,11 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../exceptions/wallet/insufficient_balance_exception.dart';
|
||||
import '../../../models/paynym/paynym_account_lite.dart';
|
||||
import '../../../notifications/show_flush_bar.dart';
|
||||
import 'confirm_paynym_connect_dialog.dart';
|
||||
import '../paynym_home_view.dart';
|
||||
import '../subwidgets/paynym_bot.dart';
|
||||
import '../../send_view/confirm_transaction_view.dart';
|
||||
import '../../send_view/send_view.dart';
|
||||
import '../../../providers/global/locale_provider.dart';
|
||||
import '../../../providers/global/wallets_provider.dart';
|
||||
import '../../../route_generator.dart';
|
||||
|
@ -37,9 +33,14 @@ import '../../../widgets/desktop/desktop_dialog.dart';
|
|||
import '../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../widgets/loading_indicator.dart';
|
||||
import '../../../widgets/qr.dart';
|
||||
import '../../../widgets/rounded_container.dart';
|
||||
import '../../../widgets/stack_dialog.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import '../../send_view/confirm_transaction_view.dart';
|
||||
import '../../send_view/send_view.dart';
|
||||
import '../paynym_home_view.dart';
|
||||
import '../subwidgets/paynym_bot.dart';
|
||||
import 'confirm_paynym_connect_dialog.dart';
|
||||
|
||||
class PaynymDetailsPopup extends ConsumerStatefulWidget {
|
||||
const PaynymDetailsPopup({
|
||||
|
@ -365,12 +366,10 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
|||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
QrImageView(
|
||||
QR(
|
||||
padding: const EdgeInsets.all(0),
|
||||
size: 100,
|
||||
data: widget.accountLite.code,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.textDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -12,10 +12,9 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../../models/paynym/paynym_account.dart';
|
||||
import '../../../notifications/show_flush_bar.dart';
|
||||
import '../subwidgets/paynym_bot.dart';
|
||||
import '../../../themes/stack_colors.dart';
|
||||
import '../../../utilities/assets.dart';
|
||||
import '../../../utilities/text_styles.dart';
|
||||
|
@ -23,6 +22,8 @@ import '../../../utilities/util.dart';
|
|||
import '../../../widgets/custom_buttons/blue_text_button.dart';
|
||||
import '../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import '../../../widgets/qr.dart';
|
||||
import '../subwidgets/paynym_bot.dart';
|
||||
|
||||
class PaynymQrPopup extends StatelessWidget {
|
||||
const PaynymQrPopup({
|
||||
|
@ -157,12 +158,10 @@ class PaynymQrPopup extends StatelessWidget {
|
|||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
QrImageView(
|
||||
QR(
|
||||
padding: const EdgeInsets.all(0),
|
||||
size: 130,
|
||||
data: paynymAccount.nonSegwitPaymentCode.code,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.textDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -14,19 +14,15 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../../exceptions/wallet/insufficient_balance_exception.dart';
|
||||
import '../../../models/paynym/paynym_account_lite.dart';
|
||||
import '../../../notifications/show_flush_bar.dart';
|
||||
import '../dialogs/confirm_paynym_connect_dialog.dart';
|
||||
import 'paynym_bot.dart';
|
||||
import '../../send_view/confirm_transaction_view.dart';
|
||||
import '../../../pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart';
|
||||
import '../../../providers/global/locale_provider.dart';
|
||||
import '../../../providers/global/wallets_provider.dart';
|
||||
import '../../../themes/stack_colors.dart';
|
||||
import '../../../utilities/assets.dart';
|
||||
|
||||
import '../../../utilities/text_styles.dart';
|
||||
import '../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
import '../../../wallets/models/tx_data.dart';
|
||||
|
@ -36,8 +32,12 @@ import '../../../widgets/custom_buttons/paynym_follow_toggle_button.dart';
|
|||
import '../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../widgets/loading_indicator.dart';
|
||||
import '../../../widgets/qr.dart';
|
||||
import '../../../widgets/rounded_container.dart';
|
||||
import '../../../widgets/rounded_white_container.dart';
|
||||
import '../../send_view/confirm_transaction_view.dart';
|
||||
import '../dialogs/confirm_paynym_connect_dialog.dart';
|
||||
import 'paynym_bot.dart';
|
||||
|
||||
class DesktopPaynymDetails extends ConsumerStatefulWidget {
|
||||
const DesktopPaynymDetails({
|
||||
|
@ -359,12 +359,10 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
|
|||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
QrImageView(
|
||||
QR(
|
||||
padding: const EdgeInsets.all(0),
|
||||
size: 100,
|
||||
data: widget.accountLite.code,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.textDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -20,7 +20,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../../../db/isar/main_db.dart';
|
||||
|
@ -39,6 +38,7 @@ import '../../../widgets/custom_buttons/blue_text_button.dart';
|
|||
import '../../../widgets/custom_buttons/simple_edit_button.dart';
|
||||
import '../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../widgets/qr.dart';
|
||||
import '../../../widgets/rounded_white_container.dart';
|
||||
import '../../../widgets/stack_dialog.dart';
|
||||
|
||||
|
@ -302,19 +302,13 @@ class _AddressCardState extends ConsumerState<AddressCard> {
|
|||
Center(
|
||||
child: RepaintBoundary(
|
||||
key: _qrKey,
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: AddressUtils.buildUriString(
|
||||
widget.coin,
|
||||
address.value,
|
||||
{},
|
||||
),
|
||||
size: 220,
|
||||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.popupBG,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../../db/isar/main_db.dart';
|
||||
import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||
|
@ -31,6 +30,7 @@ import '../../../widgets/custom_buttons/simple_copy_button.dart';
|
|||
import '../../../widgets/custom_buttons/simple_edit_button.dart';
|
||||
import '../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import '../../../widgets/qr.dart';
|
||||
import '../../../widgets/rounded_white_container.dart';
|
||||
import '../../../widgets/transaction_card.dart';
|
||||
import '../../wallet_view/sub_widgets/no_transactions_found.dart';
|
||||
|
@ -92,18 +92,13 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
|||
Center(
|
||||
child: RepaintBoundary(
|
||||
key: _qrKey,
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: AddressUtils.buildUriString(
|
||||
ref.watch(pWalletCoin(widget.walletId)),
|
||||
address.value,
|
||||
{},
|
||||
),
|
||||
size: 220,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -289,19 +284,13 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
|||
Center(
|
||||
child: RepaintBoundary(
|
||||
key: _qrKey,
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: AddressUtils.buildUriString(
|
||||
coin,
|
||||
address.value,
|
||||
{},
|
||||
),
|
||||
size: 220,
|
||||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -18,7 +18,6 @@ import 'package:flutter/rendering.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../../../notifications/show_flush_bar.dart';
|
||||
|
@ -31,6 +30,7 @@ import '../../../utilities/util.dart';
|
|||
import '../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../widgets/qr.dart';
|
||||
import '../../../widgets/stack_dialog.dart';
|
||||
|
||||
class AddressQrPopup extends StatefulWidget {
|
||||
|
@ -140,17 +140,13 @@ class _AddressQrPopupState extends State<AddressQrPopup> {
|
|||
Center(
|
||||
child: RepaintBoundary(
|
||||
key: _qrKey,
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: AddressUtils.buildUriString(
|
||||
widget.coin,
|
||||
widget.addressString,
|
||||
{},
|
||||
),
|
||||
size: 220,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -249,17 +249,15 @@ class _WalletAddressesViewState extends ConsumerState<WalletAddressesView> {
|
|||
walletId: widget.walletId,
|
||||
addressId: snapshot.data![index],
|
||||
coin: coin,
|
||||
onPressed: !isDesktop
|
||||
? null
|
||||
: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
AddressDetailsView.routeName,
|
||||
arguments: Tuple2(
|
||||
snapshot.data![index],
|
||||
widget.walletId,
|
||||
),
|
||||
);
|
||||
},
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
AddressDetailsView.routeName,
|
||||
arguments: Tuple2(
|
||||
snapshot.data![index],
|
||||
widget.walletId,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -20,7 +20,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../../notifications/show_flush_bar.dart';
|
||||
|
@ -39,6 +38,7 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart';
|
|||
import '../../widgets/desktop/primary_button.dart';
|
||||
import '../../widgets/desktop/secondary_button.dart';
|
||||
import '../../widgets/icon_widgets/x_icon.dart';
|
||||
import '../../widgets/qr.dart';
|
||||
import '../../widgets/rounded_white_container.dart';
|
||||
import '../../widgets/stack_dialog.dart';
|
||||
import '../../widgets/stack_text_field.dart';
|
||||
|
@ -215,14 +215,9 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
child: SizedBox(
|
||||
width: width + 20,
|
||||
height: width + 20,
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: uriString,
|
||||
size: width,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -556,15 +551,9 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
child: SizedBox(
|
||||
width: 234,
|
||||
height: 234,
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: _uriString,
|
||||
size: 220,
|
||||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.popupBG,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -16,7 +16,6 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../models/isar/models/isar_models.dart';
|
||||
import '../../notifications/show_flush_bar.dart';
|
||||
|
@ -44,6 +43,7 @@ import '../../widgets/custom_buttons/blue_text_button.dart';
|
|||
import '../../widgets/custom_loading_overlay.dart';
|
||||
import '../../widgets/desktop/primary_button.dart';
|
||||
import '../../widgets/desktop/secondary_button.dart';
|
||||
import '../../widgets/qr.dart';
|
||||
import '../../widgets/rounded_white_container.dart';
|
||||
import 'addresses/wallet_addresses_view.dart';
|
||||
import 'generate_receiving_uri_qr_code_view.dart';
|
||||
|
@ -575,16 +575,13 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
|||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
QrImageView(
|
||||
QR(
|
||||
data: AddressUtils.buildUriString(
|
||||
coin,
|
||||
address,
|
||||
{},
|
||||
),
|
||||
size: MediaQuery.of(context).size.width / 2,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../../../frost_route_generator.dart';
|
||||
import '../../../wallet_view/transaction_views/transaction_details_view.dart';
|
||||
import '../../../../providers/frost_wallet/frost_wallet_providers.dart';
|
||||
import '../../../../providers/global/wallets_provider.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
|
@ -14,7 +13,9 @@ import '../../../../widgets/custom_buttons/checkbox_text_button.dart';
|
|||
import '../../../../widgets/custom_buttons/simple_copy_button.dart';
|
||||
import '../../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../../widgets/detail_item.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
import '../../../wallet_view/transaction_views/transaction_details_view.dart';
|
||||
|
||||
class FrostSendStep1a extends ConsumerStatefulWidget {
|
||||
const FrostSendStep1a({super.key});
|
||||
|
@ -169,14 +170,9 @@ class _FrostSendStep1aState extends ConsumerState<FrostSendStep1a> {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
QrImageView(
|
||||
QR(
|
||||
data: ref.watch(pFrostTxData.state).state!.frostMSConfig!,
|
||||
size: qrImageSize,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -56,6 +56,7 @@ import '../../widgets/animated_text.dart';
|
|||
import '../../widgets/background.dart';
|
||||
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../widgets/custom_buttons/blue_text_button.dart';
|
||||
import '../../widgets/dialogs/firo_exchange_address_dialog.dart';
|
||||
import '../../widgets/fee_slider.dart';
|
||||
import '../../widgets/icon_widgets/addressbook_icon.dart';
|
||||
import '../../widgets/icon_widgets/clipboard_icon.dart';
|
||||
|
@ -127,6 +128,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
|
||||
bool _addressToggleFlag = false;
|
||||
|
||||
bool _isFiroExWarningDisplayed = false;
|
||||
|
||||
bool _cryptoAmountChangeLock = false;
|
||||
late VoidCallback onCryptoAmountChanged;
|
||||
|
||||
|
@ -394,6 +397,19 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
address: address ?? "",
|
||||
isTestNet: wallet.cryptoCurrency.network.isTestNet,
|
||||
);
|
||||
|
||||
ref.read(pIsExchangeAddress.state).state =
|
||||
(coin as Firo).isExchangeAddress(address ?? "");
|
||||
|
||||
if (ref.read(publicPrivateBalanceStateProvider) == FiroType.spark &&
|
||||
ref.read(pIsExchangeAddress) &&
|
||||
!_isFiroExWarningDisplayed) {
|
||||
_isFiroExWarningDisplayed = true;
|
||||
showFiroExchangeAddressWarning(
|
||||
context,
|
||||
() => _isFiroExWarningDisplayed = false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ref.read(pValidSendToAddress.notifier).state =
|
||||
|
@ -875,7 +891,10 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
@override
|
||||
void initState() {
|
||||
coin = widget.coin;
|
||||
ref.refresh(feeSheetSessionCacheProvider);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.refresh(feeSheetSessionCacheProvider);
|
||||
ref.refresh(pIsExchangeAddress);
|
||||
});
|
||||
_currentFee = 0.toAmountAsRaw(fractionDigits: coin.fractionDigits);
|
||||
|
||||
_calculateFeesFuture =
|
||||
|
@ -1003,6 +1022,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
: true);
|
||||
|
||||
if (isFiro) {
|
||||
final isExchangeAddress = ref.watch(pIsExchangeAddress);
|
||||
|
||||
ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
|
||||
selectedUTXOs = {};
|
||||
|
||||
|
@ -1019,6 +1040,19 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (previous != next &&
|
||||
next == FiroType.spark &&
|
||||
isExchangeAddress &&
|
||||
!_isFiroExWarningDisplayed) {
|
||||
_isFiroExWarningDisplayed = true;
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => showFiroExchangeAddressWarning(
|
||||
context,
|
||||
() => _isFiroExWarningDisplayed = false,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
|
@ -30,6 +29,7 @@ import '../../../../widgets/background.dart';
|
|||
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../../../widgets/custom_buttons/simple_copy_button.dart';
|
||||
import '../../../../widgets/detail_item.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
import '../../../../widgets/stack_dialog.dart';
|
||||
import '../../../add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
|
||||
|
@ -317,15 +317,9 @@ class WalletBackupView extends ConsumerWidget {
|
|||
child: SizedBox(
|
||||
width: width + 20,
|
||||
height: width + 20,
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: data,
|
||||
size: width,
|
||||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.popupBG,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -759,6 +759,32 @@ class _WalletNetworkSettingsViewState
|
|||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 12 : 9,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
borderColor: isDesktop
|
||||
? Theme.of(context).extension<StackColors>()!.background
|
||||
: null,
|
||||
padding:
|
||||
isDesktop ? const EdgeInsets.all(16) : const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Current height",
|
||||
textAlign: TextAlign.left,
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
: STextStyles.smallMed12(context),
|
||||
),
|
||||
Text(
|
||||
ref.watch(pWalletChainHeight(widget.walletId)).toString(),
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 32 : 20,
|
||||
),
|
||||
|
|
|
@ -430,7 +430,9 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
|
|||
),
|
||||
if (coin is Firo)
|
||||
FiroCacheCoordinator
|
||||
.clearSharedCache(),
|
||||
.clearSharedCache(
|
||||
coin.network,
|
||||
),
|
||||
],
|
||||
),
|
||||
context: context,
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import '../../../../db/sqlite/firo_cache.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
import '../../../../widgets/background.dart';
|
||||
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../../../widgets/detail_item.dart';
|
||||
|
@ -11,10 +12,13 @@ import '../../../../widgets/detail_item.dart';
|
|||
class SparkInfoView extends ConsumerWidget {
|
||||
const SparkInfoView({
|
||||
super.key,
|
||||
required this.walletId,
|
||||
});
|
||||
|
||||
static const String routeName = "/sparkInfo";
|
||||
|
||||
final String walletId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Background(
|
||||
|
@ -37,7 +41,9 @@ class SparkInfoView extends ConsumerWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
FutureBuilder(
|
||||
future: FiroCacheCoordinator.getSparkCacheSize(),
|
||||
future: FiroCacheCoordinator.getSparkCacheSize(
|
||||
ref.watch(pWalletCoin(walletId)).network,
|
||||
),
|
||||
builder: (_, snapshot) {
|
||||
String detail = "Loading...";
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
|
|
|
@ -243,6 +243,7 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
|
|||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
SparkInfoView.routeName,
|
||||
arguments: walletId,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
|
|
|
@ -14,7 +14,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../providers/global/wallets_provider.dart';
|
||||
|
@ -32,6 +31,7 @@ import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
|||
import '../../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../../widgets/loading_indicator.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
|
||||
class XPubView extends ConsumerStatefulWidget {
|
||||
|
@ -256,11 +256,9 @@ class _XPub extends StatelessWidget {
|
|||
builder: (child) => RoundedWhiteContainer(
|
||||
child: child,
|
||||
),
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: xpub,
|
||||
size: isDesktop ? 280 : MediaQuery.of(context).size.width / 1.5,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
|
|
|
@ -13,6 +13,7 @@ import '../../../../utilities/text_styles.dart';
|
|||
import '../../../../utilities/util.dart';
|
||||
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
import '../../../../widgets/breathing.dart';
|
||||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import '../../../../widgets/trade_card.dart';
|
||||
|
@ -49,98 +50,100 @@ class TxListItem extends ConsumerWidget {
|
|||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TransactionCardV2(
|
||||
key: UniqueKey(),
|
||||
transaction: _tx,
|
||||
),
|
||||
TradeCard(
|
||||
key: Key(
|
||||
_tx.txid +
|
||||
_tx.type.name +
|
||||
_tx.hashCode.toString() +
|
||||
trade.uuid,
|
||||
), //
|
||||
trade: trade,
|
||||
onTap: () async {
|
||||
if (Util.isDesktop) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => Navigator(
|
||||
initialRoute: TradeDetailsView.routeName,
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
onGenerateInitialRoutes: (_, __) {
|
||||
return [
|
||||
FadePageRoute(
|
||||
DesktopDialog(
|
||||
maxHeight: null,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
bottom: 16,
|
||||
child: Breathing(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TransactionCardV2(
|
||||
key: UniqueKey(),
|
||||
transaction: _tx,
|
||||
),
|
||||
TradeCard(
|
||||
key: Key(
|
||||
_tx.txid +
|
||||
_tx.type.name +
|
||||
_tx.hashCode.toString() +
|
||||
trade.uuid,
|
||||
), //
|
||||
trade: trade,
|
||||
onTap: () async {
|
||||
if (Util.isDesktop) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => Navigator(
|
||||
initialRoute: TradeDetailsView.routeName,
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
onGenerateInitialRoutes: (_, __) {
|
||||
return [
|
||||
FadePageRoute(
|
||||
DesktopDialog(
|
||||
maxHeight: null,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Trade details",
|
||||
style: STextStyles.desktopH3(
|
||||
context),
|
||||
),
|
||||
DesktopDialogCloseButton(
|
||||
onPressedOverride: Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pop,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Trade details",
|
||||
style:
|
||||
STextStyles.desktopH3(context),
|
||||
),
|
||||
DesktopDialogCloseButton(
|
||||
onPressedOverride: Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pop,
|
||||
),
|
||||
],
|
||||
Flexible(
|
||||
child: TradeDetailsView(
|
||||
tradeId: trade.tradeId,
|
||||
// TODO: [prio:med]
|
||||
// transactionIfSentFromStack: tx,
|
||||
transactionIfSentFromStack: null,
|
||||
walletName: ref
|
||||
.watch(pWalletName(_tx.walletId)),
|
||||
walletId: _tx.walletId,
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: TradeDetailsView(
|
||||
tradeId: trade.tradeId,
|
||||
// TODO: [prio:med]
|
||||
// transactionIfSentFromStack: tx,
|
||||
transactionIfSentFromStack: null,
|
||||
walletName: ref
|
||||
.watch(pWalletName(_tx.walletId)),
|
||||
walletId: _tx.walletId,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const RouteSettings(
|
||||
name: TradeDetailsView.routeName,
|
||||
),
|
||||
),
|
||||
const RouteSettings(
|
||||
name: TradeDetailsView.routeName,
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
TradeDetailsView.routeName,
|
||||
arguments: Tuple4(
|
||||
trade.tradeId,
|
||||
_tx,
|
||||
_tx.walletId,
|
||||
ref.read(pWalletName(_tx.walletId)),
|
||||
];
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
TradeDetailsView.routeName,
|
||||
arguments: Tuple4(
|
||||
trade.tradeId,
|
||||
_tx,
|
||||
_tx.walletId,
|
||||
ref.read(pWalletName(_tx.walletId)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -149,10 +152,12 @@ class TxListItem extends ConsumerWidget {
|
|||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: TransactionCardV2(
|
||||
// this may mess with combined firo transactions
|
||||
key: UniqueKey(),
|
||||
transaction: _tx,
|
||||
child: Breathing(
|
||||
child: TransactionCardV2(
|
||||
// this may mess with combined firo transactions
|
||||
key: UniqueKey(),
|
||||
transaction: _tx,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -165,9 +170,11 @@ class TxListItem extends ConsumerWidget {
|
|||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: FusionTxGroupCard(
|
||||
key: UniqueKey(),
|
||||
group: group,
|
||||
child: Breathing(
|
||||
child: FusionTxGroupCard(
|
||||
key: UniqueKey(),
|
||||
group: group,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -80,7 +80,6 @@ import '../buy_view/buy_in_wallet_view.dart';
|
|||
import '../cashfusion/cashfusion_view.dart';
|
||||
import '../coin_control/coin_control_view.dart';
|
||||
import '../exchange_view/wallet_initiated_exchange_view.dart';
|
||||
import '../home_view/home_view.dart';
|
||||
import '../monkey/monkey_view.dart';
|
||||
import '../notification_views/notifications_view.dart';
|
||||
import '../ordinals/ordinals_view.dart';
|
||||
|
@ -257,40 +256,43 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
DateTime? _cachedTime;
|
||||
// DateTime? _cachedTime;
|
||||
|
||||
Future<bool> _onWillPop() async {
|
||||
if (_rescanningOnOpen || _lelantusRescanRecovery) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final now = DateTime.now();
|
||||
const timeout = Duration(milliseconds: 1500);
|
||||
if (_cachedTime == null || now.difference(_cachedTime!) > timeout) {
|
||||
_cachedTime = now;
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => WillPopScope(
|
||||
onWillPop: () async {
|
||||
Navigator.of(context).popUntil(
|
||||
ModalRoute.withName(HomeView.routeName),
|
||||
);
|
||||
_logout();
|
||||
return false;
|
||||
},
|
||||
child: const StackDialog(title: "Tap back again to exit wallet"),
|
||||
),
|
||||
).timeout(
|
||||
timeout,
|
||||
onTimeout: () => Navigator.of(context).popUntil(
|
||||
ModalRoute.withName(WalletView.routeName),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
_logout();
|
||||
|
||||
return true;
|
||||
// final now = DateTime.now();
|
||||
// const timeout = Duration(milliseconds: 1500);
|
||||
// if (_cachedTime == null || now.difference(_cachedTime!) > timeout) {
|
||||
// _cachedTime = now;
|
||||
// unawaited(
|
||||
// showDialog<dynamic>(
|
||||
// context: context,
|
||||
// barrierDismissible: false,
|
||||
// builder: (_) => WillPopScope(
|
||||
// onWillPop: () async {
|
||||
// Navigator.of(context).popUntil(
|
||||
// ModalRoute.withName(HomeView.routeName),
|
||||
// );
|
||||
// _logout();
|
||||
// return false;
|
||||
// },
|
||||
// child: const StackDialog(title: "Tap back again to exit wallet"),
|
||||
// ),
|
||||
// ).timeout(
|
||||
// timeout,
|
||||
// onTimeout: () => Navigator.of(context).popUntil(
|
||||
// ModalRoute.withName(WalletView.routeName),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// return false;
|
||||
}
|
||||
|
||||
void _logout() async {
|
||||
|
|
|
@ -13,7 +13,6 @@ import 'dart:async';
|
|||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../../app_config.dart';
|
||||
import '../../../models/exchange/incomplete_exchange.dart';
|
||||
|
@ -37,6 +36,7 @@ import '../../../widgets/desktop/primary_button.dart';
|
|||
import '../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../widgets/desktop/simple_desktop_dialog.dart';
|
||||
import '../../../widgets/fade_stack.dart';
|
||||
import '../../../widgets/qr.dart';
|
||||
import '../subwidgets/desktop_exchange_steps_indicator.dart';
|
||||
import 'subwidgets/desktop_step_1.dart';
|
||||
import 'subwidgets/desktop_step_2.dart';
|
||||
|
@ -397,7 +397,7 @@ class _StepScaffoldState extends ConsumerState<StepScaffold> {
|
|||
height: 48,
|
||||
),
|
||||
Center(
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
// TODO: grab coin uri scheme from somewhere
|
||||
// data: "${coin.uriScheme}:$receivingAddress",
|
||||
data: ref.watch(
|
||||
|
@ -406,9 +406,6 @@ class _StepScaffoldState extends ConsumerState<StepScaffold> {
|
|||
),
|
||||
),
|
||||
size: 290,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'dart:io';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../../pages/wallets_view/wallets_overview.dart';
|
||||
import '../../providers/providers.dart';
|
||||
import '../../themes/coin_icon_provider.dart';
|
||||
|
@ -21,6 +22,7 @@ import '../../utilities/amount/amount.dart';
|
|||
import '../../utilities/text_styles.dart';
|
||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../wallets/isar/providers/all_wallets_info_provider.dart';
|
||||
import '../../widgets/breathing.dart';
|
||||
import '../../widgets/conditional_parent.dart';
|
||||
import '../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||
|
@ -146,71 +148,56 @@ class _DesktopWalletSummaryRowState
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(
|
||||
() => _hovering = true,
|
||||
),
|
||||
onExit: (_) => setState(
|
||||
() => _hovering = false,
|
||||
),
|
||||
child: AnimatedScale(
|
||||
scale: _hovering ? 1.00 : 0.98,
|
||||
duration: const Duration(
|
||||
milliseconds: 200,
|
||||
),
|
||||
child: RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(20),
|
||||
hoverColor: Colors.transparent,
|
||||
onPressed: _onPressed,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.file(
|
||||
File(
|
||||
ref.watch(coinIconProvider(widget.coin)),
|
||||
),
|
||||
width: 28,
|
||||
height: 28,
|
||||
return Breathing(
|
||||
child: RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(20),
|
||||
hoverColor: Colors.transparent,
|
||||
onPressed: _onPressed,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.file(
|
||||
File(
|
||||
ref.watch(coinIconProvider(widget.coin)),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text(
|
||||
widget.coin.prettyName,
|
||||
style:
|
||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Text(
|
||||
widget.walletCount == 1
|
||||
? "${widget.walletCount} wallet"
|
||||
: "${widget.walletCount} wallets",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
width: 28,
|
||||
height: 28,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text(
|
||||
widget.coin.prettyName,
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.textDark,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Text(
|
||||
widget.walletCount == 1
|
||||
? "${widget.walletCount} wallet"
|
||||
: "${widget.walletCount} wallets",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.textSubtitle1,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: TablePriceInfo(
|
||||
coin: widget.coin,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: TablePriceInfo(
|
||||
coin: widget.coin,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -294,7 +294,9 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
|
|||
width: 2,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: FiroCacheCoordinator.getSparkCacheSize(),
|
||||
future: FiroCacheCoordinator.getSparkCacheSize(
|
||||
wallet.cryptoCurrency.network,
|
||||
),
|
||||
builder: (_, snapshot) => Text(
|
||||
snapshot.data ?? "",
|
||||
),
|
||||
|
|
|
@ -16,7 +16,6 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../../models/isar/models/isar_models.dart';
|
||||
|
@ -42,6 +41,7 @@ import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
|||
import '../../../../widgets/custom_loading_overlay.dart';
|
||||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
|
||||
class DesktopReceive extends ConsumerStatefulWidget {
|
||||
|
@ -476,15 +476,13 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
|||
height: 32,
|
||||
),
|
||||
Center(
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: AddressUtils.buildUriString(
|
||||
coin,
|
||||
_qrcodeContent ?? "",
|
||||
{},
|
||||
),
|
||||
size: 200,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -60,6 +60,7 @@ import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
|||
import '../../../../widgets/desktop/desktop_fee_dialog.dart';
|
||||
import '../../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../../widgets/dialogs/firo_exchange_address_dialog.dart';
|
||||
import '../../../../widgets/fee_slider.dart';
|
||||
import '../../../../widgets/icon_widgets/addressbook_icon.dart';
|
||||
import '../../../../widgets/icon_widgets/clipboard_icon.dart';
|
||||
|
@ -121,6 +122,8 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
|
||||
bool _addressToggleFlag = false;
|
||||
|
||||
bool _isFiroExWarningDisplayed = false;
|
||||
|
||||
bool _cryptoAmountChangeLock = false;
|
||||
late VoidCallback onCryptoAmountChanged;
|
||||
|
||||
|
@ -706,6 +709,19 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
address: address ?? "",
|
||||
isTestNet: wallet.cryptoCurrency.network.isTestNet,
|
||||
);
|
||||
|
||||
ref.read(pIsExchangeAddress.state).state =
|
||||
(coin as Firo).isExchangeAddress(address ?? "");
|
||||
|
||||
if (ref.read(publicPrivateBalanceStateProvider) == FiroType.spark &&
|
||||
ref.read(pIsExchangeAddress) &&
|
||||
!_isFiroExWarningDisplayed) {
|
||||
_isFiroExWarningDisplayed = true;
|
||||
showFiroExchangeAddressWarning(
|
||||
context,
|
||||
() => _isFiroExWarningDisplayed = false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ref.read(pValidSendToAddress.notifier).state =
|
||||
|
@ -842,6 +858,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
void initState() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.refresh(feeSheetSessionCacheProvider);
|
||||
ref.refresh(pIsExchangeAddress);
|
||||
ref.read(pValidSendToAddress.state).state = false;
|
||||
ref.read(pValidSparkSendToAddress.state).state = false;
|
||||
});
|
||||
|
@ -944,15 +961,31 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
});
|
||||
}
|
||||
|
||||
final firoType = ref.watch(publicPrivateBalanceStateProvider);
|
||||
|
||||
final isExchangeAddress = ref.watch(pIsExchangeAddress);
|
||||
ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
|
||||
if (previous != next &&
|
||||
next == FiroType.spark &&
|
||||
isExchangeAddress &&
|
||||
!_isFiroExWarningDisplayed) {
|
||||
_isFiroExWarningDisplayed = true;
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => showFiroExchangeAddressWarning(
|
||||
context,
|
||||
() => _isFiroExWarningDisplayed = false,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
final showCoinControl = ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.enableCoinControl,
|
||||
),
|
||||
) &&
|
||||
ref.watch(pWallets).getWallet(walletId) is CoinControlInterface &&
|
||||
(coin is Firo
|
||||
? ref.watch(publicPrivateBalanceStateProvider) == FiroType.public
|
||||
: true);
|
||||
(coin is Firo ? firoType == FiroType.public : true);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
@ -978,7 +1011,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
DropdownButtonHideUnderline(
|
||||
child: DropdownButton2(
|
||||
isExpanded: true,
|
||||
value: ref.watch(publicPrivateBalanceStateProvider.state).state,
|
||||
value: firoType,
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
value: FiroType.spark,
|
||||
|
@ -1464,8 +1497,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
if (_address == null || _address!.isEmpty) {
|
||||
error = null;
|
||||
} else if (coin is Firo) {
|
||||
if (ref.watch(publicPrivateBalanceStateProvider) ==
|
||||
FiroType.lelantus) {
|
||||
if (firoType == FiroType.lelantus) {
|
||||
if (_data != null && _data!.contactLabel == _address) {
|
||||
error = SparkInterface.validateSparkAddress(
|
||||
address: _data!.address,
|
||||
|
@ -1526,15 +1558,13 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
),
|
||||
if (isStellar ||
|
||||
(ref.watch(pValidSparkSendToAddress) &&
|
||||
ref.watch(publicPrivateBalanceStateProvider) !=
|
||||
FiroType.lelantus))
|
||||
firoType != FiroType.lelantus))
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
if (isStellar ||
|
||||
(ref.watch(pValidSparkSendToAddress) &&
|
||||
ref.watch(publicPrivateBalanceStateProvider) !=
|
||||
FiroType.lelantus))
|
||||
firoType != FiroType.lelantus))
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
|
|
|
@ -19,8 +19,7 @@ import '../../../../../providers/global/wallets_provider.dart';
|
|||
import '../../../../../themes/stack_colors.dart';
|
||||
import '../../../../../utilities/assets.dart';
|
||||
import '../../../../../utilities/text_styles.dart';
|
||||
import '../../../../../wallets/crypto_currency/coins/banano.dart';
|
||||
import '../../../../../wallets/crypto_currency/coins/firo.dart';
|
||||
import '../../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../../../../wallets/isar/models/wallet_info.dart';
|
||||
import '../../../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
import '../../../../../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart';
|
||||
|
@ -187,7 +186,9 @@ class _MoreFeaturesDialogState extends ConsumerState<MoreFeaturesDialog> {
|
|||
onPressed: () async => widget.onFusionPressed?.call(),
|
||||
),
|
||||
if (wallet is SparkInterface)
|
||||
const _MoreFeaturesClearSparkCacheItem(),
|
||||
_MoreFeaturesClearSparkCacheItem(
|
||||
cryptoCurrency: wallet.cryptoCurrency,
|
||||
),
|
||||
if (wallet is LelantusInterface)
|
||||
_MoreFeaturesItemBase(
|
||||
child: Row(
|
||||
|
@ -371,10 +372,10 @@ class _MoreFeaturesItemBase extends StatelessWidget {
|
|||
class _MoreFeaturesClearSparkCacheItem extends StatefulWidget {
|
||||
const _MoreFeaturesClearSparkCacheItem({
|
||||
super.key,
|
||||
required this.cryptoCurrency,
|
||||
});
|
||||
|
||||
static const double iconSizeBG = 46;
|
||||
static const double iconSize = 24;
|
||||
final CryptoCurrency cryptoCurrency;
|
||||
|
||||
@override
|
||||
State<_MoreFeaturesClearSparkCacheItem> createState() =>
|
||||
|
@ -396,7 +397,9 @@ class _MoreFeaturesClearSparkCacheItemState
|
|||
}
|
||||
_onPressedLock = true;
|
||||
try {
|
||||
await FiroCacheCoordinator.clearSharedCache();
|
||||
await FiroCacheCoordinator.clearSharedCache(
|
||||
widget.cryptoCurrency.network,
|
||||
);
|
||||
setState(() {
|
||||
// trigger rebuild for cache size display
|
||||
});
|
||||
|
@ -434,7 +437,9 @@ class _MoreFeaturesClearSparkCacheItemState
|
|||
style: STextStyles.w600_20(context),
|
||||
),
|
||||
FutureBuilder(
|
||||
future: FiroCacheCoordinator.getSparkCacheSize(),
|
||||
future: FiroCacheCoordinator.getSparkCacheSize(
|
||||
widget.cryptoCurrency.network,
|
||||
),
|
||||
builder: (_, snapshot) {
|
||||
return Text(
|
||||
snapshot.data ?? "",
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
|
||||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
|
||||
class QRCodeDesktopPopupContent extends StatelessWidget {
|
||||
const QRCodeDesktopPopupContent({
|
||||
|
@ -39,11 +39,9 @@ class QRCodeDesktopPopupContent extends StatelessWidget {
|
|||
const SizedBox(
|
||||
height: 14,
|
||||
),
|
||||
QrImageView(
|
||||
QR(
|
||||
data: value,
|
||||
size: 300,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -9,27 +9,37 @@
|
|||
*/
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../wallet/public_private_balance_state_provider.dart';
|
||||
|
||||
import '../../utilities/amount/amount.dart';
|
||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../wallet/public_private_balance_state_provider.dart';
|
||||
|
||||
final pSendAmount = StateProvider.autoDispose<Amount?>((_) => null);
|
||||
final pValidSendToAddress = StateProvider.autoDispose<bool>((_) => false);
|
||||
final pValidSparkSendToAddress = StateProvider.autoDispose<bool>((_) => false);
|
||||
|
||||
final pIsExchangeAddress = StateProvider<bool>((_) => false);
|
||||
|
||||
final pPreviewTxButtonEnabled =
|
||||
Provider.autoDispose.family<bool, CryptoCurrency>((ref, coin) {
|
||||
final amount = ref.watch(pSendAmount) ?? Amount.zero;
|
||||
|
||||
if (coin is Firo) {
|
||||
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;
|
||||
final firoType = ref.watch(publicPrivateBalanceStateProvider);
|
||||
switch (firoType) {
|
||||
case FiroType.lelantus:
|
||||
return ref.watch(pValidSendToAddress) &&
|
||||
!ref.watch(pValidSparkSendToAddress) &&
|
||||
amount > Amount.zero;
|
||||
|
||||
case FiroType.spark:
|
||||
return (ref.watch(pValidSendToAddress) ||
|
||||
ref.watch(pValidSparkSendToAddress)) &&
|
||||
!ref.watch(pIsExchangeAddress) &&
|
||||
amount > Amount.zero;
|
||||
|
||||
case FiroType.public:
|
||||
return ref.watch(pValidSendToAddress) && amount > Amount.zero;
|
||||
}
|
||||
} else {
|
||||
return ref.watch(pValidSendToAddress) && amount > Amount.zero;
|
||||
|
|
|
@ -1982,13 +1982,18 @@ class RouteGenerator {
|
|||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
case SparkInfoView.routeName:
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => const SparkInfoView(),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
),
|
||||
);
|
||||
if (args is String) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => SparkInfoView(
|
||||
walletId: args,
|
||||
),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
// == Desktop specific routes ============================================
|
||||
case CreatePasswordView.routeName:
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
||||
|
||||
import '../../../models/coinlib/exp2pkh_address.dart';
|
||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||
import '../../../models/node_model.dart';
|
||||
import '../../../utilities/amount/amount.dart';
|
||||
|
@ -77,6 +80,21 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
fractionDigits: fractionDigits,
|
||||
);
|
||||
|
||||
Uint8List get exAddressVersion {
|
||||
switch (network) {
|
||||
case CryptoCurrencyNetwork.main:
|
||||
// https://github.com/firoorg/firo/blob/master/src/chainparams.cpp#L357
|
||||
return Uint8List.fromList([0x01, 0xb9, 0xbb]);
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
// https://github.com/firoorg/firo/blob/master/src/chainparams.cpp#L669
|
||||
return Uint8List.fromList([0x01, 0xb9, 0xb1]);
|
||||
|
||||
default:
|
||||
throw Exception("Unsupported network: $network");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
coinlib.Network get networkParams {
|
||||
switch (network) {
|
||||
|
@ -169,7 +187,11 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinlib.Address.fromString(address, networkParams);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return validateSparkAddress(address);
|
||||
if (validateSparkAddress(address)) {
|
||||
return true;
|
||||
} else {
|
||||
return isExchangeAddress(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,6 +202,18 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
);
|
||||
}
|
||||
|
||||
bool isExchangeAddress(String address) {
|
||||
try {
|
||||
EXP2PKHAddress.fromString(
|
||||
address,
|
||||
exAddressVersion,
|
||||
);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
NodeModel get defaultNode {
|
||||
switch (network) {
|
||||
|
|
|
@ -387,6 +387,7 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
|
|||
parseAnonFees();
|
||||
final tags = await FiroCacheCoordinator.getUsedCoinTagsFor(
|
||||
txid: txData["txid"] as String,
|
||||
network: cryptoCurrency.network,
|
||||
);
|
||||
spentSparkCoins = sparkCoinsInvolvedSpent
|
||||
.where(
|
||||
|
@ -631,9 +632,23 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
|
|||
BigInt.from(jsonUTXO["value"] as int);
|
||||
|
||||
if (blocked) {
|
||||
blockedReason = "Possible masternode output. "
|
||||
try {
|
||||
blocked = await electrumXClient.isMasterNodeCollateral(
|
||||
txid: jsonTX!["txid"] as String,
|
||||
index: jsonUTXO["tx_pos"] as int,
|
||||
);
|
||||
} catch (_) {
|
||||
// call failed, lock utxo just in case
|
||||
// it should logically already be blocked
|
||||
// but just in case
|
||||
blocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (blocked) {
|
||||
blockedReason = "Possible masternode collateral. "
|
||||
"Unlock and spend at your own risk.";
|
||||
label = "Possible masternode";
|
||||
label = "Possible masternode collateral";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -698,12 +713,14 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
|
|||
FiroCacheCoordinator.runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
||||
i,
|
||||
electrumXClient,
|
||||
cryptoCurrency.network,
|
||||
),
|
||||
);
|
||||
}
|
||||
final sparkUsedCoinTagsFuture =
|
||||
FiroCacheCoordinator.runFetchAndUpdateSparkUsedCoinTags(
|
||||
electrumXClient,
|
||||
cryptoCurrency.network,
|
||||
);
|
||||
|
||||
// receiving addresses
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:isar/isar.dart';
|
|||
import '../../../electrumx_rpc/cached_electrumx_client.dart';
|
||||
import '../../../electrumx_rpc/client_manager.dart';
|
||||
import '../../../electrumx_rpc/electrumx_client.dart';
|
||||
import '../../../models/coinlib/exp2pkh_address.dart';
|
||||
import '../../../models/isar/models/blockchain_data/v2/input_v2.dart';
|
||||
import '../../../models/isar/models/blockchain_data/v2/output_v2.dart';
|
||||
import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||
|
@ -24,6 +25,7 @@ import '../../crypto_currency/coins/firo.dart';
|
|||
import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
|
||||
import '../../models/tx_data.dart';
|
||||
import '../impl/bitcoin_wallet.dart';
|
||||
import '../impl/firo_wallet.dart';
|
||||
import '../impl/peercoin_wallet.dart';
|
||||
import '../intermediate/bip39_hd_wallet.dart';
|
||||
import 'cpfp_interface.dart';
|
||||
|
@ -725,11 +727,23 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
|||
|
||||
// Add transaction output
|
||||
for (var i = 0; i < txData.recipients!.length; i++) {
|
||||
final address = coinlib.Address.fromString(
|
||||
normalizeAddress(txData.recipients![i].address),
|
||||
cryptoCurrency.networkParams,
|
||||
);
|
||||
late final coinlib.Address address;
|
||||
|
||||
try {
|
||||
address = coinlib.Address.fromString(
|
||||
normalizeAddress(txData.recipients![i].address),
|
||||
cryptoCurrency.networkParams,
|
||||
);
|
||||
} catch (_) {
|
||||
if (this is FiroWallet) {
|
||||
address = EXP2PKHAddress.fromString(
|
||||
normalizeAddress(txData.recipients![i].address),
|
||||
(cryptoCurrency as Firo).exAddressVersion,
|
||||
);
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
final output = coinlib.Output.fromAddress(
|
||||
txData.recipients![i].amount.raw,
|
||||
address,
|
||||
|
|
|
@ -278,13 +278,17 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
|||
final List<Map<String, dynamic>> setMaps = [];
|
||||
final List<({int groupId, String blockHash})> idAndBlockHashes = [];
|
||||
for (int i = 1; i <= currentId; i++) {
|
||||
final resultSet = await FiroCacheCoordinator.getSetCoinsForGroupId(i);
|
||||
final resultSet = await FiroCacheCoordinator.getSetCoinsForGroupId(
|
||||
i,
|
||||
network: cryptoCurrency.network,
|
||||
);
|
||||
if (resultSet.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
||||
i,
|
||||
cryptoCurrency.network,
|
||||
);
|
||||
if (info == null) {
|
||||
throw Exception("The `info` should never be null here");
|
||||
|
@ -716,13 +720,13 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
|||
return result;
|
||||
} catch (e) {
|
||||
Logging.instance.log(
|
||||
"refreshSparkMempoolData() failed: $e",
|
||||
"_refreshSparkCoinsMempoolCheck() failed: $e",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
return [];
|
||||
} finally {
|
||||
Logging.instance.log(
|
||||
"$walletId ${info.name} refreshSparkCoinsMempoolCheck() run "
|
||||
"$walletId ${info.name} _refreshSparkCoinsMempoolCheck() run "
|
||||
"duration: ${DateTime.now().difference(start)}",
|
||||
level: LogLevel.Debug,
|
||||
);
|
||||
|
@ -741,6 +745,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
|||
final setExists =
|
||||
await FiroCacheCoordinator.checkSetInfoForGroupIdExists(
|
||||
id,
|
||||
cryptoCurrency.network,
|
||||
);
|
||||
if (!setExists) {
|
||||
groupIds.add(id);
|
||||
|
@ -755,6 +760,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
|||
FiroCacheCoordinator.runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
||||
e,
|
||||
electrumXClient,
|
||||
cryptoCurrency.network,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -763,6 +769,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
|||
...possibleFutures,
|
||||
FiroCacheCoordinator.runFetchAndUpdateSparkUsedCoinTags(
|
||||
electrumXClient,
|
||||
cryptoCurrency.network,
|
||||
),
|
||||
]);
|
||||
|
||||
|
@ -782,11 +789,13 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
|||
groupIdTimestampUTCMap[i.toString()] as int? ?? 0;
|
||||
final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
||||
i,
|
||||
cryptoCurrency.network,
|
||||
);
|
||||
final anonymitySetResult =
|
||||
await FiroCacheCoordinator.getSetCoinsForGroupId(
|
||||
i,
|
||||
newerThanTimeStamp: lastCheckedTimeStampUTC,
|
||||
network: cryptoCurrency.network,
|
||||
);
|
||||
final coinsRaw = anonymitySetResult
|
||||
.map(
|
||||
|
@ -882,7 +891,10 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
|||
// only fetch tags from db if we need them to compare against any items
|
||||
// in coinsToCheck
|
||||
if (coinsToCheck.isNotEmpty) {
|
||||
spentCoinTags = await FiroCacheCoordinator.getUsedCoinTags(0);
|
||||
spentCoinTags = await FiroCacheCoordinator.getUsedCoinTags(
|
||||
0,
|
||||
cryptoCurrency.network,
|
||||
);
|
||||
}
|
||||
|
||||
// check and update coins if required
|
||||
|
@ -992,6 +1004,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
|||
|
||||
final pairs = await FiroCacheCoordinator.getUsedCoinTxidsFor(
|
||||
tags: tags,
|
||||
network: cryptoCurrency.network,
|
||||
);
|
||||
|
||||
pairs.removeWhere((e) => usedCoinTxidsFoundLocally.contains(e.txid));
|
||||
|
|
32
lib/widgets/breathing.dart
Normal file
32
lib/widgets/breathing.dart
Normal file
|
@ -0,0 +1,32 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class Breathing extends StatefulWidget {
|
||||
const Breathing({super.key, required this.child});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
State<Breathing> createState() => _BreathingState();
|
||||
}
|
||||
|
||||
class _BreathingState extends State<Breathing> {
|
||||
bool _hovering = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(
|
||||
() => _hovering = true,
|
||||
),
|
||||
onExit: (_) => setState(
|
||||
() => _hovering = false,
|
||||
),
|
||||
child: AnimatedScale(
|
||||
scale: _hovering ? 1.00 : 0.98,
|
||||
duration: const Duration(
|
||||
milliseconds: 200,
|
||||
),
|
||||
child: widget.child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
30
lib/widgets/dialogs/firo_exchange_address_dialog.dart
Normal file
30
lib/widgets/dialogs/firo_exchange_address_dialog.dart
Normal file
|
@ -0,0 +1,30 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../utilities/util.dart';
|
||||
import '../stack_dialog.dart';
|
||||
|
||||
class FiroExchangeAddressDialog extends StatelessWidget {
|
||||
const FiroExchangeAddressDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StackOkDialog(
|
||||
title: "Firo exchange address detected",
|
||||
message: "Sending to an exchange address from a Spark balance is not"
|
||||
" allowed. Please send from your transparent balance.",
|
||||
desktopPopRootNavigator: Util.isDesktop,
|
||||
maxWidth: Util.isDesktop ? 500 : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showFiroExchangeAddressWarning(
|
||||
BuildContext context,
|
||||
VoidCallback onClosed,
|
||||
) async {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => const FiroExchangeAddressDialog(),
|
||||
);
|
||||
onClosed();
|
||||
}
|
|
@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../../../notifications/show_flush_bar.dart';
|
||||
|
@ -17,6 +17,7 @@ import '../../../utilities/text_styles.dart';
|
|||
import '../../../utilities/util.dart';
|
||||
import '../../conditional_parent.dart';
|
||||
import '../../desktop/secondary_button.dart';
|
||||
import '../../qr.dart';
|
||||
import '../../rounded_container.dart';
|
||||
import '../../rounded_white_container.dart';
|
||||
import '../simple_mobile_dialog.dart';
|
||||
|
@ -154,18 +155,9 @@ class _FrostStepQrDialogState extends State<FrostStepQrDialog> {
|
|||
padding: const EdgeInsets.all(16),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: QrImageView(
|
||||
child: QR(
|
||||
data: widget.data,
|
||||
padding: EdgeInsets.zero,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
// dataModuleStyle: QrDataModuleStyle(
|
||||
// dataModuleShape: QrDataModuleShape.square,
|
||||
// color: Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .accentColorDark,
|
||||
// ),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
27
lib/widgets/qr.dart
Normal file
27
lib/widgets/qr.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
/// Centralised Qr code image widget
|
||||
class QR extends StatelessWidget {
|
||||
const QR({super.key, required this.data, this.size, this.padding});
|
||||
|
||||
final String data;
|
||||
final double? size;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return QrImageView(
|
||||
data: data,
|
||||
size: size,
|
||||
padding: padding ?? const EdgeInsets.all(10),
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
// backgroundColor:
|
||||
// Theme.of(context).extension<StackColors>()!.background,
|
||||
// foregroundColor: Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .accentColorDark,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -188,14 +188,19 @@ class StackOkDialog extends StatelessWidget {
|
|||
height: 8,
|
||||
),
|
||||
if (message != null)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
message!,
|
||||
style: STextStyles.smallMed14(context),
|
||||
),
|
||||
],
|
||||
ConstrainedBox(
|
||||
constraints:
|
||||
BoxConstraints(maxWidth: maxWidth ?? double.infinity),
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
message!,
|
||||
style: STextStyles.smallMed14(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
|
|
|
@ -1807,8 +1807,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: f1d02f7ad489df3119a540a7f31485db6d837843
|
||||
resolved-ref: f1d02f7ad489df3119a540a7f31485db6d837843
|
||||
ref: "647cadc3c82c276dc07915b02d24538fd610f220"
|
||||
resolved-ref: "647cadc3c82c276dc07915b02d24538fd610f220"
|
||||
url: "https://github.com/cypherstack/tor.git"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
|
|
Loading…
Reference in a new issue