Merge branch 'cake-tech:dev' into dev

This commit is contained in:
BSN ∞/21M
2026-07-27 15:28:31 +02:00
committed by GitHub
40 changed files with 1071 additions and 210 deletions
+5 -2
View File
@@ -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 {
+2 -1
View File
@@ -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);
+429
View File
@@ -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"
+2
View File
@@ -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';
+6
View File
@@ -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();
+20 -3
View File
@@ -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);
+49
View File
@@ -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;
+27 -2
View File
@@ -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'] ??= '';
+279 -63
View File
@@ -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;
}
+16 -8
View File
@@ -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);
}
+5 -5
View File
@@ -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,
+8
View File
@@ -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;
+27 -2
View File
@@ -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"),
),
),
),
+1 -1
View File
@@ -21,7 +21,7 @@ dependencies:
# path: ../../zkool2
git:
url: https://github.com/cake-tech/zkool2.git
ref: dbd3dde8d8485428326a58e55660b7404666dd7c
ref: 6329d4aacb4568ee80ff84baba5ea93b07ae1013
path_provider: any
sqflite_common_ffi: any
+1 -1
View File
@@ -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)',
+1 -58
View File
@@ -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):
@@ -83,34 +73,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 +88,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):
@@ -211,7 +172,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 +197,10 @@ DEPENDENCIES:
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
- AppAuth
- AppCheckCore
- CryptoSwift
- DKImagePickerController
- DKPhotoGallery
- GoogleSignIn
- GoogleUtilities
- GTMAppAuth
- GTMSessionFetcher
- OrderedSet
- PromisesObjC
- SDWebImage
- sqlite3
- SwiftyGif
@@ -292,8 +245,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 +291,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
@@ -364,11 +313,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 +321,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 +341,6 @@ SPEC CHECKSUMS:
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
YttriumWrapper: 31e937fe9fbe0f1314d2ca6be9ce9b379a059966
PODFILE CHECKSUM: 5296465b1c6d14d506230356756826012f65d97a
PODFILE CHECKSUM: b08d7f4c8d9c64f5648599a30caea878b0f7616a
COCOAPODS: 1.16.2
+6 -6
View File
@@ -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",
+11 -4
View File
@@ -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>
],
)),
);
});
}
}
+6
View File
@@ -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();
@@ -18,7 +18,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>(
(_) => zcash!.ironwoodActive(_wallet.walletAddresses),
(_) => _syncZcashShieldedLabel(),
);
}
}
final WalletBase _wallet;
@@ -27,10 +34,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();
+9 -3
View File
@@ -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;
}
+9
View File
@@ -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;
}
}
+1
View File
@@ -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:
+18 -18
View File
@@ -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
@@ -213,6 +208,11 @@ class CWZcash extends Zcash {
@override
Future<void> rescanInternalChange(WalletBase wallet) async {}
@override
bool ironwoodActive(WalletAddresses walletAddresses) {
return (walletAddresses as ZcashWalletAddresses).ironwoodActive;
}
}
const wordList = [
+17
View File
@@ -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 -4
View File
@@ -14,15 +14,15 @@ TYPES=($MONERO_COM $CAKEWALLET)
APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="6.3.2"
MONERO_COM_BUILD_NUMBER=4163
MONERO_COM_VERSION="6.4.0"
MONERO_COM_BUILD_NUMBER=4164
MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app"
MONERO_COM_SCHEME="monero.com"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="6.3.2"
CAKEWALLET_BUILD_NUMBER=4418
CAKEWALLET_VERSION="6.4.0"
CAKEWALLET_BUILD_NUMBER=4419
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
CAKEWALLET_SCHEME="cakewallet"
+4 -4
View File
@@ -12,13 +12,13 @@ TYPES=($MONERO_COM $CAKEWALLET)
APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="6.3.2"
MONERO_COM_BUILD_NUMBER=169
MONERO_COM_VERSION="6.4.0"
MONERO_COM_BUILD_NUMBER=170
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="6.3.2"
CAKEWALLET_BUILD_NUMBER=429
CAKEWALLET_VERSION="6.4.0"
CAKEWALLET_BUILD_NUMBER=433
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
+6 -2
View File
@@ -1704,6 +1704,7 @@ import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cw_core/wallet_addresses.dart';
""";
const zcashCWHeaders = """
@@ -1721,13 +1722,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);
@@ -1766,6 +1769,7 @@ abstract class Zcash {
Future<int> getHeightByDate(DateTime date);
bool showMissingFundsCard(WalletBase wallet);
Future<void> rescanInternalChange(WalletBase wallet);
bool ironwoodActive(WalletAddresses walletAddresses);
}
""";