mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
zec ironwood (#3425)
* initial * wip * ironwood detection, migration * dev: zec address validation on rt * fix: ironwood spend * ironwood regressions * fix: tx history * bump: zkool2 * fix: tx amounts on pending fix: ironwood pending balance fix: orchard->ironwood migration fix: orchard->any pre ironwood txs fix: ironwood -> any txs fix: WrongSpendAuthorizingKey fix: tx history fix: autoshield to ironwood fix: zkool to latest version break: my sleep schedule :sweating: :worksonmymachine: * thx fable * downgrade frb to 2.11.1 * fix: tx amounts in history * Migating... / migration label for O->I txs
This commit is contained in:
@@ -586,11 +586,12 @@ class WalletInfo {
|
||||
"addressPageType": addressPageType,
|
||||
"receiveInfoboxDismissed": receiveInfoboxDismissed ? 1 : 0,
|
||||
"showCombinedBalance": showCombinedBalance ? 1 : 0,
|
||||
"favoriteTokenAddress": favoriteTokenAddress
|
||||
"favoriteTokenAddress": favoriteTokenAddress,
|
||||
"network": network,
|
||||
};
|
||||
|
||||
factory WalletInfo.fromJson(Map<String, dynamic> json) {
|
||||
return WalletInfo(
|
||||
final info = WalletInfo(
|
||||
json[selfIdColumn] as int,
|
||||
json['id'] as String,
|
||||
json['name'] as String,
|
||||
@@ -616,6 +617,8 @@ class WalletInfo {
|
||||
json['receiveInfoboxDismissed'] != 0,
|
||||
json["showCombinedBalance"] != 0,
|
||||
json["favoriteTokenAddress"] as String? ?? null);
|
||||
info.network = json['network'] as String?;
|
||||
return info;
|
||||
}
|
||||
|
||||
Future<int> save() async {
|
||||
|
||||
@@ -274,7 +274,8 @@ class WalletInfo extends HiveObject {
|
||||
addressPageType,
|
||||
false,
|
||||
true,
|
||||
null);
|
||||
null)
|
||||
..network = network;
|
||||
final wiId = await walletInfo.save();
|
||||
for (final address in usedAddresses ?? <String>[]) {
|
||||
await newWi.WalletInfoAddress.insert(wiId, newWi.WalletInfoAddressType.used, address);
|
||||
|
||||
@@ -0,0 +1,429 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: cd6add6f846f35fb79f3c315296703c1a24f3cfd7f4739d91a74961c1c7e9f1b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "100.0.0"
|
||||
analysis_server_plugin:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: analysis_server_plugin
|
||||
sha256: c4021359e7a673ecd9c6db5f11fea12fabb55e0ba96fa08c78920ae81936f1d1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.15"
|
||||
analyzer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "6ba98576948803398b69e3a444df24eacdbe12ed699c7014e120ea38552debbf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.0"
|
||||
analyzer_plugin:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: analyzer_plugin
|
||||
sha256: "9a81dc36f7f71890d7cf83960a3a7df1e6c9a24035f98eb848e91ad48b1d21c6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.14.9"
|
||||
analyzer_testing:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: analyzer_testing
|
||||
sha256: da27ae9bd4bf3ce4ebab6295cf922ca21a2b14ea8fcacc859342aca796a48a2d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.6"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.1"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
cli_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_config
|
||||
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "956a3de0725ca232ad353565a8290d3357592bf4250f6f298a185e2d949c5d3d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.15.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "59d53ef8eaed9d288ed9767618e2b31c4fa0383a127db59d5eb2e737a7638a60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.9"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.20"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_preamble
|
||||
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.2"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.2"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_packages_handler
|
||||
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
shelf_static:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_static
|
||||
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_map_stack_trace
|
||||
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_maps
|
||||
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.13"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.2"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
sha256: "0d5ba5602ec3baa28c8ce365e1efc5575969c765f45c554a3e167dc7945b9c30"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.31.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "475610b2aa23c19687cce2961e44b0cc57cafe220f67c2b80201231b2a07fbe7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.13"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: a39c204a4fc7a7ccb04a2b985e359fda3cc37e45e0b8ac61c3fb1a05aa832132
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.19"
|
||||
test_reflective_loader:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test_reflective_loader
|
||||
sha256: d828d5ca15179aaac2aaf8f510cf0a52ec28e0031681b044ec5e581a4b8002e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.2.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
yaml_edit:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml_edit
|
||||
sha256: "07c9e63ba42519745182b88ca12264a7ba2484d8239958778dfe4d44fe760488"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.4"
|
||||
sdks:
|
||||
dart: ">=3.11.0 <4.0.0"
|
||||
@@ -11,4 +11,6 @@ export 'src/zcash_wallet_service.dart';
|
||||
export 'src/models.dart';
|
||||
export 'src/zcash_transaction_credentials.dart';
|
||||
export 'src/pending_zcash_transaction.dart';
|
||||
export 'src/zcash_network.dart';
|
||||
export 'src/zcash_receive_page_options.dart';
|
||||
export 'src/zcash_wallet_addresses.dart';
|
||||
|
||||
@@ -7,6 +7,7 @@ class ZcashNewWalletCredentials extends WalletCredentials {
|
||||
required final String? passphrase,
|
||||
final String? mnemonic,
|
||||
final int? seedPhraseLength,
|
||||
this.network = 0,
|
||||
}) : super(
|
||||
name: name,
|
||||
password: password,
|
||||
@@ -17,6 +18,7 @@ class ZcashNewWalletCredentials extends WalletCredentials {
|
||||
}
|
||||
|
||||
String? mnemonic;
|
||||
int network;
|
||||
}
|
||||
|
||||
class ZcashFromSeedWalletCredentials extends WalletCredentials {
|
||||
@@ -26,8 +28,10 @@ class ZcashFromSeedWalletCredentials extends WalletCredentials {
|
||||
required final String? passphrase,
|
||||
required this.seed,
|
||||
required super.height,
|
||||
this.network = 0,
|
||||
}) : super(name: name, password: password, passphrase: passphrase);
|
||||
final String? seed;
|
||||
int network;
|
||||
}
|
||||
|
||||
class ZcashFromKeysWalletCredentials extends WalletCredentials {
|
||||
@@ -36,6 +40,8 @@ class ZcashFromKeysWalletCredentials extends WalletCredentials {
|
||||
final String? password,
|
||||
required final int? height,
|
||||
required this.privateKey,
|
||||
this.network = 0,
|
||||
}) : super(name: name, password: password, height: height);
|
||||
final String? privateKey;
|
||||
int network;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ class PendingZcashTransaction with PendingTransaction {
|
||||
throw TransactionCommitFailed(errorMessage: result);
|
||||
}
|
||||
_txId = txId;
|
||||
zcashWallet.rememberPendingOutgoingAmount(txId, amount);
|
||||
},
|
||||
);
|
||||
await zcashWallet.updateTransactions();
|
||||
|
||||
@@ -21,8 +21,19 @@ class ZcashMempoolService {
|
||||
|
||||
bool _loopRunning = false;
|
||||
String? _nodeUrl;
|
||||
int? _lastBlockHeight;
|
||||
StreamSubscription<zkool_mempool.MempoolMsg>? _subscription;
|
||||
|
||||
void removeTx(final String txId) {
|
||||
_txsById.remove(ZcashWalletService.normalizeTxId(txId));
|
||||
}
|
||||
|
||||
void removeKnownTxs(final Iterable<String> txIds) {
|
||||
for (final txId in txIds) {
|
||||
removeTx(txId);
|
||||
}
|
||||
}
|
||||
|
||||
List<zkool_mempool.MempoolTx> txsForAccount(final int accountId) => _txsById.values
|
||||
.where(
|
||||
(final tx) =>
|
||||
@@ -49,6 +60,7 @@ class ZcashMempoolService {
|
||||
await _subscription?.cancel();
|
||||
_subscription = null;
|
||||
_txsById.clear();
|
||||
_lastBlockHeight = null;
|
||||
try {
|
||||
await _mempool?.cancel();
|
||||
} catch (e) {
|
||||
@@ -69,8 +81,8 @@ class ZcashMempoolService {
|
||||
return;
|
||||
}
|
||||
switch (msg) {
|
||||
case zkool_mempool.MempoolMsg_BlockHeight():
|
||||
_onBlockMined();
|
||||
case zkool_mempool.MempoolMsg_BlockHeight(:final field0):
|
||||
_onBlockHeight(field0);
|
||||
case zkool_mempool.MempoolMsg_TxId(:final field0):
|
||||
_onMempoolTx(field0);
|
||||
}
|
||||
@@ -97,7 +109,12 @@ class ZcashMempoolService {
|
||||
}
|
||||
}
|
||||
|
||||
void _onBlockMined() {
|
||||
void _onBlockHeight(final int height) {
|
||||
final heightChanged = _lastBlockHeight != null && height > _lastBlockHeight!;
|
||||
_lastBlockHeight = height;
|
||||
if (!heightChanged) {
|
||||
return;
|
||||
}
|
||||
final affected = _allAffectedAccounts();
|
||||
_txsById.clear();
|
||||
onAccountsUpdated?.call(affected);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
enum ZcashNetwork {
|
||||
mainnet,
|
||||
testnet,
|
||||
regtest;
|
||||
|
||||
/// NU6.3 / Ironwood activation height on zkool regtest (see zkool2/misc/zebra.toml).
|
||||
static const int regtestNu63Height = 250;
|
||||
|
||||
String get value => name;
|
||||
|
||||
String get label => switch (this) {
|
||||
ZcashNetwork.mainnet => 'Mainnet',
|
||||
ZcashNetwork.testnet => 'Testnet',
|
||||
ZcashNetwork.regtest => 'Regtest',
|
||||
};
|
||||
|
||||
String get dbFileName => switch (this) {
|
||||
ZcashNetwork.mainnet => 'zec.v2.db',
|
||||
ZcashNetwork.testnet => 'zec.testnet.v2.db',
|
||||
ZcashNetwork.regtest => 'zec.regtest.v2.db',
|
||||
};
|
||||
|
||||
/// Default lightwalletd endpoint for dev networks.
|
||||
String get defaultNodeUri => switch (this) {
|
||||
ZcashNetwork.mainnet => 'zec.rocks:443',
|
||||
ZcashNetwork.testnet => 'testnet.lightwalletd.com:9067',
|
||||
ZcashNetwork.regtest => '10.0.2.2:9067', // 10.0.2.2 - AVD host bridge
|
||||
};
|
||||
|
||||
bool get useSsl => this == ZcashNetwork.mainnet;
|
||||
|
||||
static ZcashNetwork fromName(final String? name) => switch (name) {
|
||||
'testnet' => ZcashNetwork.testnet,
|
||||
'regtest' => ZcashNetwork.regtest,
|
||||
_ => ZcashNetwork.mainnet,
|
||||
};
|
||||
|
||||
static ZcashNetwork fromIndex(final int index) => switch (index) {
|
||||
1 => ZcashNetwork.testnet,
|
||||
2 => ZcashNetwork.regtest,
|
||||
_ => ZcashNetwork.mainnet,
|
||||
};
|
||||
|
||||
int get networkIndex => switch (this) {
|
||||
ZcashNetwork.testnet => 1,
|
||||
ZcashNetwork.regtest => 2,
|
||||
ZcashNetwork.mainnet => 0,
|
||||
};
|
||||
}
|
||||
@@ -19,7 +19,7 @@ class ZcashReceivePageOption implements ReceivePageOption {
|
||||
case ZcashAddressType.shieldedSapling:
|
||||
return shieldedSapling;
|
||||
case ZcashAddressType.shieldedOrchard:
|
||||
return shieldedOrchard;
|
||||
return shieldedOrchard();
|
||||
case ZcashAddressType.unifiedType:
|
||||
return unified;
|
||||
}
|
||||
@@ -57,13 +57,18 @@ class ZcashReceivePageOption implements ReceivePageOption {
|
||||
description: "Sapling",
|
||||
iconPath: "assets/new-ui/address-type-picker-icons/zec/sapling.svg",
|
||||
);
|
||||
static const shieldedOrchard = ZcashReceivePageOption._(
|
||||
ZcashAddressType.shieldedOrchard,
|
||||
"Shielded",
|
||||
description: "Default (Orchard)",
|
||||
iconPath: "assets/new-ui/address-type-picker-icons/zec/shielded.svg",
|
||||
isCommon: true,
|
||||
);
|
||||
static const _shieldedOrchardIcon =
|
||||
"assets/new-ui/address-type-picker-icons/zec/shielded.svg";
|
||||
|
||||
static ZcashReceivePageOption shieldedOrchard({final bool ironwood = false}) {
|
||||
return ZcashReceivePageOption._(
|
||||
ZcashAddressType.shieldedOrchard,
|
||||
"Shielded",
|
||||
description: ironwood ? "Default (Ironwood)" : "Default (Orchard)",
|
||||
iconPath: _shieldedOrchardIcon,
|
||||
isCommon: true,
|
||||
);
|
||||
}
|
||||
static const unified = ZcashReceivePageOption._(
|
||||
ZcashAddressType.unifiedType,
|
||||
"Unified",
|
||||
@@ -78,13 +83,15 @@ class ZcashReceivePageOption implements ReceivePageOption {
|
||||
return value;
|
||||
}
|
||||
|
||||
static const all = [
|
||||
ZcashReceivePageOption.shieldedOrchard,
|
||||
ZcashReceivePageOption.shieldedSapling,
|
||||
ZcashReceivePageOption.unified,
|
||||
ZcashReceivePageOption.transparentRotated,
|
||||
ZcashReceivePageOption.transparent,
|
||||
];
|
||||
static List<ZcashReceivePageOption> allOptionsFor({final bool ironwood = false}) => [
|
||||
shieldedOrchard(ironwood: ironwood),
|
||||
shieldedSapling,
|
||||
unified,
|
||||
transparentRotated,
|
||||
transparent,
|
||||
];
|
||||
|
||||
static List<ZcashReceivePageOption> get allOptions => allOptionsFor();
|
||||
|
||||
ZcashAddressType toType() {
|
||||
return type;
|
||||
|
||||
@@ -34,6 +34,7 @@ class ZcashTaddressRotation {
|
||||
static bool _isStarted = false;
|
||||
static zkool_coin.Coin get c => ZcashWalletBase.c;
|
||||
static const int _sweepThreshold = 30000;
|
||||
static const int _minSpendableNote = 5000;
|
||||
static const int _lookahead = 5;
|
||||
// Matches transparentLimit in ZcashWalletBase._oneshotSync.
|
||||
static const int _transparentSyncLimit = 100;
|
||||
@@ -407,18 +408,42 @@ class ZcashTaddressRotation {
|
||||
final result = await ZcashWalletBase.runWithCoin(
|
||||
accountId: rotationAccount,
|
||||
func: (final coin) async {
|
||||
final height = await zkool_network.getCurrentHeight(c: coin);
|
||||
final notes = await zkool_account.listNotes(c: coin);
|
||||
var amount = BigInt.zero;
|
||||
for (final note in notes) {
|
||||
if (note.pool != NotePool.transparent.index || note.locked) {
|
||||
continue;
|
||||
}
|
||||
if (note.value < BigInt.from(_minSpendableNote)) {
|
||||
continue;
|
||||
}
|
||||
if (note.height > height) {
|
||||
continue;
|
||||
}
|
||||
amount += note.value;
|
||||
}
|
||||
if (amount < BigInt.from(_sweepThreshold)) {
|
||||
throw Exception('rotation sweep: insufficient spendable transparent notes');
|
||||
}
|
||||
final ironwood = await zkool_network.isIronwoodActive(c: coin);
|
||||
final tx = await zkool_pay.prepare(
|
||||
recipients: [
|
||||
zkool_paydart.Recipient(assetBase: zecBase, address: toAddress, amount: transparentBal),
|
||||
zkool_paydart.Recipient(
|
||||
assetBase: zecBase,
|
||||
address: toAddress,
|
||||
amount: amount,
|
||||
pools: ironwood ? ironwoodPoolMask : null,
|
||||
),
|
||||
],
|
||||
options: zkool_pay.PaymentOptions(
|
||||
srcPools: 1,
|
||||
recipientPaysFee: true,
|
||||
smartTransparent: false,
|
||||
mode: 0,
|
||||
),
|
||||
c: coin,
|
||||
);
|
||||
final height = await zkool_network.getCurrentHeight(c: coin);
|
||||
final signTx = await zkool_pay.signTransaction(pczt: tx, c: coin);
|
||||
final txBytes = await zkool_pay.extractTransaction(package: signTx);
|
||||
return zkool_pay.broadcastTransaction(height: height, txBytes: txBytes, c: coin);
|
||||
|
||||
@@ -20,6 +20,7 @@ class ZcashTransactionInfo extends TransactionInfo {
|
||||
final TxType? txType,
|
||||
final bool isRotationReceive = false,
|
||||
final bool isShieldAction = false,
|
||||
final bool isIronwoodMigration = false,
|
||||
}) {
|
||||
this.id = id;
|
||||
this.amount = amount;
|
||||
@@ -38,6 +39,7 @@ class ZcashTransactionInfo extends TransactionInfo {
|
||||
}
|
||||
additionalInfo['isRotationReceive'] = isRotationReceive;
|
||||
additionalInfo['isAutoShield'] = isShieldAction || ZcashWalletService.isAutoshieldTx(txHash);
|
||||
additionalInfo['isIronwoodMigration'] = isIronwoodMigration;
|
||||
|
||||
if (additionalInfo['isAutoShield'] == true) {
|
||||
additionalInfo['memo'] ??= '';
|
||||
|
||||
@@ -23,6 +23,7 @@ import 'package:cw_zcash/src/util/crc32.dart';
|
||||
import 'package:cw_zcash/src/zcash_mempool.dart';
|
||||
import 'package:cw_zcash/src/zcash_taddress_rotation.dart';
|
||||
import 'package:cw_zcash/src/zcash_wallet_addresses.dart';
|
||||
import 'package:cw_zcash/src/zcash_network.dart';
|
||||
import 'package:cw_zcash/src/zkool_compat.dart';
|
||||
import 'package:cw_zcash/src/zkooltx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
@@ -32,6 +33,7 @@ import 'package:zkool/src/rust/api/coin.dart' as zkool_coin;
|
||||
import 'package:zkool/src/rust/api/mempool.dart' as zkool_mempool;
|
||||
import 'package:zkool/src/rust/api/sync.dart' as zkool_sync;
|
||||
import 'package:zkool/src/rust/api/pay.dart' as zkool_pay;
|
||||
import 'package:zkool/src/rust/api/migrate.dart' as zkool_migrate;
|
||||
import 'package:zkool/src/rust/api/network.dart' as zkool_network;
|
||||
import 'package:zkool/src/rust/pay.dart' as zkool_paydart;
|
||||
import 'package:zkool/src/rust/frb_generated.dart' as zkool_frb;
|
||||
@@ -61,6 +63,15 @@ abstract class ZcashWalletBase
|
||||
|
||||
int accountId;
|
||||
|
||||
final Map<String, BigInt> _pendingOutgoingAmounts = {};
|
||||
|
||||
void rememberPendingOutgoingAmount(final String txId, final Money amount) {
|
||||
if (amount.isZero) {
|
||||
return;
|
||||
}
|
||||
_pendingOutgoingAmounts[ZcashWalletService.normalizeTxId(txId)] = amount.amount;
|
||||
}
|
||||
|
||||
@override
|
||||
@observable
|
||||
SyncStatus syncStatus = NotConnectedSyncStatus();
|
||||
@@ -147,6 +158,7 @@ abstract class ZcashWalletBase
|
||||
c = c.setLwd(url: lwdUrl, serverType: 0);
|
||||
syncStatus = ConnectedSyncStatus();
|
||||
unawaited(ZcashMempoolService.instance.ensureRunning(c));
|
||||
unawaited(_updateIronwoodActive());
|
||||
_ensureSyncLoopRunning();
|
||||
unawaited(_refreshSyncStatus());
|
||||
unawaited(_oneshotSync());
|
||||
@@ -463,12 +475,14 @@ abstract class ZcashWalletBase
|
||||
return await runWithCoin(
|
||||
accountId: accountId,
|
||||
func: (coin) async {
|
||||
final ironwood = await zkool_network.isIronwoodActive(c: coin);
|
||||
final txPlan = await zkool_pay.prepare(
|
||||
recipients: recipients,
|
||||
options: zkool_pay.PaymentOptions(
|
||||
srcPools: 7,
|
||||
srcPools: ironwood ? 15 : 7,
|
||||
recipientPaysFee: receipientPaysFee,
|
||||
smartTransparent: false,
|
||||
mode: 0,
|
||||
),
|
||||
c: coin,
|
||||
);
|
||||
@@ -502,18 +516,57 @@ abstract class ZcashWalletBase
|
||||
|
||||
static const _dispPhrase = "Received to disposable address";
|
||||
|
||||
bool _hasExternalOutputs(final ZkoolTx tx) =>
|
||||
tx.outputsWithAddress.any((final o) => !_addressBelongsToWallet(o.address));
|
||||
|
||||
bool _isIronwoodMigrationTx(final ZkoolTx tx) {
|
||||
// zkool classifies migration as selfTransfer with fee-sized net value.
|
||||
if (tx.type != TxType.selfTransfer) {
|
||||
return false;
|
||||
}
|
||||
final orchardSpent = tx.orchardSpent;
|
||||
final spentFromOrchard = orchardSpent > BigInt.zero ||
|
||||
tx.spendPools.contains(NotePool.orchard.index);
|
||||
if (!spentFromOrchard) {
|
||||
return false;
|
||||
}
|
||||
if (tx.ironwoodReceived > BigInt.zero ||
|
||||
tx.notePools.contains(NotePool.ironwood.index)) {
|
||||
return true;
|
||||
}
|
||||
// Orchard → Orchard split step (SD notes created, all outputs are ours).
|
||||
if (tx.orchardReceived > BigInt.zero && !_hasExternalOutputs(tx)) {
|
||||
return true;
|
||||
}
|
||||
// Orchard → Ironwood before the IW note is attached to tx details.
|
||||
return orchardSpent > tx.value;
|
||||
}
|
||||
|
||||
BigInt _migrationDisplayAmount(final ZkoolTx tx) {
|
||||
if (tx.ironwoodReceived > BigInt.zero) {
|
||||
return tx.ironwoodReceived;
|
||||
}
|
||||
if (tx.orchardReceived > BigInt.zero) {
|
||||
return tx.orchardReceived;
|
||||
}
|
||||
return tx.orchardSpent;
|
||||
}
|
||||
|
||||
ZcashTransactionInfo _zcashInfoFromMempoolTx(
|
||||
final zkool_mempool.MempoolTx tx,
|
||||
final int accountId,
|
||||
) {
|
||||
final accountNotes = tx.notes.where((final n) => n.account == accountId);
|
||||
final txHash = ZcashWalletService.normalizeTxId(tx.txid);
|
||||
final pendingAmount = _pendingOutgoingAmounts[txHash];
|
||||
final netValue = accountNotes.fold<BigInt>(
|
||||
BigInt.zero,
|
||||
(final sum, final note) => sum + BigInt.from(note.value),
|
||||
);
|
||||
final direction = netValue >= BigInt.zero
|
||||
? TransactionDirection.incoming
|
||||
: TransactionDirection.outgoing;
|
||||
final direction = pendingAmount != null || netValue < BigInt.zero
|
||||
? TransactionDirection.outgoing
|
||||
: TransactionDirection.incoming;
|
||||
final displayAmount = pendingAmount ?? netValue.abs();
|
||||
final memo = accountNotes
|
||||
.map((final n) => n.memo)
|
||||
.whereType<String>()
|
||||
@@ -525,8 +578,8 @@ abstract class ZcashWalletBase
|
||||
);
|
||||
|
||||
final info = ZcashTransactionInfo(
|
||||
id: ZcashWalletService.normalizeTxId(tx.txid),
|
||||
amount: Money(netValue.abs(), currency),
|
||||
id: txHash,
|
||||
amount: Money(displayAmount, currency),
|
||||
fee: Money.zero(currency),
|
||||
direction: direction,
|
||||
isPending: true,
|
||||
@@ -555,11 +608,15 @@ abstract class ZcashWalletBase
|
||||
? currentHeight - tx.height + 1
|
||||
: 0;
|
||||
final memo = extraMemo != null ? "${tx.memo ?? ''}\n$extraMemo".trim() : tx.memo;
|
||||
final direction = directionOverride ?? tx.direction;
|
||||
final isMigration = directionOverride == null && _isIronwoodMigrationTx(tx);
|
||||
final direction = directionOverride ??
|
||||
(isMigration ? TransactionDirection.outgoing : tx.direction);
|
||||
final recipientAddresses = _recipientAddresses(_paymentOutputAddresses(tx), direction);
|
||||
final amount = amountOverride ??
|
||||
(isMigration ? _migrationDisplayAmount(tx) : tx.value);
|
||||
final info = ZcashTransactionInfo(
|
||||
id: tx.txHash,
|
||||
amount: Money(amountOverride ?? tx.value, currency),
|
||||
amount: Money(amount, currency),
|
||||
fee: Money.zero(currency),
|
||||
direction: direction,
|
||||
isPending: tx.height == 0,
|
||||
@@ -571,6 +628,7 @@ abstract class ZcashWalletBase
|
||||
txType: tx.type,
|
||||
isRotationReceive: isRotationReceive,
|
||||
isShieldAction: isShieldAction,
|
||||
isIronwoodMigration: isMigration,
|
||||
);
|
||||
if (recipientAddresses.isNotEmpty) {
|
||||
info.outputAddresses = recipientAddresses;
|
||||
@@ -627,6 +685,9 @@ abstract class ZcashWalletBase
|
||||
'tx_$txHash$suffix';
|
||||
|
||||
static int _txDisplayPriority(final ZcashTransactionInfo info) {
|
||||
if (info.additionalInfo['isIronwoodMigration'] == true) {
|
||||
return 4;
|
||||
}
|
||||
if (info.additionalInfo['isAutoShield'] == true) {
|
||||
return 3;
|
||||
}
|
||||
@@ -650,7 +711,8 @@ abstract class ZcashWalletBase
|
||||
return;
|
||||
}
|
||||
if (infoPriority == existingPriority &&
|
||||
info.additionalInfo['isAutoShield'] == true &&
|
||||
(info.additionalInfo['isAutoShield'] == true ||
|
||||
info.additionalInfo['isIronwoodMigration'] == true) &&
|
||||
info.direction == TransactionDirection.outgoing &&
|
||||
existing.direction == TransactionDirection.incoming) {
|
||||
byHash[hash] = info;
|
||||
@@ -813,6 +875,10 @@ abstract class ZcashWalletBase
|
||||
|
||||
final Map<String, ZcashTransactionInfo> splitEntries = {};
|
||||
for (final tx in txs) {
|
||||
_pendingOutgoingAmounts.remove(ZcashWalletService.normalizeTxId(tx.txHash));
|
||||
if (tx.height > 0) {
|
||||
ZcashMempoolService.instance.removeTx(tx.txHash);
|
||||
}
|
||||
final isShield = _isShieldActionTx(tx, rotationSweepHashes: rotationSweepHashes);
|
||||
if (_shouldSplitAutoshieldTx(tx, isShield: isShield)) {
|
||||
byHash.remove(tx.txHash);
|
||||
@@ -834,10 +900,15 @@ abstract class ZcashWalletBase
|
||||
_offerTx(byHash, _zcashInfoFromZkoolTx(tx, currentHeight, isShieldAction: isShield));
|
||||
}
|
||||
|
||||
final knownHashes = {for (final tx in txs) tx.txHash, ...byHash.keys};
|
||||
final knownHashes = {
|
||||
for (final tx in txs) ZcashWalletService.normalizeTxId(tx.txHash),
|
||||
for (final hash in byHash.keys) ZcashWalletService.normalizeTxId(hash),
|
||||
};
|
||||
for (final mempoolTx in ZcashMempoolService.instance.txsForAccount(accountId)) {
|
||||
final hash = ZcashWalletService.normalizeTxId(mempoolTx.txid);
|
||||
if (knownHashes.contains(hash)) {
|
||||
ZcashMempoolService.instance.removeTx(hash);
|
||||
_pendingOutgoingAmounts.remove(hash);
|
||||
continue;
|
||||
}
|
||||
final info = _zcashInfoFromMempoolTx(mempoolTx, accountId);
|
||||
@@ -998,31 +1069,36 @@ abstract class ZcashWalletBase
|
||||
}
|
||||
|
||||
bool _isTransactionUpdating = false;
|
||||
bool _transactionUpdateQueued = false;
|
||||
|
||||
Future<void> updateTransactions() async {
|
||||
if (_isTransactionUpdating) {
|
||||
_transactionUpdateQueued = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_isTransactionUpdating = true;
|
||||
try {
|
||||
if (_isTransactionUpdating) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
_transactionUpdateQueued = false;
|
||||
final transactions = await fetchTransactions();
|
||||
|
||||
_isTransactionUpdating = true;
|
||||
final transactions = await fetchTransactions();
|
||||
final currentIds = transactionHistory.transactions.keys.toSet();
|
||||
final newIds = transactions.keys.toSet();
|
||||
|
||||
final currentIds = transactionHistory.transactions.keys.toSet();
|
||||
final newIds = transactions.keys.toSet();
|
||||
currentIds
|
||||
.difference(newIds)
|
||||
.forEach((final id) => transactionHistory.transactions.remove(id));
|
||||
|
||||
currentIds
|
||||
.difference(newIds)
|
||||
.forEach((final id) => transactionHistory.transactions.remove(id));
|
||||
|
||||
transactions.forEach((final key, final tx) {
|
||||
transactionHistory.transactions[key] = tx;
|
||||
});
|
||||
await transactionHistory.save();
|
||||
_isTransactionUpdating = false;
|
||||
transactions.forEach((final key, final tx) {
|
||||
transactionHistory.transactions[key] = tx;
|
||||
});
|
||||
await transactionHistory.save();
|
||||
} while (_transactionUpdateQueued);
|
||||
} catch (e, stackTrace) {
|
||||
printV("Update transactions error: $e");
|
||||
printV("Stack trace: $stackTrace");
|
||||
} finally {
|
||||
_isTransactionUpdating = false;
|
||||
}
|
||||
}
|
||||
@@ -1081,6 +1157,8 @@ abstract class ZcashWalletBase
|
||||
|
||||
static final autoShieldMutex = Mutex();
|
||||
static DateTime? _lastAutoShieldAt;
|
||||
static final ironwoodMigrateMutex = Mutex();
|
||||
static DateTime? _lastIronwoodMigrateAt;
|
||||
Future<void> _autoShield() async {
|
||||
if (_lastAutoShieldAt != null &&
|
||||
_lastAutoShieldAt!.isAfter(DateTime.now().subtract(const Duration(seconds: 75)))) {
|
||||
@@ -1105,35 +1183,37 @@ abstract class ZcashWalletBase
|
||||
accountId: accountId,
|
||||
func: (coin) async {
|
||||
final _notes = await zkool_account.listNotes(c: coin);
|
||||
final List<zkool_account.TxNote> txNotes = [];
|
||||
BigInt sweepable = BigInt.zero;
|
||||
for (int i = 0; i < _notes.length; i++) {
|
||||
final note = _notes[i];
|
||||
if (note.pool < 0 || note.pool >= NotePool.values.length) {
|
||||
continue;
|
||||
}
|
||||
final noteType = NotePool.values[note.pool];
|
||||
if ([NotePool.sapling, NotePool.transparent].contains(noteType)) {
|
||||
txNotes.add(note);
|
||||
if (noteType == NotePool.transparent || noteType == NotePool.sapling) {
|
||||
sweepable += note.value;
|
||||
}
|
||||
}
|
||||
|
||||
final sweepable = txNotes.isEmpty
|
||||
? BigInt.from(0)
|
||||
: txNotes.map((final txn) => txn.value).reduce((final a, final b) => a + b);
|
||||
|
||||
if (sweepable <= BigInt.from(_autoShieldMinSweep)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ironwood = await zkool_network.isIronwoodActive(c: coin);
|
||||
final txPlan = await zkool_pay.prepare(
|
||||
recipients: [
|
||||
zkool_paydart.Recipient(
|
||||
assetBase: zecBase,
|
||||
address: walletAddresses.orchardAddress!,
|
||||
amount: sweepable,
|
||||
pools: ironwood ? ironwoodPoolMask : null,
|
||||
),
|
||||
],
|
||||
options: zkool_pay.PaymentOptions(
|
||||
srcPools: 3,
|
||||
recipientPaysFee: true,
|
||||
smartTransparent: false,
|
||||
mode: 0,
|
||||
),
|
||||
c: coin,
|
||||
);
|
||||
@@ -1156,34 +1236,134 @@ abstract class ZcashWalletBase
|
||||
_lastAutoShieldAt = DateTime.now();
|
||||
printV("shielded: $txId");
|
||||
await updateTransactions();
|
||||
await _refreshBalance(runAutoShield: false);
|
||||
await _refreshBalance(runAutoShield: false, runIronwoodMigrate: false);
|
||||
}
|
||||
|
||||
Future<void> _refreshBalance({required final bool runAutoShield}) async {
|
||||
Future<void> _ironwoodMigrate() async {
|
||||
if (_lastIronwoodMigrateAt != null &&
|
||||
_lastIronwoodMigrateAt!.isAfter(DateTime.now().subtract(const Duration(seconds: 75)))) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await ironwoodMigrateMutex.acquire();
|
||||
await _$ironwoodMigrate();
|
||||
} catch (e, s) {
|
||||
printV("ironwood migration failed: $e");
|
||||
s.toString().split("\n").forEach(printV);
|
||||
} finally {
|
||||
ironwoodMigrateMutex.release();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _$ironwoodMigrate() async {
|
||||
if (syncStatus is! SyncedSyncStatus) {
|
||||
return;
|
||||
}
|
||||
final event = await runWithCoin(
|
||||
accountId: accountId,
|
||||
func: (coin) async {
|
||||
if (!await zkool_network.isIronwoodActive(c: coin)) {
|
||||
return null;
|
||||
}
|
||||
final bal = await zkool_sync.balance(c: coin);
|
||||
if (bal.field0.length <= 2 || bal.field0[2] <= BigInt.zero) {
|
||||
return null;
|
||||
}
|
||||
return zkool_migrate.stepMigration(c: coin);
|
||||
},
|
||||
);
|
||||
if (event == null) {
|
||||
return;
|
||||
}
|
||||
switch (event) {
|
||||
case zkool_migrate.MigrationEvent_Complete():
|
||||
case zkool_migrate.MigrationEvent_NothingToDo():
|
||||
return;
|
||||
case zkool_migrate.MigrationEvent_SplitComplete(:final fee):
|
||||
printV("ironwood split step complete, fee: $fee");
|
||||
case zkool_migrate.MigrationEvent_MigrateComplete(:final fee):
|
||||
printV("ironwood migrate step complete, fee: $fee");
|
||||
case zkool_migrate.MigrationEvent_Error(:final message):
|
||||
printV("ironwood migration error: $message");
|
||||
return;
|
||||
}
|
||||
|
||||
_lastIronwoodMigrateAt = DateTime.now();
|
||||
await updateTransactions();
|
||||
await _refreshBalance(runAutoShield: false, runIronwoodMigrate: false);
|
||||
}
|
||||
|
||||
Future<void> _updateIronwoodActive() async {
|
||||
bool? active;
|
||||
try {
|
||||
active = await runWithCoin(
|
||||
accountId: accountId,
|
||||
func: (final coin) => zkool_network.isIronwoodActive(c: coin),
|
||||
);
|
||||
} catch (e) {
|
||||
printV("isIronwoodActive: $e");
|
||||
}
|
||||
|
||||
if (active == null && networkFor(walletInfo) == ZcashNetwork.regtest) {
|
||||
try {
|
||||
final height = await runWithCoin(
|
||||
accountId: accountId,
|
||||
func: (final coin) => zkool_network.getCurrentHeight(c: coin),
|
||||
);
|
||||
active = height >= ZcashNetwork.regtestNu63Height;
|
||||
printV("regtest ironwood inferred from height $height: $active");
|
||||
} catch (e) {
|
||||
printV("regtest height check failed: $e");
|
||||
}
|
||||
}
|
||||
|
||||
if (active == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ironwoodActive = active;
|
||||
runInAction(() => walletAddresses.setIronwoodActive(active!));
|
||||
printV("ironwoodActive=$active (account $accountId)");
|
||||
}
|
||||
|
||||
Future<void> _refreshBalance({
|
||||
required final bool runAutoShield,
|
||||
final bool runIronwoodMigrate = true,
|
||||
}) async {
|
||||
try {
|
||||
await _updateIronwoodActive();
|
||||
final bal = await runWithCoin(
|
||||
accountId: accountId,
|
||||
func: (coin) => zkool_sync.balance(c: coin),
|
||||
func: (final coin) async => zkool_sync.balance(c: coin),
|
||||
);
|
||||
|
||||
// 0 - transparent
|
||||
// 1 - sapling
|
||||
// 2 - orchard
|
||||
final confirmedTotal = bal.field0.reduce((final a, final b) => a + b);
|
||||
|
||||
// int knownOutPending = 0;
|
||||
// ZcashWalletBase.temporarySentTx[accountId]?.forEach((final sTx) {
|
||||
// knownOutPending += sTx.value; // it's negative
|
||||
// });
|
||||
final confirmedSpendable = confirmedTotal - bal.field0[0];
|
||||
// 0 - transparent, 1 - sapling, 2 - orchard, 3 - ironwood
|
||||
final transparent = bal.field0[0];
|
||||
final sapling = bal.field0.length > 1 ? bal.field0[1] : BigInt.zero;
|
||||
final orchard = bal.field0.length > 2 ? bal.field0[2] : BigInt.zero;
|
||||
final ironwood = bal.field0.length > 3 ? bal.field0[3] : BigInt.zero;
|
||||
|
||||
if (runAutoShield) {
|
||||
await _autoShield();
|
||||
}
|
||||
if (runIronwoodMigrate) {
|
||||
await _ironwoodMigrate();
|
||||
}
|
||||
|
||||
// After NU6.3, Orchard notes are migrated to Ironwood - show them as unconfirmed.
|
||||
final BigInt availableAmount;
|
||||
final BigInt unavailableAmount;
|
||||
if (ironwoodActive == true && orchard > BigInt.zero) {
|
||||
availableAmount = sapling + ironwood;
|
||||
unavailableAmount = transparent + orchard;
|
||||
} else {
|
||||
availableAmount = sapling + orchard + ironwood;
|
||||
unavailableAmount = transparent;
|
||||
}
|
||||
|
||||
balance[CryptoCurrency.zec] = ZcashBalance(
|
||||
Money(confirmedSpendable, currency),
|
||||
Money(confirmedTotal - confirmedSpendable, currency),
|
||||
Money(availableAmount, currency),
|
||||
Money(unavailableAmount, currency),
|
||||
frozen: Money.zero(currency),
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
@@ -1211,7 +1391,9 @@ abstract class ZcashWalletBase
|
||||
late ZcashWalletAddresses walletAddresses = ZcashWalletAddresses(accountId, walletInfo);
|
||||
|
||||
static Future<ZcashWallet> create(final WalletCredentials credentials) async {
|
||||
await $init();
|
||||
final network = networkForCredentials(credentials);
|
||||
await $init(network: network);
|
||||
credentials.walletInfo?.network = network.value;
|
||||
final newWalletCredentials = credentials as ZcashNewWalletCredentials;
|
||||
|
||||
String mnemonic;
|
||||
@@ -1222,7 +1404,7 @@ abstract class ZcashWalletBase
|
||||
mnemonic = bip39.generateMnemonic(strength: strength);
|
||||
}
|
||||
|
||||
final birthHeight = await ZcashHeight.getBlockHeightByTime(DateTime.now());
|
||||
final birthHeight = await birthHeightForNetwork(network);
|
||||
|
||||
final accountId = await restoreZcashWalletFromSeed(
|
||||
name: credentials.name,
|
||||
@@ -1241,7 +1423,9 @@ abstract class ZcashWalletBase
|
||||
}
|
||||
|
||||
static Future<ZcashWallet> restore(final WalletCredentials credentials) async {
|
||||
await $init();
|
||||
final network = networkForCredentials(credentials);
|
||||
await $init(network: network);
|
||||
credentials.walletInfo?.network = network.value;
|
||||
final fromSeedCredentials = credentials as ZcashFromSeedWalletCredentials;
|
||||
final String? seed = fromSeedCredentials.seed;
|
||||
if (seed == null || seed.isEmpty) {
|
||||
@@ -1265,7 +1449,9 @@ abstract class ZcashWalletBase
|
||||
}
|
||||
|
||||
static Future<ZcashWallet> restoreKeys(final WalletCredentials credentials) async {
|
||||
await $init();
|
||||
final network = networkForCredentials(credentials);
|
||||
await $init(network: network);
|
||||
credentials.walletInfo?.network = network.value;
|
||||
final fromKeysCredentials = credentials as ZcashFromKeysWalletCredentials;
|
||||
final String? keys = fromKeysCredentials.privateKey;
|
||||
if (keys == null || keys.isEmpty) {
|
||||
@@ -1299,7 +1485,8 @@ abstract class ZcashWalletBase
|
||||
required final String password,
|
||||
required final WalletInfo walletInfo,
|
||||
}) async {
|
||||
await $init();
|
||||
final network = networkFor(walletInfo);
|
||||
await $init(network: network);
|
||||
// if (password.isNotEmpty) {
|
||||
// setDbPasswd(coin, password);
|
||||
// }
|
||||
@@ -1379,9 +1566,32 @@ abstract class ZcashWalletBase
|
||||
|
||||
static WalletType get _type => WalletType.zcash;
|
||||
|
||||
static Future<String> getDbDataPath() async {
|
||||
static ZcashNetwork networkFor(final WalletInfo? walletInfo) =>
|
||||
ZcashNetwork.fromName(walletInfo?.network ?? ZcashNetwork.mainnet.value);
|
||||
|
||||
static ZcashNetwork networkForCredentials(final WalletCredentials credentials) {
|
||||
if (credentials is ZcashNewWalletCredentials) {
|
||||
return ZcashNetwork.fromIndex(credentials.network);
|
||||
}
|
||||
if (credentials is ZcashFromSeedWalletCredentials) {
|
||||
return ZcashNetwork.fromIndex(credentials.network);
|
||||
}
|
||||
if (credentials is ZcashFromKeysWalletCredentials) {
|
||||
return ZcashNetwork.fromIndex(credentials.network);
|
||||
}
|
||||
return ZcashNetwork.mainnet;
|
||||
}
|
||||
|
||||
static Future<int> birthHeightForNetwork(final ZcashNetwork network) async {
|
||||
if (network != ZcashNetwork.mainnet) {
|
||||
return 1;
|
||||
}
|
||||
return ZcashHeight.getBlockHeightByTime(DateTime.now());
|
||||
}
|
||||
|
||||
static Future<String> getDbDataPath({final ZcashNetwork network = ZcashNetwork.mainnet}) async {
|
||||
final pathForWalletType = await pathForWalletTypeDir(type: _type);
|
||||
final dbDataPath = "${pathForWalletType}/zec.v2.db";
|
||||
final dbDataPath = "${pathForWalletType}/${network.dbFileName}";
|
||||
if (!Directory(pathForWalletType).existsSync()) {
|
||||
Directory(pathForWalletType).createSync(recursive: true);
|
||||
}
|
||||
@@ -1398,6 +1608,8 @@ abstract class ZcashWalletBase
|
||||
}
|
||||
|
||||
static bool _initialized = false;
|
||||
static bool _rustInitialized = false;
|
||||
static ZcashNetwork? _activeNetwork;
|
||||
|
||||
static void unlockDatabase(final String password) {
|
||||
_password = password;
|
||||
@@ -1406,22 +1618,25 @@ abstract class ZcashWalletBase
|
||||
static var c = zkool_coin.Coin();
|
||||
|
||||
static String? _password;
|
||||
static Future<void> $init() async {
|
||||
if (_initialized) return;
|
||||
_initialized = true;
|
||||
printV(r".$init()");
|
||||
await zkool_frb.RustLib.init();
|
||||
static Future<void> $init({final ZcashNetwork network = ZcashNetwork.mainnet}) async {
|
||||
if (!_rustInitialized) {
|
||||
await zkool_frb.RustLib.init();
|
||||
_rustInitialized = true;
|
||||
}
|
||||
if (_initialized && _activeNetwork == network) {
|
||||
return;
|
||||
}
|
||||
printV(r".$init($network)");
|
||||
ZcashMempoolService.instance.onAccountsUpdated = (final accountIds) {
|
||||
for (final accountId in accountIds) {
|
||||
unawaited(refreshWalletForAccount(accountId));
|
||||
}
|
||||
};
|
||||
final dbFile = File(await getDbDataPath());
|
||||
final dbFile = File(await getDbDataPath(network: network));
|
||||
final ywalletDbFile = File(await getDbDataPathLegacyYwallet());
|
||||
await zkool_network.initDatadir(directory: dbFile.parent.path);
|
||||
// c = await c.openDatabase(dbFilepath: dbFile.path, password: 'cw_zcash_migration');
|
||||
c = await c.openDatabase(dbFilepath: dbFile.path, password: null);
|
||||
printV("initWallet");
|
||||
printV("initWallet: ${dbFile.path}");
|
||||
if (_password == null) {
|
||||
throw Exception("Zcash wallet locked! Please contact support");
|
||||
}
|
||||
@@ -1432,6 +1647,7 @@ abstract class ZcashWalletBase
|
||||
//TODO(mrcyjanek): migrate to zkool
|
||||
}
|
||||
|
||||
_activeNetwork = network;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,24 @@ abstract class ZcashWalletAddressesBase extends WalletAddresses with Store {
|
||||
@observable
|
||||
String? unifiedAddress;
|
||||
|
||||
@observable
|
||||
bool ironwoodActive = false;
|
||||
|
||||
@action
|
||||
void setIronwoodActive(final bool active) {
|
||||
ironwoodActive = active;
|
||||
}
|
||||
|
||||
@override
|
||||
@computed
|
||||
List<ReceivePageOption> get receivePageOptions {
|
||||
return [
|
||||
...ZcashReceivePageOption.allOptionsFor(ironwood: ironwoodActive),
|
||||
...ReceivePageOptions.where((final element) => element != ReceivePageOption.mainnet),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
String get latestAddress {
|
||||
switch (addressPageType) {
|
||||
case ZcashAddressType.transparent:
|
||||
@@ -267,14 +283,6 @@ abstract class ZcashWalletAddressesBase extends WalletAddresses with Store {
|
||||
return addressInfos[0] ?? [];
|
||||
}
|
||||
|
||||
@override
|
||||
List<ReceivePageOption> get receivePageOptions {
|
||||
return [
|
||||
...ZcashReceivePageOption.all,
|
||||
...ReceivePageOptions.where((final element) => element != ReceivePageOption.mainnet),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
PaymentURI getPaymentUri(final String amount) => ZcashURI(amount: amount, address: address);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,11 @@ class ZcashWalletService
|
||||
|
||||
@override
|
||||
Future<ZcashWallet> openWallet(final String name, final String password) async {
|
||||
await ZcashWalletBase.$init();
|
||||
final walletInfo = await WalletInfo.get(name, getType());
|
||||
if (walletInfo == null) {
|
||||
throw Exception('Wallet not found');
|
||||
}
|
||||
await ZcashWalletBase.$init(network: ZcashWalletBase.networkFor(walletInfo));
|
||||
if (await isWalletExit(name)) {
|
||||
final path = (await pathForWallet(name: name, type: getType())) + ".v2";
|
||||
if (!File(path).existsSync()) {
|
||||
@@ -100,10 +104,6 @@ class ZcashWalletService
|
||||
}
|
||||
|
||||
await loadShieldTxs();
|
||||
final walletInfo = await WalletInfo.get(name, getType());
|
||||
if (walletInfo == null) {
|
||||
throw Exception('Wallet not found');
|
||||
}
|
||||
try {
|
||||
final wallet = await ZcashWalletBase.open(
|
||||
name: name,
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
final zecBase = Uint8List(32);
|
||||
|
||||
/// Whether NU6.3 (Ironwood) is active at the chain tip. Updated on balance refresh.
|
||||
bool ironwoodActive = false;
|
||||
|
||||
/// Pool bitmask for Ironwood (pool index 3). Orchard and Ironwood share the same address.
|
||||
const ironwoodPoolMask = 8;
|
||||
|
||||
int? get shieldedRecipientPools => ironwoodActive ? ironwoodPoolMask : null;
|
||||
|
||||
@@ -17,7 +17,7 @@ enum TxType {
|
||||
transparentSelfTransfer, // 12
|
||||
}
|
||||
|
||||
enum NotePool { transparent, sapling, orchard, unknown }
|
||||
enum NotePool { transparent, sapling, orchard, ironwood }
|
||||
|
||||
class ZkoolTx {
|
||||
ZkoolTx(final zkool_account.Tx tx, final zkool_account.TxAccount txAccount)
|
||||
@@ -72,6 +72,22 @@ class ZkoolTx {
|
||||
.fold(BigInt.zero, (final a, final o) => a + o.value);
|
||||
}
|
||||
|
||||
BigInt get ironwoodReceived {
|
||||
final fromNotes = _txAccount.notes
|
||||
.where((final n) => n.pool == NotePool.ironwood.index)
|
||||
.fold(BigInt.zero, (final a, final n) => a + n.value);
|
||||
if (fromNotes > BigInt.zero) {
|
||||
return fromNotes;
|
||||
}
|
||||
return _txAccount.outputs
|
||||
.where((final o) => o.pool == NotePool.ironwood.index)
|
||||
.fold(BigInt.zero, (final a, final o) => a + o.value);
|
||||
}
|
||||
|
||||
BigInt get orchardSpent => _txAccount.spends
|
||||
.where((final s) => s.pool == NotePool.orchard.index)
|
||||
.fold(BigInt.zero, (final a, final s) => a + s.value);
|
||||
|
||||
String get txHash {
|
||||
final reversed = Uint8List.fromList(_txAccount.txid.reversed.toList());
|
||||
final txId = uint8ListToHex(reversed);
|
||||
@@ -222,6 +238,13 @@ class ZkoolTx {
|
||||
}
|
||||
|
||||
static Uint8List _txidFromJson(final dynamic raw) {
|
||||
return _bytesFromJson(raw, fieldName: "txid");
|
||||
}
|
||||
|
||||
static Uint8List _bytesFromJson(final dynamic raw, {required final String fieldName}) {
|
||||
if (raw == null) {
|
||||
return Uint8List(0);
|
||||
}
|
||||
if (raw is Uint8List) {
|
||||
return raw;
|
||||
}
|
||||
@@ -231,7 +254,7 @@ class ZkoolTx {
|
||||
if (raw is String) {
|
||||
return base64.decode(raw);
|
||||
}
|
||||
throw FormatException("Invalid txid in ZkoolTx json: $raw");
|
||||
throw FormatException("Invalid $fieldName in ZkoolTx json: $raw");
|
||||
}
|
||||
|
||||
static List<T> _listFromJson<T>(final dynamic raw, final T Function(Map<String, dynamic>) parse) {
|
||||
@@ -257,6 +280,7 @@ class ZkoolTx {
|
||||
tpe: txJson["tpe"] == null ? null : _asInt(txJson["tpe"]),
|
||||
zsaValue: _asInt(txJson["zsaValue"]),
|
||||
assetDisplay: txJson["assetDisplay"] as String? ?? "",
|
||||
isUserMemo: txJson["isUserMemo"] as bool? ?? false,
|
||||
),
|
||||
zkool_account.TxAccount(
|
||||
id: _asInt(txAccountJson["id"]),
|
||||
@@ -304,6 +328,7 @@ class ZkoolTx {
|
||||
pool: _asInt(a["pool"]),
|
||||
output: a["output"] == null ? null : _asInt(a["output"]),
|
||||
memo: a["memo"] as String?,
|
||||
memoBytes: _bytesFromJson(a["memoBytes"], fieldName: "memoBytes"),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -21,7 +21,7 @@ dependencies:
|
||||
# path: ../../zkool2
|
||||
git:
|
||||
url: https://github.com/cake-tech/zkool2.git
|
||||
ref: dbd3dde8d8485428326a58e55660b7404666dd7c
|
||||
ref: 97d18db966c061a536fa9056efb3756f4b9aa89d
|
||||
|
||||
path_provider: any
|
||||
sqflite_common_ffi: any
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ post_install do |installer|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
|
||||
'$(inherited)',
|
||||
|
||||
|
||||
+8
-59
@@ -1,14 +1,4 @@
|
||||
PODS:
|
||||
- AppAuth (1.7.6):
|
||||
- AppAuth/Core (= 1.7.6)
|
||||
- AppAuth/ExternalUserAgent (= 1.7.6)
|
||||
- AppAuth/Core (1.7.6)
|
||||
- AppAuth/ExternalUserAgent (1.7.6):
|
||||
- AppAuth/Core
|
||||
- AppCheckCore (11.2.0):
|
||||
- GoogleUtilities/Environment (~> 8.0)
|
||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||
- PromisesObjC (~> 2.4)
|
||||
- bitbox_flutter (0.0.1):
|
||||
- Flutter
|
||||
- breez_sdk_spark_flutter (0.14.0):
|
||||
@@ -64,6 +54,8 @@ PODS:
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_contacts (0.0.1):
|
||||
- Flutter
|
||||
- flutter_inappwebview_ios (0.0.1):
|
||||
- Flutter
|
||||
- flutter_inappwebview_ios/Core (= 0.0.1)
|
||||
@@ -83,34 +75,6 @@ PODS:
|
||||
- Flutter
|
||||
- fluttertoast (0.0.2):
|
||||
- Flutter
|
||||
- google_sign_in_ios (0.0.1):
|
||||
- AppAuth (>= 1.7.4)
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- GoogleSignIn (~> 8.0)
|
||||
- GTMSessionFetcher (>= 3.4.0)
|
||||
- GoogleSignIn (8.0.0):
|
||||
- AppAuth (< 2.0, >= 1.7.3)
|
||||
- AppCheckCore (~> 11.0)
|
||||
- GTMAppAuth (< 5.0, >= 4.1.1)
|
||||
- GTMSessionFetcher/Core (~> 3.3)
|
||||
- GoogleUtilities/Environment (8.1.0):
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/Logger (8.1.0):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/Privacy (8.1.0)
|
||||
- GoogleUtilities/UserDefaults (8.1.0):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Privacy
|
||||
- GTMAppAuth (4.1.1):
|
||||
- AppAuth/Core (~> 1.7)
|
||||
- GTMSessionFetcher/Core (< 4.0, >= 3.3)
|
||||
- GTMSessionFetcher (3.5.0):
|
||||
- GTMSessionFetcher/Full (= 3.5.0)
|
||||
- GTMSessionFetcher/Core (3.5.0)
|
||||
- GTMSessionFetcher/Full (3.5.0):
|
||||
- GTMSessionFetcher/Core
|
||||
- image_picker_ios (0.0.1):
|
||||
- Flutter
|
||||
- in_app_review (2.0.0):
|
||||
@@ -126,7 +90,6 @@ PODS:
|
||||
- payjoin_flutter (0.23.0)
|
||||
- permission_handler_apple (9.4.8):
|
||||
- Flutter
|
||||
- PromisesObjC (2.4.0)
|
||||
- quick_actions_ios (0.0.1):
|
||||
- Flutter
|
||||
- reown_yttrium (0.0.1):
|
||||
@@ -204,6 +167,7 @@ DEPENDENCIES:
|
||||
- fast_scanner (from `.symlinks/plugins/fast_scanner/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_contacts (from `.symlinks/plugins/flutter_contacts/ios`)
|
||||
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
||||
- flutter_local_authentication (from `.symlinks/plugins/flutter_local_authentication/ios`)
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||
@@ -211,7 +175,6 @@ DEPENDENCIES:
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
- google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
||||
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
||||
@@ -237,17 +200,10 @@ DEPENDENCIES:
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/CocoaPods/Specs.git:
|
||||
- AppAuth
|
||||
- AppCheckCore
|
||||
- CryptoSwift
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- GoogleSignIn
|
||||
- GoogleUtilities
|
||||
- GTMAppAuth
|
||||
- GTMSessionFetcher
|
||||
- OrderedSet
|
||||
- PromisesObjC
|
||||
- SDWebImage
|
||||
- sqlite3
|
||||
- SwiftyGif
|
||||
@@ -278,6 +234,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_contacts:
|
||||
:path: ".symlinks/plugins/flutter_contacts/ios"
|
||||
flutter_inappwebview_ios:
|
||||
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
|
||||
flutter_local_authentication:
|
||||
@@ -292,8 +250,6 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
fluttertoast:
|
||||
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||
google_sign_in_ios:
|
||||
:path: ".symlinks/plugins/google_sign_in_ios/darwin"
|
||||
image_picker_ios:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
in_app_review:
|
||||
@@ -340,8 +296,6 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/wakelock_plus/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
|
||||
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
||||
bitbox_flutter: 9505732798041c413152669751beeaecc5fe400f
|
||||
breez_sdk_spark_flutter: ed3b6709b8ce9b40309d8728640eade5b19e8a0d
|
||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||
@@ -357,6 +311,7 @@ SPEC CHECKSUMS:
|
||||
fast_scanner: 2cb1ad3e69e645e9980fb4961396ce5804caa3e3
|
||||
file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
flutter_contacts: 2ec1d6b62ca2869c3bbb3871bd74d4e3ef157135
|
||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||
flutter_local_authentication: 989278c681612f1ee0e36019e149137f114b9d7f
|
||||
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
|
||||
@@ -364,11 +319,6 @@ SPEC CHECKSUMS:
|
||||
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||
flutter_secure_storage: 2c2ff13db9e0a5647389bff88b0ecac56e3f3418
|
||||
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
||||
google_sign_in_ios: b48bb9af78576358a168361173155596c845f0b9
|
||||
GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4
|
||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
|
||||
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
||||
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
||||
in_app_review: 7dd1ea365263f834b8464673f9df72c80c17c937
|
||||
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
|
||||
@@ -377,7 +327,6 @@ SPEC CHECKSUMS:
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
payjoin_flutter: d9d4c8aa16bd5dfedb9b21d0edc8199e0187d96e
|
||||
permission_handler_apple: 92d754bbaa7361d436db2d6c3c1c2a0fdcec462e
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
quick_actions_ios: 500fcc11711d9f646739093395c4ae8eec25f779
|
||||
reown_yttrium: cee334ade64725b1d83f7b34c706a6aae2696d58
|
||||
rive_common: dd421daaf9ae69f0125aa761dd96abd278399952
|
||||
@@ -398,6 +347,6 @@ SPEC CHECKSUMS:
|
||||
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
|
||||
YttriumWrapper: 31e937fe9fbe0f1314d2ca6be9ce9b379a059966
|
||||
|
||||
PODFILE CHECKSUM: 5296465b1c6d14d506230356756826012f65d97a
|
||||
PODFILE CHECKSUM: b08d7f4c8d9c64f5648599a30caea878b0f7616a
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
COCOAPODS: 1.17.0
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
@@ -509,7 +509,7 @@
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -578,7 +578,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -628,7 +628,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
@@ -660,7 +660,7 @@
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -704,7 +704,7 @@
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cake_wallet/zcash/zcash_network_type.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
|
||||
@@ -10,8 +11,11 @@ const BEFORE_REGEX = '(^|\\s)';
|
||||
const AFTER_REGEX = '(\$|\\s)';
|
||||
|
||||
class AddressValidator extends TextValidator {
|
||||
AddressValidator({required CryptoCurrency type, bool isTestnet = false})
|
||||
: super(
|
||||
AddressValidator({
|
||||
required CryptoCurrency type,
|
||||
bool isTestnet = false,
|
||||
String? network,
|
||||
}) : super(
|
||||
errorMessage: S.current.error_text_address,
|
||||
useAdditionalValidation: [CryptoCurrency.btc, CryptoCurrency.ltc].contains(type)
|
||||
? (String txt) {
|
||||
@@ -32,7 +36,7 @@ class AddressValidator extends TextValidator {
|
||||
: type == CryptoCurrency.zano
|
||||
? zano?.validateAddress
|
||||
: null,
|
||||
pattern: getPattern(type, isTestnet: isTestnet),
|
||||
pattern: getPattern(type, isTestnet: isTestnet, network: network),
|
||||
length: getLength(type),
|
||||
);
|
||||
|
||||
@@ -61,7 +65,7 @@ class AddressValidator extends TextValidator {
|
||||
CryptoCurrency.btcln,
|
||||
];
|
||||
|
||||
static String getPattern(CryptoCurrency type, {bool isTestnet = false}) {
|
||||
static String? getPattern(CryptoCurrency type, {bool isTestnet = false, String? network}) {
|
||||
var pattern = "";
|
||||
if (type is Erc20Token) {
|
||||
pattern = '0x[0-9a-zA-Z]+';
|
||||
@@ -170,6 +174,9 @@ class AddressValidator extends TextValidator {
|
||||
case CryptoCurrency.hbar:
|
||||
pattern = '[0-9a-zA-Z.]+';
|
||||
case CryptoCurrency.zec:
|
||||
if (ZcashNetworkType.isDevNetwork(network)) {
|
||||
return null;
|
||||
}
|
||||
pattern = '(?:'
|
||||
't1[0-9A-Za-z]{33}'
|
||||
'|t3[0-9A-Za-z]{33}'
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:cake_wallet/src/widgets/section_divider.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
|
||||
import 'package:cw_core/receive_page_option.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
@@ -34,6 +35,7 @@ class _ReceiveAddressTypeSelectorState extends State<ReceiveAddressTypeSelector>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(builder: (final _) {
|
||||
final commonOptions = widget.receiveOptionViewModel.options
|
||||
.where((element) =>
|
||||
element.isCommon ||
|
||||
@@ -223,6 +225,7 @@ class _ReceiveAddressTypeSelectorState extends State<ReceiveAddressTypeSelector>
|
||||
],
|
||||
)),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +162,7 @@ import 'package:cw_core/unspent_coin_type.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/zcash/zcash_network_type.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -822,6 +823,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
final isChildWallet = args['isChildWallet'] as bool? ?? false;
|
||||
final useTestnet = args['useTestnet'] as bool;
|
||||
final toggleTestnet = args['toggleTestnet'] as Function(bool? val);
|
||||
final zcashNetwork = args['zcashNetwork'] as int? ?? ZcashNetworkType.mainnet;
|
||||
final setZcashNetwork =
|
||||
args['setZcashNetwork'] as void Function(int network)? ?? (_) {};
|
||||
final restoredWallet = args['restoredWallet'] as RestoredWallet?;
|
||||
|
||||
final viewModelParam = {'type': type, 'isPow': false};
|
||||
@@ -832,6 +836,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
isChildWallet: isChildWallet,
|
||||
useTestnet: useTestnet,
|
||||
toggleUseTestnet: toggleTestnet,
|
||||
zcashNetwork: zcashNetwork,
|
||||
setZcashNetwork: setZcashNetwork,
|
||||
advancedPrivacySettingsViewModel:
|
||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
|
||||
nodeViewModel: getIt.get<NodeCreateOrEditViewModel>(param1: viewModelParam),
|
||||
|
||||
@@ -14,11 +14,13 @@ import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/zcash/zcash_network_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
@@ -28,6 +30,8 @@ class AdvancedPrivacySettingsPage extends BasePage {
|
||||
required this.isChildWallet,
|
||||
required this.useTestnet,
|
||||
required this.toggleUseTestnet,
|
||||
required this.zcashNetwork,
|
||||
required this.setZcashNetwork,
|
||||
required this.advancedPrivacySettingsViewModel,
|
||||
required this.nodeViewModel,
|
||||
required this.seedSettingsViewModel,
|
||||
@@ -44,6 +48,8 @@ class AdvancedPrivacySettingsPage extends BasePage {
|
||||
final bool isChildWallet;
|
||||
final bool useTestnet;
|
||||
final Function(bool? val) toggleUseTestnet;
|
||||
final int zcashNetwork;
|
||||
final void Function(int network) setZcashNetwork;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => _AdvancedPrivacySettingsBody(
|
||||
@@ -51,6 +57,8 @@ class AdvancedPrivacySettingsPage extends BasePage {
|
||||
isChildWallet,
|
||||
useTestnet,
|
||||
toggleUseTestnet,
|
||||
zcashNetwork,
|
||||
setZcashNetwork,
|
||||
advancedPrivacySettingsViewModel,
|
||||
nodeViewModel,
|
||||
seedSettingsViewModel,
|
||||
@@ -63,6 +71,8 @@ class _AdvancedPrivacySettingsBody extends StatefulWidget {
|
||||
this.isChildWallet,
|
||||
this.useTestnet,
|
||||
this.toggleUseTestnet,
|
||||
this.zcashNetwork,
|
||||
this.setZcashNetwork,
|
||||
this.privacySettingsViewModel,
|
||||
this.nodeViewModel,
|
||||
this.seedTypeViewModel,
|
||||
@@ -76,6 +86,8 @@ class _AdvancedPrivacySettingsBody extends StatefulWidget {
|
||||
final bool isChildWallet;
|
||||
final bool useTestnet;
|
||||
final Function(bool? val) toggleUseTestnet;
|
||||
final int zcashNetwork;
|
||||
final void Function(int network) setZcashNetwork;
|
||||
|
||||
@override
|
||||
_AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState();
|
||||
@@ -87,6 +99,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
final _formKey = GlobalKey<NodeFormState>();
|
||||
final _passphraseFormKey = GlobalKey<FormState>();
|
||||
bool? testnetValue;
|
||||
int? zcashNetworkValue;
|
||||
|
||||
bool obscurePassphrase = true;
|
||||
|
||||
@@ -112,6 +125,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
if (testnetValue == null && widget.useTestnet) {
|
||||
testnetValue = widget.useTestnet;
|
||||
}
|
||||
zcashNetworkValue ??= widget.zcashNetwork;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
@@ -281,6 +295,20 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
],
|
||||
);
|
||||
}),
|
||||
if (FeatureFlag.hasDevOptions &&
|
||||
widget.privacySettingsViewModel.type == WalletType.zcash)
|
||||
SettingsChoicesCell(
|
||||
ChoicesListItem<int>(
|
||||
title: 'Zcash network',
|
||||
items: ZcashNetworkType.values,
|
||||
selectedItem: zcashNetworkValue ?? ZcashNetworkType.mainnet,
|
||||
displayItem: ZcashNetworkType.label,
|
||||
onItemSelected: (final network) {
|
||||
setState(() => zcashNetworkValue = network);
|
||||
widget.setZcashNetwork(network);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (widget.privacySettingsViewModel.type == WalletType.bitcoin ||
|
||||
widget.privacySettingsViewModel.type == WalletType.decred)
|
||||
Builder(builder: (_) {
|
||||
|
||||
@@ -316,6 +316,8 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||
"type": _walletNewVM.type,
|
||||
"useTestnet": _walletNewVM.useTestnet,
|
||||
"toggleTestnet": _walletNewVM.toggleUseTestnet,
|
||||
"zcashNetwork": _walletNewVM.zcashNetwork,
|
||||
"setZcashNetwork": _walletNewVM.setZcashNetwork,
|
||||
"isChildWallet": widget.isChildWallet,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -121,7 +121,9 @@ class WalletRestorePage extends BasePage {
|
||||
'isFromRestore': true,
|
||||
'type': walletRestoreViewModel.type,
|
||||
'useTestnet': walletRestoreViewModel.useTestnet,
|
||||
'toggleTestnet': walletRestoreViewModel.toggleUseTestnet
|
||||
'toggleTestnet': walletRestoreViewModel.toggleUseTestnet,
|
||||
'zcashNetwork': walletRestoreViewModel.zcashNetwork,
|
||||
'setZcashNetwork': walletRestoreViewModel.setZcashNetwork,
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -538,10 +538,16 @@ abstract class DashboardViewModelBase with Store {
|
||||
.map((item) => _txIdentityString(item.transaction.txHash, item.transaction.direction))
|
||||
.toSet();
|
||||
|
||||
transactions.removeWhere(
|
||||
(item) => newKeys
|
||||
.contains(_txIdentityString(item.transaction.txHash, item.transaction.direction)),
|
||||
);
|
||||
transactions.removeWhere((item) {
|
||||
if (wallet.type == WalletType.zcash) {
|
||||
return newTransactions.any(
|
||||
(n) => n.transaction.txHash == item.transaction.txHash,
|
||||
);
|
||||
}
|
||||
return newKeys.contains(
|
||||
_txIdentityString(item.transaction.txHash, item.transaction.direction),
|
||||
);
|
||||
});
|
||||
|
||||
transactions.addAll(newTransactions);
|
||||
// transactions.clear();
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:cake_wallet/zcash/zcash.dart';
|
||||
import 'package:cw_core/receive_page_option.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_zcash/cw_zcash.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'receive_option_view_model.g.dart';
|
||||
@@ -18,7 +19,14 @@ abstract class ReceiveOptionViewModelBase with Store {
|
||||
? ReceivePageOption.testnet
|
||||
: _wallet.type == WalletType.zcash
|
||||
? zcash!.getSelectedAddressType(_wallet)
|
||||
: ReceivePageOption.mainnet);
|
||||
: ReceivePageOption.mainnet) {
|
||||
if (_wallet.type == WalletType.zcash) {
|
||||
reaction<bool>(
|
||||
(_) => (_wallet.walletAddresses as ZcashWalletAddresses).ironwoodActive,
|
||||
(_) => _syncZcashShieldedLabel(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final WalletBase _wallet;
|
||||
|
||||
@@ -27,10 +35,21 @@ abstract class ReceiveOptionViewModelBase with Store {
|
||||
@observable
|
||||
ReceivePageOption selectedReceiveOption;
|
||||
|
||||
@computed
|
||||
List<ReceivePageOption> get options => _wallet.walletAddresses.receivePageOptions;
|
||||
|
||||
String get walletTypeString => walletTypeToString(_wallet.type);
|
||||
|
||||
@action
|
||||
void selectReceiveOption(ReceivePageOption option) => selectedReceiveOption = option;
|
||||
|
||||
@action
|
||||
void _syncZcashShieldedLabel() {
|
||||
for (final option in options) {
|
||||
if (option.value == selectedReceiveOption.value) {
|
||||
selectedReceiveOption = option;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
return 'Transaction has missing data';
|
||||
}
|
||||
|
||||
if (transaction.additionalInfo['isIronwoodMigration'] == true) {
|
||||
return 'Migration';
|
||||
}
|
||||
if (transaction.additionalInfo['isAutoShield'] == true) {
|
||||
if (transaction.isPending) {
|
||||
final status = formattedStatus;
|
||||
|
||||
@@ -48,8 +48,11 @@ abstract class SendTemplateViewModelBase with Store {
|
||||
amountParsingProxy: _appStore.amountParsingProxy,
|
||||
);
|
||||
|
||||
AddressValidator get addressValidator =>
|
||||
AddressValidator(type: _wallet.currency, isTestnet: _wallet.isTestnet);
|
||||
AddressValidator get addressValidator => AddressValidator(
|
||||
type: _wallet.currency,
|
||||
isTestnet: _wallet.isTestnet,
|
||||
network: _wallet.walletInfo.network,
|
||||
);
|
||||
|
||||
TemplateValidator get templateValidator => TemplateValidator();
|
||||
|
||||
|
||||
@@ -190,7 +190,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
// Check if the address is valid for the current currency (except for Zano, which can use handles as addresses)
|
||||
if (selectedCryptoCurrency != CryptoCurrency.zano) {
|
||||
final isValidAddress = AddressValidator(type: selectedCryptoCurrency).isValid(query);
|
||||
final isValidAddress = AddressValidator(
|
||||
type: selectedCryptoCurrency,
|
||||
network: wallet.walletInfo.network,
|
||||
).isValid(query);
|
||||
if (isValidAddress) return null;
|
||||
}
|
||||
|
||||
@@ -317,8 +320,11 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
Validator<String> get allAmountValidator => AllAmountValidator();
|
||||
|
||||
Validator<String> get addressValidator =>
|
||||
AddressValidator(type: selectedCryptoCurrency, isTestnet: wallet.isTestnet);
|
||||
Validator<String> get addressValidator => AddressValidator(
|
||||
type: selectedCryptoCurrency,
|
||||
isTestnet: wallet.isTestnet,
|
||||
network: wallet.walletInfo.network,
|
||||
);
|
||||
|
||||
Validator<String> get textValidator => TextValidator();
|
||||
|
||||
|
||||
@@ -428,6 +428,9 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
}
|
||||
|
||||
String get formattedTitle {
|
||||
if (transactionInfo.additionalInfo['isIronwoodMigration'] == true) {
|
||||
return 'Migration';
|
||||
}
|
||||
if (transactionInfo.additionalInfo['isAutoShield'] == true) {
|
||||
return S.current.shielding;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/zcash/zcash_network_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:polyseed/polyseed.dart';
|
||||
|
||||
@@ -33,9 +34,13 @@ abstract class WalletCreationVMBase with Store {
|
||||
@observable
|
||||
bool _useTestnet = false;
|
||||
|
||||
int _zcashNetwork = ZcashNetworkType.mainnet;
|
||||
|
||||
@computed
|
||||
bool get useTestnet => _useTestnet;
|
||||
|
||||
int get zcashNetwork => _zcashNetwork;
|
||||
|
||||
@observable
|
||||
String name;
|
||||
|
||||
@@ -252,4 +257,8 @@ abstract class WalletCreationVMBase with Store {
|
||||
void toggleUseTestnet(bool? value) {
|
||||
_useTestnet = value ?? !_useTestnet;
|
||||
}
|
||||
|
||||
void setZcashNetwork(int network) {
|
||||
_zcashNetwork = network;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
passphrase: passphrase,
|
||||
network: zcashNetwork,
|
||||
);
|
||||
case WalletType.decred:
|
||||
return decred!.createDecredNewWalletCredentials(name: name);
|
||||
|
||||
@@ -224,6 +224,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
height: height,
|
||||
network: zcashNetwork,
|
||||
);
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
|
||||
+13
-18
@@ -7,12 +7,14 @@ class CWZcash extends Zcash {
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? mnemonic,
|
||||
required String? passphrase}) {
|
||||
required String? passphrase,
|
||||
int network = 0}) {
|
||||
return ZcashNewWalletCredentials(
|
||||
name: name,
|
||||
passphrase: passphrase,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
network: network,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,9 +38,10 @@ class CWZcash extends Zcash {
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
String? passphrase,
|
||||
required int? height}) {
|
||||
required int? height,
|
||||
int network = 0}) {
|
||||
return ZcashFromSeedWalletCredentials(
|
||||
name: name, seed: mnemonic, passphrase: passphrase, password: password, height: height);
|
||||
name: name, seed: mnemonic, passphrase: passphrase, password: password, height: height, network: network);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -161,9 +164,12 @@ class CWZcash extends Zcash {
|
||||
@override
|
||||
ReceivePageOption getSelectedAddressType(Object wallet) {
|
||||
final zcashWallet = wallet as ZcashWallet;
|
||||
final t =
|
||||
(zcashWallet.walletAddresses as ZcashWalletAddresses).walletInfo.addressPageType ?? "";
|
||||
return ZcashReceivePageOption.fromType(ZcashReceivePageOption.typeFromString(t));
|
||||
final addresses = zcashWallet.walletAddresses as ZcashWalletAddresses;
|
||||
final type = ZcashReceivePageOption.typeFromString(addresses.walletInfo.addressPageType ?? "");
|
||||
if (type == ZcashAddressType.shieldedOrchard) {
|
||||
return ZcashReceivePageOption.shieldedOrchard(ironwood: addresses.ironwoodActive);
|
||||
}
|
||||
return ZcashReceivePageOption.fromType(type);
|
||||
}
|
||||
|
||||
bool hasSelectedTransparentAddress(Object wallet) {
|
||||
@@ -172,18 +178,7 @@ class CWZcash extends Zcash {
|
||||
|
||||
@override
|
||||
dynamic getZcashAddressType(ReceivePageOption option) {
|
||||
switch (option) {
|
||||
case ZcashReceivePageOption.unified:
|
||||
return ZcashAddressType.unifiedType;
|
||||
case ZcashReceivePageOption.transparent:
|
||||
return ZcashAddressType.transparent;
|
||||
case ZcashReceivePageOption.shieldedSapling:
|
||||
return ZcashAddressType.shieldedSapling;
|
||||
case ZcashReceivePageOption.shieldedOrchard:
|
||||
return ZcashAddressType.shieldedOrchard;
|
||||
default:
|
||||
throw Exception("Unknown ReceivePageOption!");
|
||||
}
|
||||
return (option as ZcashReceivePageOption).toType();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/// Zcash network indices passed from lib/ into cw_zcash.
|
||||
abstract final class ZcashNetworkType {
|
||||
static const int mainnet = 0;
|
||||
static const int testnet = 1;
|
||||
static const int regtest = 2;
|
||||
|
||||
static const List<int> values = [mainnet, testnet, regtest];
|
||||
|
||||
static String label(final int network) => switch (network) {
|
||||
testnet => 'Testnet',
|
||||
regtest => 'Regtest',
|
||||
_ => 'Mainnet',
|
||||
};
|
||||
|
||||
static bool isDevNetwork(final String? network) =>
|
||||
network?.toLowerCase() == 'testnet' || network?.toLowerCase() == 'regtest';
|
||||
}
|
||||
+4
-2
@@ -1721,13 +1721,15 @@ abstract class Zcash {
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? mnemonic,
|
||||
required String? passphrase});
|
||||
required String? passphrase,
|
||||
int network = 0});
|
||||
WalletCredentials createZcashRestoreWalletFromSeedCredentials(
|
||||
{required String name,
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
String? passphrase,
|
||||
required int? height});
|
||||
required int? height,
|
||||
int network = 0});
|
||||
WalletCredentials createZcashRestoreWalletFromPrivateKey(
|
||||
{required String name, required String privateKey, required String password, required int height});
|
||||
String getAddress(WalletBase wallet);
|
||||
|
||||
Reference in New Issue
Block a user