mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-19 05:58:50 +02:00
Add decred (#1938)
* decred: Add decred. (#1322) * multi: Add initial decred screens. (#1165) Use a mock libwallet for now. * cw_decred: add libdcrwallet dependency and link library for android, ios and macos (#1240) * change cw_decred from package to plugin * add libdcrwallet dependency and link library for android, ios and macos * remove spvwallet, make some libdcrwallet fns async, light refactor * libdcrwallet: use json payload returns * use specific libwallet commit hash * decred: fix Rename wallet. --------- Co-authored-by: JoeGruff <joegruffins@gmail.com> * decred: Add sync. * decred: Add send transaction. * decred: Fix fee estimation. * decred: List transactions. * decred: Add rescan. * decred: Sign message. * decred: Add new addr and addrs. * decred: Add change wallet pass. * decred: Add restore from seed. * decred: Add watching only wallets. * decred: Enable mainnet. * decred: Allow using blank node address. This allows a persistent peer to be unset, falling back to decred seeders. * decred: Rescan from wallet birthday. * add and update macos build scripts, update build readme, gitignore macos project.pbxproj Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com> * multi: hide decred rescan page if it's not ready - move hasRescan method to WalletBase and implement for decred Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com> * cw_decred: fix bug where decred wallets are not loaded after app restart Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com> * add buy and sell for decred via onramp Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com> * bug-fix: account for other send outputs that are part of the same tx Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com> * decred: Return address with no peers. * decred: Update pubspec. * decred: Add verify message. * upgrade hive_generator dep in cw_decred * decred: Clean up code. --------- Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com> Co-authored-by: Wisdom Arerosuoghene <wisdom.arerosuoghene@gmail.com> Co-authored-by: Philemon Ukane <ukanephilemon@gmail.com> * fix extracted addresses not used fix conflicts with main * remove print [skip ci] * minor formatting * fix initial migration version * add build decred script to workflow * install go before build decred fix switch cases * trial 2 to fix decred build * re-install go * revert build script change * refactor/clean nodes functions * Fix address book issue Fix send ALL (to be continued with the fees point) * Fix transactions display issues Add missing file * Fix unconfirmed balance not displayed Change Wallet order Minor cleanup * Fix workflow * Fix workflow * Fix workflow * test * hardcode path for now * fix + cleanup decred build script to work on mac and linux * Update decred build script * Run actions on pull requests, extract commit message * run after checkout * add safe directory * Get commit message from base.sha instead of last commit * base -> head * Do not merge main branch into pr * [skip slack] [run tests] clone by sha * Proper name for decred library in the build script * Throw an error when ANDROID_HOME or ANDROID_NDK_VERSION is missing * Fix conflicts with main * minor code enhancement * decred: Add used address history. (#1941) * decred: Update pubspec. * decred testnet * decred: Add used address history. * decred: Remove default node list. * populate transaction history before sync begins * decred: Add some awaits. * decred: Fix send all. * decred: Add clang export to build script. * decred: Update logo colors. * cleanup cw_decred.dart * make decred wallet addresses selectable in receive page * decred: Always set default addr when used. * decred: Add back default node list. * decred: Allow creating addresses manually. --------- Co-authored-by: Wisdom Arerosuoghene <wisdom.arerosuoghene@gmail.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * minor fixes and cleanup * minor fix, feel free to test now * - Fix transaction details - Fix Nodes - Add processing sync status * Add decred info card * push missing file * Add missing text for decred info card * minor: change docs link [skip ci] * decred: Update derivation info. (#2013) * decred: Update derivation info. * decred: Allow unsynced unused addresses. * decred: Update dcrwallet dep to 4.3.0. * Merge main and fix conflicts * Merge main and fix conflicts * decred: Fix background sync panic. (#2080) * decred: Run libwallet in isolate. (#2077) * decred: Fix contact save inquiry. (#2083) Also fix tx time and the fee shown on pending transactions. * Disable send button in view only decred wallets * - Fix frozen coins - Add URI support - Fix fees in tx details - Handle empty coins send - Handle wallets in address book * Merge main * remove print [skip ci] * Fix restore from QR * minor improvement for QR restore * minor fixes [skip ci] * decred: Get slip44 addrs before sync completes. (#2092) * - Fix loading wallet more than one time - Fix minor UI issue --------- Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com> Co-authored-by: JoeGruffins <34998433+JoeGruffins@users.noreply.github.com> Co-authored-by: Wisdom Arerosuoghene <wisdom.arerosuoghene@gmail.com> Co-authored-by: Philemon Ukane <ukanephilemon@gmail.com> Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>
This commit is contained in:
co-authored by
JoeGruffins
Wisdom Arerosuoghene
Philemon Ukane
Czarek Nakamoto
parent
52a39e29d4
commit
0ba54fa602
@@ -117,7 +117,7 @@ class AddressValidator extends TextValidator {
|
||||
case CryptoCurrency.zec:
|
||||
pattern = 't1[0-9a-zA-Z]{33}|t3[0-9a-zA-Z]{33}';
|
||||
case CryptoCurrency.dcr:
|
||||
pattern = 'D[ksecS]([0-9a-zA-Z])+';
|
||||
pattern = '(D|T|S)[ksecS]([0-9a-zA-Z])+';
|
||||
case CryptoCurrency.rvn:
|
||||
pattern = '[Rr]([1-9a-km-zA-HJ-NP-Z]){33}';
|
||||
case CryptoCurrency.near:
|
||||
|
||||
@@ -17,3 +17,14 @@ class NodePathValidator extends TextValidator {
|
||||
isAutovalidate: true,
|
||||
);
|
||||
}
|
||||
|
||||
// NodeAddressValidatorDecredBlankException allows decred to send a blank ip
|
||||
// address which effectively clears the current set persistant peer.
|
||||
class NodeAddressValidatorDecredBlankException extends TextValidator {
|
||||
NodeAddressValidatorDecredBlankException()
|
||||
: super(
|
||||
errorMessage: S.current.error_text_node_address,
|
||||
isAutovalidate: true,
|
||||
pattern:
|
||||
'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\$|^[0-9a-zA-Z.\-]+\$');
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/utils/language_list.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
@@ -50,6 +51,8 @@ class SeedValidator extends Validator<MnemonicItem> {
|
||||
return wownero!.getWowneroWordList(language);
|
||||
case WalletType.zano:
|
||||
return zano!.getWordList(language);
|
||||
case WalletType.decred:
|
||||
return decred!.getDecredWordList();
|
||||
case WalletType.none:
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -63,5 +63,9 @@ String syncStatusTitle(SyncStatus syncStatus) {
|
||||
return S.current.sync_status_attempting_scan;
|
||||
}
|
||||
|
||||
if (syncStatus is ProcessingSyncStatus) {
|
||||
return syncStatus.message ?? S.current.processing;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ class WalletCreationService {
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.zano:
|
||||
case WalletType.decred:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
part of 'decred.dart';
|
||||
|
||||
class CWDecred extends Decred {
|
||||
CWDecred() {}
|
||||
|
||||
@override
|
||||
WalletCredentials createDecredNewWalletCredentials(
|
||||
{required String name, WalletInfo? walletInfo}) =>
|
||||
DecredNewWalletCredentials(name: name, walletInfo: walletInfo);
|
||||
|
||||
@override
|
||||
WalletCredentials createDecredRestoreWalletFromSeedCredentials(
|
||||
{required String name, required String mnemonic, required String password}) =>
|
||||
DecredRestoreWalletFromSeedCredentials(name: name, mnemonic: mnemonic, password: password);
|
||||
|
||||
@override
|
||||
WalletCredentials createDecredRestoreWalletFromPubkeyCredentials(
|
||||
{required String name, required String pubkey, required String password}) =>
|
||||
DecredRestoreWalletFromPubkeyCredentials(name: name, pubkey: pubkey, password: password);
|
||||
|
||||
@override
|
||||
WalletService createDecredWalletService(
|
||||
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
|
||||
return DecredWalletService(walletInfoSource, unspentCoinSource);
|
||||
}
|
||||
|
||||
@override
|
||||
List<TransactionPriority> getTransactionPriorities() => DecredTransactionPriority.all;
|
||||
|
||||
@override
|
||||
TransactionPriority getDecredTransactionPriorityMedium() => DecredTransactionPriority.medium;
|
||||
|
||||
@override
|
||||
TransactionPriority getDecredTransactionPrioritySlow() => DecredTransactionPriority.slow;
|
||||
|
||||
@override
|
||||
TransactionPriority deserializeDecredTransactionPriority(int raw) =>
|
||||
DecredTransactionPriority.deserialize(raw: raw);
|
||||
|
||||
@override
|
||||
Object createDecredTransactionCredentials(List<Output> outputs, TransactionPriority priority) =>
|
||||
DecredTransactionCredentials(
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
.toList(),
|
||||
priority: priority as DecredTransactionPriority);
|
||||
|
||||
List<AddressInfo> getAddressInfos(Object wallet) {
|
||||
final decredWallet = wallet as DecredWallet;
|
||||
return decredWallet.walletAddresses.getAddressInfos();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAddress(Object wallet, String address, String label) async {
|
||||
final decredWallet = wallet as DecredWallet;
|
||||
await decredWallet.walletAddresses.updateAddress(address, label);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> generateNewAddress(Object wallet, String label) async {
|
||||
final decredWallet = wallet as DecredWallet;
|
||||
await decredWallet.walletAddresses.generateNewAddress(label);
|
||||
}
|
||||
|
||||
@override
|
||||
String formatterDecredAmountToString({required int amount}) =>
|
||||
decredAmountToString(amount: amount);
|
||||
|
||||
@override
|
||||
double formatterDecredAmountToDouble({required int amount}) =>
|
||||
decredAmountToDouble(amount: amount);
|
||||
|
||||
@override
|
||||
int formatterStringDoubleToDecredAmount(String amount) => stringDoubleToDecredAmount(amount);
|
||||
|
||||
@override
|
||||
List<Unspent> getUnspents(Object wallet) {
|
||||
final decredWallet = wallet as DecredWallet;
|
||||
return decredWallet.unspents();
|
||||
}
|
||||
|
||||
@override
|
||||
void updateUnspents(Object wallet) {
|
||||
final decredWallet = wallet as DecredWallet;
|
||||
decredWallet.unspents();
|
||||
}
|
||||
|
||||
@override
|
||||
int heightByDate(DateTime date) {
|
||||
final genesisBlocktime = DateTime.fromMillisecondsSinceEpoch(1454954400 * 1000);
|
||||
final minutesDiff = date.difference(genesisBlocktime).inMinutes;
|
||||
// Decred has five minute blocks on mainnet.
|
||||
// NOTE: This is off by about a day but is currently unused by decred as we
|
||||
// rescan from the wallet birthday.
|
||||
return minutesDiff ~/ 5;
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> getDecredWordList() => wordlist;
|
||||
|
||||
@override
|
||||
String pubkey(Object wallet) {
|
||||
final decredWallet = wallet as DecredWallet;
|
||||
return decredWallet.pubkey;
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -69,6 +69,7 @@ import 'package:cake_wallet/haven/haven.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
@@ -990,7 +991,8 @@ Future<void> setup({
|
||||
(Node? editingNode, bool? isSelected) => NodeCreateOrEditPage(
|
||||
nodeCreateOrEditViewModel: getIt.get<NodeCreateOrEditViewModel>(param2: false),
|
||||
editingNode: editingNode,
|
||||
isSelected: isSelected));
|
||||
isSelected: isSelected,
|
||||
type: getIt.get<AppStore>().wallet!.type));
|
||||
|
||||
getIt.registerFactoryParam<PowNodeCreateOrEditPage, Node?, bool?>(
|
||||
(Node? editingNode, bool? isSelected) => PowNodeCreateOrEditPage(
|
||||
@@ -1115,6 +1117,8 @@ Future<void> setup({
|
||||
return wownero!.createWowneroWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||
case WalletType.zano:
|
||||
return zano!.createZanoWalletService(_walletInfoSource);
|
||||
case WalletType.decred:
|
||||
return decred!.createDecredWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService');
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -59,6 +59,7 @@ class MainActions {
|
||||
static MainActions sendAction = MainActions._(
|
||||
name: (context) => S.of(context).send,
|
||||
image: 'assets/images/upload.png',
|
||||
isEnabled: (viewModel) => viewModel.canSend,
|
||||
onTap: (BuildContext context, DashboardViewModel viewModel) async {
|
||||
Navigator.pushNamed(context, Routes.send);
|
||||
},
|
||||
|
||||
+66
-196
@@ -4,111 +4,62 @@ import "package:yaml/yaml.dart";
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
Future<List<Node>> loadDefaultNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/node_list.yml');
|
||||
Future<List<Node>> loadDefaultNodes(WalletType type) async {
|
||||
String path;
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
path = 'assets/node_list.yml';
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
path = 'assets/bitcoin_electrum_server_list.yml';
|
||||
break;
|
||||
case WalletType.litecoin:
|
||||
path = 'assets/litecoin_electrum_server_list.yml';
|
||||
break;
|
||||
case WalletType.haven:
|
||||
path = 'assets/haven_node_list.yml';
|
||||
break;
|
||||
case WalletType.ethereum:
|
||||
path = 'assets/ethereum_server_list.yml';
|
||||
break;
|
||||
case WalletType.nano:
|
||||
path = 'assets/nano_node_list.yml';
|
||||
break;
|
||||
case WalletType.bitcoinCash:
|
||||
path = 'assets/bitcoin_cash_electrum_server_list.yml';
|
||||
break;
|
||||
case WalletType.polygon:
|
||||
path = 'assets/polygon_node_list.yml';
|
||||
break;
|
||||
case WalletType.solana:
|
||||
path = 'assets/solana_node_list.yml';
|
||||
break;
|
||||
case WalletType.tron:
|
||||
path = 'assets/tron_node_list.yml';
|
||||
break;
|
||||
case WalletType.wownero:
|
||||
path = 'assets/wownero_node_list.yml';
|
||||
break;
|
||||
case WalletType.zano:
|
||||
path = 'assets/zano_node_list.yml';
|
||||
break;
|
||||
case WalletType.decred:
|
||||
path = 'assets/decred_node_list.yml';
|
||||
break;
|
||||
case WalletType.banano:
|
||||
case WalletType.none:
|
||||
path = '';
|
||||
break;
|
||||
}
|
||||
|
||||
final nodesRaw = await rootBundle.loadString(path);
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
node.type = WalletType.monero;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadBitcoinElectrumServerList() async {
|
||||
final serverListRaw = await rootBundle.loadString('assets/bitcoin_electrum_server_list.yml');
|
||||
final loadedServerList = loadYaml(serverListRaw) as YamlList;
|
||||
final serverList = <Node>[];
|
||||
|
||||
for (final raw in loadedServerList) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
node.type = WalletType.bitcoin;
|
||||
serverList.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return serverList;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadLitecoinElectrumServerList() async {
|
||||
final serverListRaw = await rootBundle.loadString('assets/litecoin_electrum_server_list.yml');
|
||||
final loadedServerList = loadYaml(serverListRaw) as YamlList;
|
||||
final serverList = <Node>[];
|
||||
|
||||
for (final raw in loadedServerList) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
node.type = WalletType.litecoin;
|
||||
serverList.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return serverList;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultHavenNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/haven_node_list.yml');
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
node.type = WalletType.haven;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultEthereumNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/ethereum_server_list.yml');
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
node.type = WalletType.ethereum;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadBitcoinCashElectrumServerList() async {
|
||||
final serverListRaw = await rootBundle.loadString('assets/bitcoin_cash_electrum_server_list.yml');
|
||||
final loadedServerList = loadYaml(serverListRaw) as YamlList;
|
||||
final serverList = <Node>[];
|
||||
|
||||
for (final raw in loadedServerList) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
node.type = WalletType.bitcoinCash;
|
||||
serverList.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return serverList;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultNanoNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/nano_node_list.yml');
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
node.type = WalletType.nano;
|
||||
node.type = type;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
@@ -132,103 +83,19 @@ Future<List<Node>> loadDefaultNanoPowNodes() async {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultPolygonNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/polygon_node_list.yml');
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
|
||||
node.type = WalletType.polygon;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultSolanaNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/solana_node_list.yml');
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
|
||||
node.type = WalletType.solana;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultTronNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/tron_node_list.yml');
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
|
||||
node.type = WalletType.tron;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultWowneroNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/wownero_node_list.yml');
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
|
||||
node.type = WalletType.wownero;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultZanoNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/zano_node_list.yml');
|
||||
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
|
||||
node.type = WalletType.zano;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
final moneroNodes = await loadDefaultNodes();
|
||||
final bitcoinElectrumServerList = await loadBitcoinElectrumServerList();
|
||||
final litecoinElectrumServerList = await loadLitecoinElectrumServerList();
|
||||
final bitcoinCashElectrumServerList = await loadBitcoinCashElectrumServerList();
|
||||
final havenNodes = await loadDefaultHavenNodes();
|
||||
final ethereumNodes = await loadDefaultEthereumNodes();
|
||||
final nanoNodes = await loadDefaultNanoNodes();
|
||||
final polygonNodes = await loadDefaultPolygonNodes();
|
||||
final solanaNodes = await loadDefaultSolanaNodes();
|
||||
final tronNodes = await loadDefaultTronNodes();
|
||||
final zanoNodes = await loadDefaultZanoNodes();
|
||||
final moneroNodes = await loadDefaultNodes(WalletType.monero);
|
||||
final bitcoinElectrumServerList = await loadDefaultNodes(WalletType.bitcoin);
|
||||
final litecoinElectrumServerList = await loadDefaultNodes(WalletType.litecoin);
|
||||
final bitcoinCashElectrumServerList = await loadDefaultNodes(WalletType.bitcoinCash);
|
||||
final havenNodes = await loadDefaultNodes(WalletType.haven);
|
||||
final ethereumNodes = await loadDefaultNodes(WalletType.ethereum);
|
||||
final nanoNodes = await loadDefaultNodes(WalletType.nano);
|
||||
final polygonNodes = await loadDefaultNodes(WalletType.polygon);
|
||||
final solanaNodes = await loadDefaultNodes(WalletType.solana);
|
||||
final tronNodes = await loadDefaultNodes(WalletType.tron);
|
||||
final decredNodes = await loadDefaultNodes(WalletType.decred);
|
||||
final zanoNodes = await loadDefaultNodes(WalletType.zano);
|
||||
|
||||
final nodes = moneroNodes +
|
||||
bitcoinElectrumServerList +
|
||||
@@ -238,7 +105,10 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
bitcoinCashElectrumServerList +
|
||||
nanoNodes +
|
||||
polygonNodes +
|
||||
solanaNodes + tronNodes + zanoNodes;
|
||||
solanaNodes +
|
||||
tronNodes +
|
||||
zanoNodes +
|
||||
decredNodes;
|
||||
|
||||
await nodeSource.clear();
|
||||
await nodeSource.addAll(nodes);
|
||||
|
||||
@@ -10,6 +10,7 @@ class PreferencesKey {
|
||||
static const currentPolygonNodeIdKey = 'current_node_id_matic';
|
||||
static const currentNanoNodeIdKey = 'current_node_id_nano';
|
||||
static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
|
||||
static const currentDecredNodeIdKey = 'current_node_id_decred';
|
||||
static const currentBananoNodeIdKey = 'current_node_id_banano';
|
||||
static const currentBananoPowNodeIdKey = 'current_node_id_banano_pow';
|
||||
static const currentFiatCurrencyKey = 'current_fiat_currency';
|
||||
@@ -48,6 +49,7 @@ class PreferencesKey {
|
||||
static const bitcoinCashTransactionPriority = 'current_fee_priority_bitcoin_cash';
|
||||
static const zanoTransactionPriority = 'current_fee_priority_zano';
|
||||
static const wowneroTransactionPriority = 'current_fee_priority_wownero';
|
||||
static const decredTransactionPriority = 'current_fee_priority_decred';
|
||||
static const customBitcoinFeeRate = 'custom_electrum_fee_rate';
|
||||
static const silentPaymentsCardDisplay = 'silentPaymentsCardDisplay';
|
||||
static const silentPaymentsAlwaysScan = 'silentPaymentsAlwaysScan';
|
||||
@@ -81,6 +83,7 @@ class PreferencesKey {
|
||||
static const lookupsENS = 'looks_up_ens';
|
||||
static const lookupsWellKnown = 'looks_up_well_known';
|
||||
static const showCameraConsent = 'show_camera_consent';
|
||||
static const showDecredInfoCard = 'show_decred_info_card';
|
||||
|
||||
static String moneroWalletUpdateV1Key(String name) =>
|
||||
'${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}';
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
@@ -35,6 +36,8 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
|
||||
return [];
|
||||
case WalletType.zano:
|
||||
return zano!.getTransactionPriorities();
|
||||
case WalletType.decred:
|
||||
return decred!.getTransactionPriorities();
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ class ProvidersHelper {
|
||||
ProviderType.moonpay,
|
||||
ProviderType.kriptonim
|
||||
];
|
||||
case WalletType.decred:
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.zano:
|
||||
@@ -113,6 +114,7 @@ class ProvidersHelper {
|
||||
];
|
||||
case WalletType.monero:
|
||||
return [ProviderType.dfx];
|
||||
case WalletType.decred:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.none:
|
||||
|
||||
+1
-1
@@ -215,7 +215,7 @@ Future<void> initializeAppConfigs() async {
|
||||
secureStorage: secureStorage,
|
||||
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
||||
havenSeedStore: havenSeedStore,
|
||||
initialMigrationVersion: 48,
|
||||
initialMigrationVersion: 49,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ bool isBIP39Wallet(WalletType walletType) {
|
||||
case WalletType.wownero:
|
||||
case WalletType.haven:
|
||||
case WalletType.zano:
|
||||
case WalletType.decred:
|
||||
case WalletType.none:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ void startCheckConnectionReaction(WalletBase wallet, SettingsStore settingsStore
|
||||
if (wallet.type == WalletType.bitcoin && wallet.syncStatus is SyncingSyncStatus) {
|
||||
return;
|
||||
}
|
||||
if (wallet.type == WalletType.decred && wallet.syncStatus is ProcessingSyncStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final connectivityResult = await (Connectivity().checkConnectivity());
|
||||
|
||||
@@ -10,7 +10,6 @@ import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
@@ -74,7 +74,8 @@ void startCurrentWalletChangeReaction(
|
||||
wallet.type == WalletType.wownero ||
|
||||
wallet.type == WalletType.bitcoin ||
|
||||
wallet.type == WalletType.litecoin ||
|
||||
wallet.type == WalletType.bitcoinCash) {
|
||||
wallet.type == WalletType.bitcoinCash ||
|
||||
wallet.type == WalletType.decred) {
|
||||
_setAutoGenerateSubaddressStatus(wallet, settingsStore);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ void startWalletSyncStatusChangeReaction(
|
||||
await updateHavenRate(fiatConversionStore);
|
||||
}
|
||||
}
|
||||
if (status is SyncingSyncStatus) {
|
||||
if (status is SyncingSyncStatus || status is ProcessingSyncStatus) {
|
||||
await WakelockPlus.enable();
|
||||
}
|
||||
if (status is SyncedSyncStatus || status is FailedSyncStatus) {
|
||||
|
||||
@@ -47,6 +47,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||
final tronIcon = Image.asset('assets/images/trx_icon.png', height: 24, width: 24);
|
||||
final wowneroIcon = Image.asset('assets/images/wownero_icon.png', height: 24, width: 24);
|
||||
final zanoIcon = Image.asset('assets/images/zano_icon.png', height: 24, width: 24);
|
||||
final decredIcon = Image.asset('assets/images/decred_icon.png', height: 24, width: 24);
|
||||
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
|
||||
|
||||
Image _newWalletImage(BuildContext context) => Image.asset(
|
||||
@@ -181,6 +182,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||
return tronIcon;
|
||||
case WalletType.zano:
|
||||
return zanoIcon;
|
||||
case WalletType.decred:
|
||||
return decredIcon;
|
||||
default:
|
||||
return nonWalletTypeIcon;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/pages/balance/balance_row_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/info_card.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
|
||||
@@ -280,81 +281,50 @@ class CryptoBalanceWidget extends StatelessWidget {
|
||||
SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||
child: DashBoardRoundedCardWidget(
|
||||
marginV: 0,
|
||||
marginH: 0,
|
||||
customBorder: 30,
|
||||
child: InfoCard(
|
||||
title: S.of(context).litecoin_mweb,
|
||||
subTitle: S.of(context).litecoin_mweb_description,
|
||||
hint: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => launchUrl(
|
||||
Uri.parse("https://docs.cakewallet.com/cryptos/litecoin/#mweb"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
child: Text(
|
||||
S.of(context).learn_more,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'Lato',
|
||||
fontWeight: FontWeight.w400,
|
||||
color:
|
||||
Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
|
||||
height: 1,
|
||||
),
|
||||
softWrap: true,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _dismissMweb(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
),
|
||||
child: Text(
|
||||
S.of(context).litecoin_mweb_dismiss,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _enableMweb(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
),
|
||||
child: Text(
|
||||
S.of(context).enable,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
icon: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
description: S.of(context).litecoin_mweb_description,
|
||||
leftButtonTitle: S.of(context).litecoin_mweb_dismiss,
|
||||
rightButtonTitle: S.of(context).enable,
|
||||
image: 'assets/images/mweb_logo.png',
|
||||
leftButtonAction: () => _dismissMweb(context),
|
||||
rightButtonAction: () => _enableMweb(context),
|
||||
hintWidget: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => launchUrl(
|
||||
Uri.parse("https://docs.cakewallet.com/cryptos/litecoin/#mweb"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
child: ImageIcon(
|
||||
AssetImage('assets/images/mweb_logo.png'),
|
||||
color: Color.fromARGB(255, 11, 70, 129),
|
||||
size: 40,
|
||||
child: Text(
|
||||
S.of(context).learn_more,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'Lato',
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
|
||||
height: 1,
|
||||
),
|
||||
softWrap: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (dashboardViewModel.showDecredInfoCard) ...[
|
||||
SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||
child: InfoCard(
|
||||
title: S.of(context).decred_info_title,
|
||||
description: S.of(context).decred_info_card_details,
|
||||
image: 'assets/images/dcr_icon.png',
|
||||
leftButtonTitle: S.of(context).litecoin_mweb_dismiss,
|
||||
rightButtonTitle: S.of(context).learn_more,
|
||||
leftButtonAction: () => dashboardViewModel.dismissDecredInfoCard(),
|
||||
rightButtonAction: () => launchUrl(Uri.parse("https://docs.cakewallet.com/cryptos/decred/#spv-sync")),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}),
|
||||
|
||||
@@ -75,8 +75,10 @@ class NavigationDock extends StatelessWidget {
|
||||
.labelTextColor,
|
||||
),
|
||||
title: action.name(context),
|
||||
onClick: () async =>
|
||||
await action.onTap(context, dashboardViewModel),
|
||||
onClick: (action.isEnabled?.call(dashboardViewModel) ?? true)
|
||||
? () async =>
|
||||
await action.onTap(context, dashboardViewModel)
|
||||
: null,
|
||||
textColor: action.isEnabled?.call(dashboardViewModel) ?? true
|
||||
? null
|
||||
: Theme.of(context)
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class InfoCard extends StatelessWidget {
|
||||
final String leftButtonTitle;
|
||||
final String rightButtonTitle;
|
||||
final String title;
|
||||
final String description;
|
||||
final String image;
|
||||
|
||||
final Function() leftButtonAction;
|
||||
final Function() rightButtonAction;
|
||||
|
||||
final Widget? hintWidget;
|
||||
|
||||
const InfoCard({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.leftButtonTitle,
|
||||
required this.rightButtonTitle,
|
||||
required this.leftButtonAction,
|
||||
required this.rightButtonAction,
|
||||
required this.image,
|
||||
this.hintWidget,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DashBoardRoundedCardWidget(
|
||||
marginH: 0,
|
||||
marginV: 0,
|
||||
customBorder: 30,
|
||||
title: title,
|
||||
subTitle: description,
|
||||
hint: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (hintWidget != null) hintWidget!,
|
||||
if (hintWidget != null) SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: leftButtonAction,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
),
|
||||
child: Text(
|
||||
leftButtonTitle,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: rightButtonAction,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
),
|
||||
child: Text(
|
||||
rightButtonTitle,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () => {},
|
||||
icon: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: CakeImageWidget(
|
||||
imageUrl: image,
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,8 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
this.solanaIcon = Image.asset('assets/images/sol_icon.png'),
|
||||
this.tronIcon = Image.asset('assets/images/trx_icon.png'),
|
||||
this.wowneroIcon = Image.asset('assets/images/wownero_icon.png'),
|
||||
this.zanoIcon = Image.asset('assets/images/zano_icon.png');
|
||||
this.zanoIcon = Image.asset('assets/images/zano_icon.png'),
|
||||
this.decredIcon = Image.asset('assets/images/decred_menu.png');
|
||||
|
||||
final largeScreen = 731;
|
||||
|
||||
@@ -64,6 +65,7 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
Image tronIcon;
|
||||
Image wowneroIcon;
|
||||
Image zanoIcon;
|
||||
Image decredIcon;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -250,6 +252,8 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
return wowneroIcon;
|
||||
case WalletType.zano:
|
||||
return zanoIcon;
|
||||
case WalletType.decred:
|
||||
return decredIcon;
|
||||
default:
|
||||
throw Exception('No icon for ${type.toString()}');
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ class SyncIndicator extends StatelessWidget {
|
||||
builder: (_) {
|
||||
final syncIndicatorWidth = 237.0;
|
||||
final status = dashboardViewModel.status;
|
||||
final statusText = status != null ? syncStatusTitle(status) : '';
|
||||
final progress = status != null ? status.progress() : 0.0;
|
||||
final statusText = syncStatusTitle(status);
|
||||
final progress = status.progress();
|
||||
final indicatorOffset = progress * syncIndicatorWidth;
|
||||
final indicatorWidth = progress < 1
|
||||
? indicatorOffset > 0 ? indicatorOffset : 0.0
|
||||
|
||||
@@ -274,7 +274,8 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
],
|
||||
);
|
||||
}),
|
||||
if (widget.privacySettingsViewModel.type == WalletType.bitcoin)
|
||||
if (widget.privacySettingsViewModel.type == WalletType.bitcoin ||
|
||||
widget.privacySettingsViewModel.type == WalletType.decred)
|
||||
Builder(builder: (_) {
|
||||
final val = testnetValue ?? false;
|
||||
return SettingsSwitcherCell(
|
||||
@@ -301,7 +302,9 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
|
||||
widget.nodeViewModel.save();
|
||||
}
|
||||
if (testnetValue == true) {
|
||||
if (testnetValue == true &&
|
||||
widget.privacySettingsViewModel.type ==
|
||||
WalletType.bitcoin) {
|
||||
// TODO: add type (mainnet/testnet) to Node class so when switching wallets the node can be switched to a matching type
|
||||
// Currently this is so you can create a working testnet wallet but you need to keep switching back the node if you use multiple wallets at once
|
||||
widget.nodeViewModel.address = publicBitcoinTestnetElectrumAddress;
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
@@ -15,7 +16,7 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
|
||||
class NodeCreateOrEditPage extends BasePage {
|
||||
NodeCreateOrEditPage({required this.nodeCreateOrEditViewModel,this.editingNode, this.isSelected})
|
||||
NodeCreateOrEditPage({required this.nodeCreateOrEditViewModel,this.editingNode, this.isSelected, this.type})
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_addressController = TextEditingController(),
|
||||
_pathController = TextEditingController(),
|
||||
@@ -86,6 +87,7 @@ class NodeCreateOrEditPage extends BasePage {
|
||||
final NodeCreateOrEditViewModel nodeCreateOrEditViewModel;
|
||||
final Node? editingNode;
|
||||
final bool? isSelected;
|
||||
final WalletType? type;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
@@ -130,6 +132,7 @@ class NodeCreateOrEditPage extends BasePage {
|
||||
formKey: _formKey,
|
||||
nodeViewModel: nodeCreateOrEditViewModel,
|
||||
editingNode: editingNode,
|
||||
type: type,
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
@@ -15,6 +16,7 @@ class NodeForm extends StatelessWidget {
|
||||
required this.nodeViewModel,
|
||||
required this.formKey,
|
||||
this.editingNode,
|
||||
this.type,
|
||||
}) : _addressController = TextEditingController(text: editingNode?.uri.host.toString()),
|
||||
_pathController = TextEditingController(text: editingNode?.path.toString()),
|
||||
_portController = TextEditingController(text: editingNode?.uri.port.toString()),
|
||||
@@ -76,6 +78,7 @@ class NodeForm extends StatelessWidget {
|
||||
final NodeCreateOrEditViewModel nodeViewModel;
|
||||
final GlobalKey<FormState> formKey;
|
||||
final Node? editingNode;
|
||||
final WalletType? type;
|
||||
|
||||
final TextEditingController _addressController;
|
||||
final TextEditingController _pathController;
|
||||
@@ -96,7 +99,7 @@ class NodeForm extends StatelessWidget {
|
||||
child: BaseTextFormField(
|
||||
controller: _addressController,
|
||||
hintText: S.of(context).node_address,
|
||||
validator: NodeAddressValidator(),
|
||||
validator: type == WalletType.decred ? NodeAddressValidatorDecredBlankException() : NodeAddressValidator(),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
||||
@@ -9,41 +9,47 @@ import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
class RescanPage extends BasePage {
|
||||
RescanPage(this._rescanViewModel)
|
||||
: _blockchainHeightWidgetKey = GlobalKey<BlockchainHeightState>();
|
||||
|
||||
@override
|
||||
String get title =>
|
||||
_rescanViewModel.isSilentPaymentsScan ? S.current.silent_payments_scanning : S.current.rescan;
|
||||
String get title => _rescanViewModel.isSilentPaymentsScan
|
||||
? S.current.silent_payments_scanning
|
||||
: S.current.rescan;
|
||||
final GlobalKey<BlockchainHeightState> _blockchainHeightWidgetKey;
|
||||
final RescanViewModel _rescanViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: Padding(
|
||||
Widget child;
|
||||
if (_rescanViewModel.wallet.type != WalletType.decred) {
|
||||
child = Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||
child:
|
||||
Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||
Observer(
|
||||
builder: (_) => BlockchainHeightWidget(
|
||||
key: _blockchainHeightWidgetKey,
|
||||
onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value,
|
||||
onHeightOrDateEntered: (value) =>
|
||||
_rescanViewModel.isButtonEnabled = value,
|
||||
isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan,
|
||||
isMwebScan: _rescanViewModel.isMwebScan,
|
||||
doSingleScan: _rescanViewModel.doSingleScan,
|
||||
hasDatePicker: !_rescanViewModel.isMwebScan,// disable date picker for mweb for now
|
||||
toggleSingleScan: () =>
|
||||
_rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan,
|
||||
hasDatePicker: !_rescanViewModel
|
||||
.isMwebScan, // disable date picker for mweb for now
|
||||
toggleSingleScan: () => _rescanViewModel.doSingleScan =
|
||||
!_rescanViewModel.doSingleScan,
|
||||
walletType: _rescanViewModel.wallet.type,
|
||||
bitcoinMempoolAPIEnabled: _rescanViewModel.isBitcoinMempoolAPIEnabled,
|
||||
bitcoinMempoolAPIEnabled:
|
||||
_rescanViewModel.isBitcoinMempoolAPIEnabled,
|
||||
)),
|
||||
Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
isLoading: _rescanViewModel.state == RescanWalletState.rescaning,
|
||||
isLoading:
|
||||
_rescanViewModel.state == RescanWalletState.rescaning,
|
||||
text: S.of(context).rescan,
|
||||
onPressed: () async {
|
||||
if (_rescanViewModel.isSilentPaymentsScan) {
|
||||
@@ -51,7 +57,8 @@ class RescanPage extends BasePage {
|
||||
}
|
||||
|
||||
_rescanViewModel.rescanCurrentWallet(
|
||||
restoreHeight: _blockchainHeightWidgetKey.currentState!.height);
|
||||
restoreHeight:
|
||||
_blockchainHeightWidgetKey.currentState!.height);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
@@ -60,7 +67,35 @@ class RescanPage extends BasePage {
|
||||
isDisabled: !_rescanViewModel.isButtonEnabled,
|
||||
))
|
||||
]),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
child = Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Spacer(),
|
||||
Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
isLoading: _rescanViewModel.state ==
|
||||
RescanWalletState.rescaning,
|
||||
text: S.of(context).rescan,
|
||||
onPressed: () async {
|
||||
await _rescanViewModel.rescanCurrentWallet(
|
||||
restoreHeight: 0);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white,
|
||||
))
|
||||
]),
|
||||
));
|
||||
}
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,14 +105,16 @@ class RescanPage extends BasePage {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
final needsToSwitch =
|
||||
await bitcoin!.getNodeIsElectrsSPEnabled(_rescanViewModel.wallet) == false;
|
||||
await bitcoin!.getNodeIsElectrsSPEnabled(_rescanViewModel.wallet) ==
|
||||
false;
|
||||
|
||||
if (needsToSwitch) {
|
||||
return showPopUp<void>(
|
||||
context: navigatorKey.currentState!.context,
|
||||
builder: (BuildContext _dialogContext) => AlertWithTwoActions(
|
||||
alertTitle: S.of(_dialogContext).change_current_node_title,
|
||||
alertContent: S.of(_dialogContext).confirm_silent_payments_switch_node,
|
||||
alertContent:
|
||||
S.of(_dialogContext).confirm_silent_payments_switch_node,
|
||||
rightButtonText: S.of(_dialogContext).confirm,
|
||||
leftButtonText: S.of(_dialogContext).cancel,
|
||||
actionRightButton: () async {
|
||||
|
||||
@@ -15,6 +15,7 @@ class WalletRestoreFromKeysForm extends StatefulWidget {
|
||||
WalletRestoreFromKeysForm({
|
||||
required this.walletRestoreViewModel,
|
||||
required this.onPrivateKeyChange,
|
||||
required this.onViewKeyEntered,
|
||||
required this.displayPrivateKeyField,
|
||||
required this.onHeightOrDateEntered,
|
||||
required this.displayWalletPassword,
|
||||
@@ -27,6 +28,7 @@ class WalletRestoreFromKeysForm extends StatefulWidget {
|
||||
final Function(bool) onHeightOrDateEntered;
|
||||
final WalletRestoreViewModel walletRestoreViewModel;
|
||||
final void Function(String)? onPrivateKeyChange;
|
||||
final void Function(bool)? onViewKeyEntered;
|
||||
final bool displayPrivateKeyField;
|
||||
final bool displayWalletPassword;
|
||||
final RestoredWallet? restoredWallet;
|
||||
@@ -97,6 +99,10 @@ class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
|
||||
blockchainHeightKey.currentState?.restoreHeightController.text = widget.restoredWallet!.height.toString();
|
||||
}
|
||||
});
|
||||
|
||||
viewKeyController.addListener(() {
|
||||
widget.onViewKeyEntered?.call(viewKeyController.text.isNotEmpty);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -187,6 +193,19 @@ class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
|
||||
}
|
||||
|
||||
Widget _restoreFromKeysFormFields() {
|
||||
// Decred can only restore a view only wallet with an account pubkey. Other
|
||||
// fields are not used.
|
||||
if (widget.walletRestoreViewModel.type == WalletType.decred) {
|
||||
return Column(
|
||||
children: [
|
||||
BaseTextFormField(
|
||||
controller: viewKeyController,
|
||||
hintText: S.of(context).view_key_public,
|
||||
maxLines: null,
|
||||
)],
|
||||
);
|
||||
}
|
||||
|
||||
if (widget.displayPrivateKeyField) {
|
||||
// the term "private key" isn't actually what we're accepting here, and it's confusing to
|
||||
// users of the nano community, what this form actually accepts (when importing for nano) is a nano seed in it's hex form, referred to in code as a "seed key"
|
||||
|
||||
@@ -168,14 +168,16 @@ class WalletRestorePage extends BasePage {
|
||||
credentials['name'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
|
||||
} else {
|
||||
credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
|
||||
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
|
||||
credentials['spendKey'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
|
||||
credentials['height'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
|
||||
credentials['name'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
|
||||
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
|
||||
if (walletRestoreViewModel.type != WalletType.decred) {
|
||||
credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
|
||||
credentials['spendKey'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
|
||||
credentials['height'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,6 +469,11 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
|
||||
widget.walletRestoreViewModel.isButtonEnabled = _isValidSeedKey();
|
||||
}
|
||||
},
|
||||
onViewKeyEntered: (bool entered) {
|
||||
if (walletRestoreViewModel.type == WalletType.decred) {
|
||||
walletRestoreViewModel.isButtonEnabled = entered;
|
||||
}
|
||||
},
|
||||
onPasswordChange: (String password) =>
|
||||
widget.walletRestoreViewModel.walletPassword = password,
|
||||
onRepeatedPasswordChange: (String repeatedPassword) =>
|
||||
@@ -538,13 +545,19 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
|
||||
|
||||
// bip39:
|
||||
final validBip39SeedLengths = [12, 18, 24];
|
||||
final nonBip39WalletTypes = [WalletType.monero, WalletType.wownero, WalletType.haven];
|
||||
final nonBip39WalletTypes = [WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.decred];
|
||||
// if it's a bip39 wallet and the length is not valid return false
|
||||
if (!nonBip39WalletTypes.contains(walletRestoreViewModel.type) &&
|
||||
!(validBip39SeedLengths.contains(seedWords.length))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((walletRestoreViewModel.type == WalletType.decred) &&
|
||||
seedWords.length !=
|
||||
WalletRestoreViewModelBase.decredSeedMnemonicLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final words =
|
||||
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet();
|
||||
return seedWords.toSet().difference(words).toSet().isEmpty;
|
||||
|
||||
@@ -72,6 +72,10 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
late bool showLegacySeedTab;
|
||||
late bool isLegacySeedOnly;
|
||||
|
||||
bool get _hasSeeds =>
|
||||
widget.walletKeysViewModel.legacySeedSplit.length > 10 ||
|
||||
widget.walletKeysViewModel.seedSplit.length > 10;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -160,11 +164,10 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
Widget _buildSeedTab(BuildContext context, bool isLegacySeed) {
|
||||
return Column(
|
||||
children: [
|
||||
if (isLegacySeedOnly || isLegacySeed)
|
||||
...[
|
||||
_buildHeightBox(),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
if (isLegacySeedOnly || isLegacySeed) ...[
|
||||
_buildHeightBox(),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
(_buildPassphraseBox() ?? Container()),
|
||||
if (widget.walletKeysViewModel.passphrase.isNotEmpty) const SizedBox(height: 20),
|
||||
Expanded(
|
||||
@@ -175,13 +178,14 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_buildBottomActionPanel(
|
||||
titleForClipboard: S.of(context).wallet_seed.toLowerCase(),
|
||||
dataToCopy: isLegacySeed
|
||||
? widget.walletKeysViewModel.legacySeed
|
||||
: widget.walletKeysViewModel.seed,
|
||||
onShowQR: () async => _showQR(context),
|
||||
),
|
||||
if (_hasSeeds)
|
||||
_buildBottomActionPanel(
|
||||
titleForClipboard: S.of(context).wallet_seed.toLowerCase(),
|
||||
dataToCopy: isLegacySeed
|
||||
? widget.walletKeysViewModel.legacySeed
|
||||
: widget.walletKeysViewModel.seed,
|
||||
onShowQR: () async => _showQR(context),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -326,7 +330,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildBottomActionPanel({
|
||||
required String titleForClipboard,
|
||||
required String dataToCopy,
|
||||
|
||||
@@ -122,6 +122,7 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
||||
final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24);
|
||||
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
|
||||
final decredIcon = Image.asset('assets/images/decred_icon.png', height: 24, width: 24);
|
||||
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
|
||||
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
|
||||
final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24);
|
||||
@@ -136,6 +137,8 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
final double tileHeight = 60;
|
||||
Flushbar<void>? _progressBar;
|
||||
|
||||
bool _loadingWallet = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final newWalletImage = Image.asset('assets/images/new_wallet.png',
|
||||
@@ -480,6 +483,10 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
}
|
||||
|
||||
Future<void> _loadWallet(WalletListItem wallet) async {
|
||||
if (_loadingWallet) return;
|
||||
|
||||
_loadingWallet = true;
|
||||
|
||||
if (SettingsStoreBase.walletPasswordDirectInput) {
|
||||
Navigator.of(context).pushNamed(Routes.walletUnlockLoadable,
|
||||
arguments: WalletUnlockArguments(
|
||||
@@ -492,13 +499,17 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
},
|
||||
walletName: wallet.name,
|
||||
walletType: wallet.type));
|
||||
_loadingWallet = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await widget.authService.authenticateAction(
|
||||
context,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (!isAuthenticatedSuccessfully) return;
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
_loadingWallet = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final requireHardwareWalletConnection = widget.walletListViewModel
|
||||
@@ -555,6 +566,8 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
.of(context)
|
||||
.wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||
}
|
||||
} finally {
|
||||
_loadingWallet = false;
|
||||
}
|
||||
},
|
||||
conditionToDetermineIfToUse2FA:
|
||||
|
||||
@@ -9,6 +9,9 @@ import 'package:intl/intl.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
|
||||
class BlockchainHeightWidget extends StatefulWidget {
|
||||
BlockchainHeightWidget({
|
||||
@@ -183,7 +186,9 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
||||
bitcoinMempoolAPIEnabled: await widget.bitcoinMempoolAPIEnabled,
|
||||
);
|
||||
} else {
|
||||
if (widget.walletType == WalletType.monero) {
|
||||
if (widget.walletType == WalletType.decred) {
|
||||
height = decred!.heightByDate(date);
|
||||
} else if (widget.walletType == WalletType.monero) {
|
||||
height = monero!.getHeightByDate(date: date);
|
||||
} else {
|
||||
assert(widget.walletType == WalletType.wownero,
|
||||
|
||||
@@ -65,6 +65,9 @@ class SeedWidgetState extends State<SeedWidget> {
|
||||
});
|
||||
widget.onSeedChange?.call(text);
|
||||
});
|
||||
Future.delayed(Duration.zero, () {
|
||||
widget.onSeedChange?.call(text);
|
||||
});
|
||||
}
|
||||
|
||||
void changeSeedLanguage(String language) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/core/secure_storage.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
@@ -139,6 +140,7 @@ abstract class SettingsStoreBase with Store {
|
||||
TransactionPriority? initialPolygonTransactionPriority,
|
||||
TransactionPriority? initialBitcoinCashTransactionPriority,
|
||||
TransactionPriority? initialZanoTransactionPriority,
|
||||
TransactionPriority? initialDecredTransactionPriority,
|
||||
Country? initialCakePayCountry})
|
||||
: nodes = ObservableMap<WalletType, Node>.of(nodes),
|
||||
powNodes = ObservableMap<WalletType, Node>.of(powNodes),
|
||||
@@ -225,6 +227,9 @@ abstract class SettingsStoreBase with Store {
|
||||
if (initialZanoTransactionPriority != null) {
|
||||
priority[WalletType.zano] = initialZanoTransactionPriority;
|
||||
}
|
||||
if (initialDecredTransactionPriority != null) {
|
||||
priority[WalletType.decred] = initialDecredTransactionPriority;
|
||||
}
|
||||
|
||||
if (initialCakePayCountry != null) {
|
||||
selectedCakePayCountry = initialCakePayCountry;
|
||||
@@ -280,6 +285,9 @@ abstract class SettingsStoreBase with Store {
|
||||
case WalletType.zano:
|
||||
key = PreferencesKey.zanoTransactionPriority;
|
||||
break;
|
||||
case WalletType.decred:
|
||||
key = PreferencesKey.decredTransactionPriority;
|
||||
break;
|
||||
default:
|
||||
key = null;
|
||||
}
|
||||
@@ -898,6 +906,7 @@ abstract class SettingsStoreBase with Store {
|
||||
TransactionPriority? bitcoinCashTransactionPriority;
|
||||
TransactionPriority? wowneroTransactionPriority;
|
||||
TransactionPriority? zanoTransactionPriority;
|
||||
TransactionPriority? decredTransactionPriority;
|
||||
|
||||
if (sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) {
|
||||
havenTransactionPriority = monero?.deserializeMoneroTransactionPriority(
|
||||
@@ -927,6 +936,10 @@ abstract class SettingsStoreBase with Store {
|
||||
zanoTransactionPriority = monero?.deserializeMoneroTransactionPriority(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.zanoTransactionPriority)!);
|
||||
}
|
||||
if (sharedPreferences.getInt(PreferencesKey.decredTransactionPriority) != null) {
|
||||
decredTransactionPriority = decred?.deserializeDecredTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.decredTransactionPriority)!);
|
||||
}
|
||||
|
||||
moneroTransactionPriority ??= monero?.getDefaultTransactionPriority();
|
||||
bitcoinTransactionPriority ??= bitcoin?.getMediumTransactionPriority();
|
||||
@@ -935,6 +948,7 @@ abstract class SettingsStoreBase with Store {
|
||||
ethereumTransactionPriority ??= ethereum?.getDefaultTransactionPriority();
|
||||
bitcoinCashTransactionPriority ??= bitcoinCash?.getDefaultTransactionPriority();
|
||||
wowneroTransactionPriority ??= wownero?.getDefaultTransactionPriority();
|
||||
decredTransactionPriority ??= decred?.getDecredTransactionPriorityMedium();
|
||||
polygonTransactionPriority ??= polygon?.getDefaultTransactionPriority();
|
||||
zanoTransactionPriority ??= zano?.getDefaultTransactionPriority();
|
||||
|
||||
@@ -1038,7 +1052,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final tronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey);
|
||||
final wowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
|
||||
final zanoNodeId = sharedPreferences.getInt(PreferencesKey.currentZanoNodeIdKey);
|
||||
|
||||
final decredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
|
||||
final moneroNode = nodeSource.get(nodeId);
|
||||
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
|
||||
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
|
||||
@@ -1047,6 +1061,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final polygonNode = nodeSource.get(polygonNodeId);
|
||||
final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId);
|
||||
final nanoNode = nodeSource.get(nanoNodeId);
|
||||
final decredNode = nodeSource.get(decredNodeId);
|
||||
final nanoPowNode = powNodeSource.get(nanoPowNodeId);
|
||||
final solanaNode = nodeSource.get(solanaNodeId);
|
||||
final tronNode = nodeSource.get(tronNodeId);
|
||||
@@ -1137,6 +1152,10 @@ abstract class SettingsStoreBase with Store {
|
||||
nodes[WalletType.zano] = zanoNode;
|
||||
}
|
||||
|
||||
if (decredNode != null) {
|
||||
nodes[WalletType.decred] = decredNode;
|
||||
}
|
||||
|
||||
final savedSyncMode = SyncMode.all.firstWhere((element) {
|
||||
return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 0);
|
||||
});
|
||||
@@ -1304,6 +1323,7 @@ abstract class SettingsStoreBase with Store {
|
||||
initialHavenTransactionPriority: havenTransactionPriority,
|
||||
initialLitecoinTransactionPriority: litecoinTransactionPriority,
|
||||
initialBitcoinCashTransactionPriority: bitcoinCashTransactionPriority,
|
||||
initialDecredTransactionPriority: decredTransactionPriority,
|
||||
initialShouldRequireTOTP2FAForAccessingWallet: shouldRequireTOTP2FAForAccessingWallet,
|
||||
initialShouldRequireTOTP2FAForSendsToContact: shouldRequireTOTP2FAForSendsToContact,
|
||||
initialShouldRequireTOTP2FAForSendsToNonContact: shouldRequireTOTP2FAForSendsToNonContact,
|
||||
@@ -1378,6 +1398,11 @@ abstract class SettingsStoreBase with Store {
|
||||
priority[WalletType.zano] = zano!.deserializeMoneroTransactionPriority(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.zanoTransactionPriority)!);
|
||||
}
|
||||
if (decred != null &&
|
||||
sharedPreferences.getInt(PreferencesKey.decredTransactionPriority) != null) {
|
||||
priority[WalletType.decred] = decred!.deserializeDecredTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.decredTransactionPriority)!);
|
||||
}
|
||||
|
||||
final generateSubaddresses =
|
||||
sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey);
|
||||
@@ -1489,6 +1514,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final tronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey);
|
||||
final wowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
|
||||
final zanoNodeId = sharedPreferences.getInt(PreferencesKey.currentZanoNodeIdKey);
|
||||
final decredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
|
||||
final moneroNode = nodeSource.get(nodeId);
|
||||
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
|
||||
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
|
||||
@@ -1496,11 +1522,12 @@ abstract class SettingsStoreBase with Store {
|
||||
final ethereumNode = nodeSource.get(ethereumNodeId);
|
||||
final polygonNode = nodeSource.get(polygonNodeId);
|
||||
final bitcoinCashNode = nodeSource.get(bitcoinCashElectrumServerId);
|
||||
final nanoNode = nodeSource.get(nanoNodeId);
|
||||
final nanoNode = nodeSource.get(nanoNodeId);
|
||||
final solanaNode = nodeSource.get(solanaNodeId);
|
||||
final tronNode = nodeSource.get(tronNodeId);
|
||||
final wowneroNode = nodeSource.get(wowneroNodeId);
|
||||
final zanoNode = nodeSource.get(zanoNodeId);
|
||||
final decredNode = nodeSource.get(decredNodeId);
|
||||
|
||||
if (moneroNode != null) {
|
||||
nodes[WalletType.monero] = moneroNode;
|
||||
@@ -1551,6 +1578,10 @@ abstract class SettingsStoreBase with Store {
|
||||
nodes[WalletType.zano] = zanoNode;
|
||||
}
|
||||
|
||||
if (decredNode != null) {
|
||||
nodes[WalletType.decred] = decredNode;
|
||||
}
|
||||
|
||||
// MIGRATED:
|
||||
|
||||
useTOTP2FA = await SecureKey.getBool(
|
||||
@@ -1687,6 +1718,9 @@ abstract class SettingsStoreBase with Store {
|
||||
case WalletType.wownero:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentWowneroNodeIdKey, node.key as int);
|
||||
break;
|
||||
case WalletType.decred:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentDecredNodeIdKey, node.key as int);
|
||||
break;
|
||||
case WalletType.zano:
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentZanoNodeIdKey, node.key as int);
|
||||
default:
|
||||
|
||||
@@ -55,6 +55,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.zano:
|
||||
case WalletType.decred:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import 'package:cake_wallet/entities/wallet_list_order_types.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
@@ -43,7 +42,8 @@ abstract class ContactListViewModelBase with Store {
|
||||
}
|
||||
}
|
||||
} else if (info.addresses?.isNotEmpty == true && info.addresses!.length > 1) {
|
||||
if ([WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type)) {
|
||||
if ([WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.decred]
|
||||
.contains(info.type)) {
|
||||
final address = info.address;
|
||||
final name = _createName(info.name, "", key: 0);
|
||||
walletContacts.add(WalletContact(
|
||||
@@ -129,11 +129,9 @@ abstract class ContactListViewModelBase with Store {
|
||||
(element.type == CryptoCurrency.btc || element.type == CryptoCurrency.ltc)) return false;
|
||||
|
||||
return element.type == _currency ||
|
||||
(element.type.tag != null &&
|
||||
_currency?.tag != null &&
|
||||
element.type.tag == _currency?.tag) ||
|
||||
_currency?.toString() == element.type.tag ||
|
||||
_currency?.tag == element.type.toString();
|
||||
(element.type.tag != null && _currency.tag != null && element.type.tag == _currency.tag) ||
|
||||
_currency.toString() == element.type.tag ||
|
||||
_currency.tag == element.type.toString();
|
||||
}
|
||||
|
||||
void dispose() => _subscription?.cancel();
|
||||
|
||||
@@ -62,12 +62,12 @@ abstract class ContactViewModelBase with Store {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_contact != null && _contact!.original.isInBox) {
|
||||
_contact?.name = name;
|
||||
_contact?.address = address;
|
||||
_contact?.type = currency!;
|
||||
_contact?.lastChange = now;
|
||||
await _contact?.save();
|
||||
if (_contact != null && _contact.original.isInBox) {
|
||||
_contact.name = name;
|
||||
_contact.address = address;
|
||||
_contact.type = currency!;
|
||||
_contact.lastChange = now;
|
||||
await _contact.save();
|
||||
} else {
|
||||
await _contacts
|
||||
.add(Contact(name: name, address: address, type: currency!, lastChange: now));
|
||||
|
||||
@@ -306,6 +306,7 @@ abstract class BalanceViewModelBase with Store {
|
||||
case WalletType.monero:
|
||||
case WalletType.wownero:
|
||||
case WalletType.zano:
|
||||
case WalletType.decred:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -23,7 +23,6 @@ import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||
@@ -169,6 +168,9 @@ abstract class DashboardViewModelBase with Store {
|
||||
type = appStore.wallet!.type,
|
||||
transactions = ObservableList<TransactionListItem>(),
|
||||
wallet = appStore.wallet! {
|
||||
showDecredInfoCard = wallet.type == WalletType.decred &&
|
||||
(sharedPreferences.getBool(PreferencesKey.showDecredInfoCard) ?? true);
|
||||
|
||||
name = wallet.name;
|
||||
type = wallet.type;
|
||||
isShowFirstYatIntroduction = false;
|
||||
@@ -261,6 +263,7 @@ abstract class DashboardViewModelBase with Store {
|
||||
reaction((_) => appStore.wallet, (wallet) {
|
||||
_onWalletChange(wallet);
|
||||
_checkMweb();
|
||||
showDecredInfoCard = wallet?.type == WalletType.decred;
|
||||
});
|
||||
|
||||
_transactionDisposer?.reaction.dispose();
|
||||
@@ -350,6 +353,10 @@ abstract class DashboardViewModelBase with Store {
|
||||
statusText = S.current.please_try_to_connect_to_another_node;
|
||||
}
|
||||
|
||||
if (status is ProcessingSyncStatus) {
|
||||
statusText = (status as ProcessingSyncStatus).message ?? S.current.processing;
|
||||
}
|
||||
|
||||
return statusText;
|
||||
}
|
||||
|
||||
@@ -399,13 +406,7 @@ abstract class DashboardViewModelBase with Store {
|
||||
bool get isTestnet => wallet.type == WalletType.bitcoin && bitcoin!.isTestnet(wallet);
|
||||
|
||||
@computed
|
||||
bool get hasRescan => [
|
||||
WalletType.bitcoin,
|
||||
WalletType.monero,
|
||||
WalletType.litecoin,
|
||||
WalletType.wownero,
|
||||
WalletType.haven
|
||||
].contains(wallet.type);
|
||||
bool get hasRescan => wallet.hasRescan;
|
||||
|
||||
@computed
|
||||
bool get isMoneroViewOnly {
|
||||
@@ -490,6 +491,9 @@ abstract class DashboardViewModelBase with Store {
|
||||
@observable
|
||||
bool mwebEnabled = false;
|
||||
|
||||
@observable
|
||||
late bool showDecredInfoCard;
|
||||
|
||||
@computed
|
||||
bool get hasEnabledMwebBefore => settingsStore.hasEnabledMwebBefore;
|
||||
|
||||
@@ -543,6 +547,12 @@ abstract class DashboardViewModelBase with Store {
|
||||
bitcoin!.setMwebEnabled(wallet, false);
|
||||
}
|
||||
|
||||
@action
|
||||
void dismissDecredInfoCard() {
|
||||
showDecredInfoCard = false;
|
||||
sharedPreferences.setBool(PreferencesKey.showDecredInfoCard, false);
|
||||
}
|
||||
|
||||
BalanceViewModel balanceViewModel;
|
||||
|
||||
AppStore appStore;
|
||||
@@ -573,6 +583,9 @@ abstract class DashboardViewModelBase with Store {
|
||||
@computed
|
||||
bool get isEnabledSwapAction => settingsStore.exchangeStatus != ExchangeApiMode.disabled;
|
||||
|
||||
@computed
|
||||
bool get canSend => wallet.canSend();
|
||||
|
||||
@observable
|
||||
bool hasSwapAction;
|
||||
|
||||
@@ -596,21 +609,28 @@ abstract class DashboardViewModelBase with Store {
|
||||
|
||||
@computed
|
||||
bool get hasSignMessages {
|
||||
if (wallet.isHardwareWallet) return false;
|
||||
|
||||
return [
|
||||
WalletType.monero,
|
||||
WalletType.litecoin,
|
||||
WalletType.bitcoin,
|
||||
WalletType.bitcoinCash,
|
||||
WalletType.ethereum,
|
||||
WalletType.polygon,
|
||||
WalletType.solana,
|
||||
WalletType.nano,
|
||||
WalletType.banano,
|
||||
WalletType.tron,
|
||||
WalletType.wownero
|
||||
].contains(wallet.type);
|
||||
if (wallet.isHardwareWallet) {
|
||||
return false;
|
||||
}
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.solana:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.tron:
|
||||
case WalletType.wownero:
|
||||
case WalletType.decred:
|
||||
return true;
|
||||
case WalletType.zano:
|
||||
case WalletType.haven:
|
||||
case WalletType.none:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool get showRepWarning {
|
||||
|
||||
@@ -214,6 +214,7 @@ abstract class HomeSettingsViewModelBase with Store {
|
||||
case WalletType.nano:
|
||||
case WalletType.wownero:
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.decred:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ abstract class ReceiveOptionViewModelBase with Store {
|
||||
(_wallet.type == WalletType.bitcoin ||
|
||||
_wallet.type == WalletType.litecoin
|
||||
? bitcoin!.getSelectedAddressType(_wallet)
|
||||
: ReceivePageOption.mainnet),
|
||||
: (_wallet.type == WalletType.decred && _wallet.isTestnet)
|
||||
? ReceivePageOption.testnet
|
||||
: ReceivePageOption.mainnet),
|
||||
_options = [] {
|
||||
final walletType = _wallet.type;
|
||||
switch (walletType) {
|
||||
@@ -33,6 +35,17 @@ abstract class ReceiveOptionViewModelBase with Store {
|
||||
case WalletType.haven:
|
||||
_options = [ReceivePageOption.mainnet];
|
||||
break;
|
||||
case WalletType.decred:
|
||||
if (_wallet.isTestnet) {
|
||||
_options = [
|
||||
ReceivePageOption.testnet,
|
||||
...ReceivePageOptions.where(
|
||||
(element) => element != ReceivePageOption.mainnet)
|
||||
];
|
||||
} else {
|
||||
_options = ReceivePageOptions;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_options = ReceivePageOptions;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
@@ -82,7 +83,9 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
if (transaction.confirmations <= 0) {
|
||||
str = S.current.pending;
|
||||
}
|
||||
if ((isPegOut || fromPegOut) && transaction.confirmations >= 0 && transaction.confirmations < 6) {
|
||||
if ((isPegOut || fromPegOut) &&
|
||||
transaction.confirmations >= 0 &&
|
||||
transaction.confirmations < 6) {
|
||||
str = " (${transaction.confirmations}/6)";
|
||||
}
|
||||
if (isPegIn) {
|
||||
@@ -224,7 +227,13 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
cryptoAmount: zano!.formatterIntAmountToDouble(amount: transaction.amount, currency: asset, forFee: false),
|
||||
price: price);
|
||||
break;
|
||||
default:
|
||||
case WalletType.decred:
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: decred!.formatterDecredAmountToDouble(amount: transaction.amount),
|
||||
price: price);
|
||||
break;
|
||||
case WalletType.none:
|
||||
case WalletType.banano:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
@@ -167,7 +169,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
wallet.type == WalletType.bitcoinCash;
|
||||
|
||||
bool get hideAddressAfterExchange =>
|
||||
wallet.type == WalletType.monero || wallet.type == WalletType.wownero;
|
||||
wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.wownero;
|
||||
|
||||
bool _useTorOnly;
|
||||
final Box<Trade> trades;
|
||||
@@ -316,8 +319,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
bool get isMoneroWallet => wallet.type == WalletType.monero;
|
||||
|
||||
|
||||
|
||||
List<CryptoCurrency> receiveCurrencies;
|
||||
|
||||
List<CryptoCurrency> depositCurrencies;
|
||||
@@ -778,6 +779,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
depositCurrency = CryptoCurrency.zano;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
break;
|
||||
case WalletType.decred:
|
||||
depositCurrency = CryptoCurrency.dcr;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
break;
|
||||
case WalletType.none:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -63,13 +63,13 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||
String socksProxyAddress;
|
||||
|
||||
@computed
|
||||
bool get isReady => address.isNotEmpty && port.isNotEmpty;
|
||||
bool get isReady =>
|
||||
(address.isNotEmpty && port.isNotEmpty) ||
|
||||
_walletType == WalletType.decred; // Allow an empty address.
|
||||
|
||||
bool get hasAuthCredentials =>
|
||||
_walletType == WalletType.monero || _walletType == WalletType.wownero || _walletType == WalletType.haven;
|
||||
|
||||
bool get hasTestnetSupport => _walletType == WalletType.bitcoin;
|
||||
|
||||
bool get hasPathSupport {
|
||||
switch (_walletType) {
|
||||
case WalletType.ethereum:
|
||||
@@ -87,6 +87,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.zano:
|
||||
case WalletType.decred:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,50 +49,10 @@ abstract class NodeListViewModelBase with Store {
|
||||
await resetToDefault(_nodeSource);
|
||||
|
||||
Node node;
|
||||
|
||||
switch (_appStore.wallet!.type) {
|
||||
case WalletType.bitcoin:
|
||||
if (_appStore.wallet!.isTestnet) {
|
||||
node = getBitcoinTestnetDefaultElectrumServer(nodes: _nodeSource)!;
|
||||
} else {
|
||||
node = getBitcoinDefaultElectrumServer(nodes: _nodeSource)!;
|
||||
}
|
||||
break;
|
||||
case WalletType.monero:
|
||||
node = getMoneroDefaultNode(nodes: _nodeSource);
|
||||
break;
|
||||
case WalletType.litecoin:
|
||||
node = getLitecoinDefaultElectrumServer(nodes: _nodeSource)!;
|
||||
break;
|
||||
case WalletType.haven:
|
||||
node = getHavenDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
case WalletType.ethereum:
|
||||
node = getEthereumDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
case WalletType.bitcoinCash:
|
||||
node = getBitcoinCashDefaultElectrumServer(nodes: _nodeSource)!;
|
||||
break;
|
||||
case WalletType.nano:
|
||||
node = getNanoDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
case WalletType.polygon:
|
||||
node = getPolygonDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
case WalletType.solana:
|
||||
node = getSolanaDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
case WalletType.tron:
|
||||
node = getTronDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
case WalletType.wownero:
|
||||
node = getWowneroDefaultNode(nodes: _nodeSource);
|
||||
break;
|
||||
case WalletType.zano:
|
||||
node = getZanoDefaultNode(nodes: _nodeSource)!;
|
||||
break;
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}');
|
||||
if (_appStore.wallet!.type == WalletType.bitcoin && _appStore.wallet!.isTestnet) {
|
||||
node = getBitcoinTestnetDefaultElectrumServer(nodes: _nodeSource)!;
|
||||
} else {
|
||||
node = getDefaultNode(nodes: _nodeSource, type: _appStore.wallet!.type)!;
|
||||
}
|
||||
|
||||
await setAsCurrent(node);
|
||||
|
||||
@@ -44,6 +44,9 @@ class WalletRestoreFromQRCode {
|
||||
'zano': WalletType.zano,
|
||||
'zano-wallet': WalletType.zano,
|
||||
'zano_wallet': WalletType.zano,
|
||||
'decred': WalletType.decred,
|
||||
'decred-wallet': WalletType.decred,
|
||||
'decred_wallet': WalletType.decred,
|
||||
};
|
||||
|
||||
static WalletType? _extractWalletType(String code) {
|
||||
@@ -69,8 +72,12 @@ class WalletRestoreFromQRCode {
|
||||
}
|
||||
|
||||
static String? _extractAddressFromUrl(String rawString, WalletType type) {
|
||||
return AddressResolver.extractAddressByType(
|
||||
raw: rawString, type: walletTypeToCryptoCurrency(type));
|
||||
try {
|
||||
return AddressResolver.extractAddressByType(
|
||||
raw: rawString, type: walletTypeToCryptoCurrency(type));
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static String? _extractSeedPhraseFromUrl(String rawString, WalletType walletType) {
|
||||
@@ -122,7 +129,6 @@ class WalletRestoreFromQRCode {
|
||||
}
|
||||
if (queryParameters['address'] == null) {
|
||||
queryParameters['address'] = _extractAddressFromUrl(code, walletType);
|
||||
|
||||
}
|
||||
|
||||
Map<String, dynamic> credentials = {'type': walletType, ...queryParameters, 'raw_qr': code};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
@@ -88,7 +89,13 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
return transactionPriority == bitcoinCash!.getBitcoinCashTransactionPrioritySlow();
|
||||
case WalletType.polygon:
|
||||
return transactionPriority == polygon!.getPolygonTransactionPrioritySlow();
|
||||
default:
|
||||
case WalletType.decred:
|
||||
return transactionPriority == decred!.getDecredTransactionPrioritySlow();
|
||||
case WalletType.none:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
@@ -101,6 +102,9 @@ abstract class OutputBase with Store {
|
||||
case WalletType.bitcoinCash:
|
||||
_amount = bitcoin!.formatterStringDoubleToBitcoinAmount(_cryptoAmount);
|
||||
break;
|
||||
case WalletType.decred:
|
||||
_amount = decred!.formatterStringDoubleToDecredAmount(_cryptoAmount);
|
||||
break;
|
||||
case WalletType.haven:
|
||||
_amount = haven!.formatterMoneroParseAmount(amount: _cryptoAmount);
|
||||
break;
|
||||
@@ -188,6 +192,10 @@ abstract class OutputBase with Store {
|
||||
if (_wallet.type == WalletType.zano) {
|
||||
return zano!.formatterIntAmountToDouble(amount: fee, currency: cryptoCurrencyHandler(), forFee: true);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.decred) {
|
||||
return decred!.formatterDecredAmountToDouble(amount: fee);
|
||||
}
|
||||
} catch (e) {
|
||||
printV(e.toString());
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/wallet_contact.dart';
|
||||
@@ -251,6 +252,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
wallet.type == WalletType.litecoin ||
|
||||
wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.wownero ||
|
||||
wallet.type == WalletType.decred ||
|
||||
wallet.type == WalletType.bitcoinCash;
|
||||
|
||||
@computed
|
||||
@@ -539,6 +541,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
case WalletType.zano:
|
||||
return zano!.createZanoTransactionCredentials(
|
||||
outputs: outputs, priority: priority!, currency: selectedCryptoCurrency);
|
||||
case WalletType.decred:
|
||||
this.coinTypeToSpendFrom = UnspentCoinType.any;
|
||||
return decred!.createDecredTransactionCredentials(outputs, priority!);
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${wallet.type}');
|
||||
}
|
||||
@@ -681,55 +686,51 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
}
|
||||
}
|
||||
|
||||
if (walletType == WalletType.bitcoin ||
|
||||
walletType == WalletType.litecoin ||
|
||||
walletType == WalletType.bitcoinCash) {
|
||||
if (error is TransactionWrongBalanceException) {
|
||||
if (error.amount != null)
|
||||
return S.current
|
||||
.tx_wrong_balance_with_amount_exception(currency.toString(), error.amount.toString());
|
||||
if (error is TransactionWrongBalanceException) {
|
||||
if (error.amount != null)
|
||||
return S.current
|
||||
.tx_wrong_balance_with_amount_exception(currency.toString(), error.amount.toString());
|
||||
|
||||
return S.current.tx_wrong_balance_exception(currency.toString());
|
||||
}
|
||||
if (error is TransactionNoInputsException) {
|
||||
return S.current.tx_not_enough_inputs_exception;
|
||||
}
|
||||
if (error is TransactionNoFeeException) {
|
||||
return S.current.tx_zero_fee_exception;
|
||||
}
|
||||
if (error is TransactionNoDustException) {
|
||||
return S.current.tx_no_dust_exception;
|
||||
}
|
||||
if (error is TransactionCommitFailed) {
|
||||
if (error.errorMessage != null && error.errorMessage!.contains("no peers replied")) {
|
||||
return S.current.tx_commit_failed_no_peers;
|
||||
}
|
||||
return "${S.current.tx_commit_failed}${error.errorMessage != null ? "\n\n${error.errorMessage}" : ""}";
|
||||
}
|
||||
if (error is TransactionCommitFailedDustChange) {
|
||||
return S.current.tx_rejected_dust_change;
|
||||
}
|
||||
if (error is TransactionCommitFailedDustOutput) {
|
||||
return S.current.tx_rejected_dust_output;
|
||||
}
|
||||
if (error is TransactionCommitFailedDustOutputSendAll) {
|
||||
return S.current.tx_rejected_dust_output_send_all;
|
||||
}
|
||||
if (error is TransactionCommitFailedVoutNegative) {
|
||||
return S.current.tx_rejected_vout_negative;
|
||||
}
|
||||
if (error is TransactionCommitFailedBIP68Final) {
|
||||
return S.current.tx_rejected_bip68_final;
|
||||
}
|
||||
if (error is TransactionCommitFailedLessThanMin) {
|
||||
return S.current.fee_less_than_min;
|
||||
}
|
||||
if (error is TransactionNoDustOnChangeException) {
|
||||
return S.current.tx_commit_exception_no_dust_on_change(error.min, error.max);
|
||||
}
|
||||
if (error is TransactionInputNotSupported) {
|
||||
return S.current.tx_invalid_input;
|
||||
return S.current.tx_wrong_balance_exception(currency.toString());
|
||||
}
|
||||
if (error is TransactionNoInputsException) {
|
||||
return S.current.tx_not_enough_inputs_exception;
|
||||
}
|
||||
if (error is TransactionNoFeeException) {
|
||||
return S.current.tx_zero_fee_exception;
|
||||
}
|
||||
if (error is TransactionNoDustException) {
|
||||
return S.current.tx_no_dust_exception;
|
||||
}
|
||||
if (error is TransactionCommitFailed) {
|
||||
if (error.errorMessage != null && error.errorMessage!.contains("no peers replied")) {
|
||||
return S.current.tx_commit_failed_no_peers;
|
||||
}
|
||||
return "${S.current.tx_commit_failed}${error.errorMessage != null ? "\n\n${error.errorMessage}" : ""}";
|
||||
}
|
||||
if (error is TransactionCommitFailedDustChange) {
|
||||
return S.current.tx_rejected_dust_change;
|
||||
}
|
||||
if (error is TransactionCommitFailedDustOutput) {
|
||||
return S.current.tx_rejected_dust_output;
|
||||
}
|
||||
if (error is TransactionCommitFailedDustOutputSendAll) {
|
||||
return S.current.tx_rejected_dust_output_send_all;
|
||||
}
|
||||
if (error is TransactionCommitFailedVoutNegative) {
|
||||
return S.current.tx_rejected_vout_negative;
|
||||
}
|
||||
if (error is TransactionCommitFailedBIP68Final) {
|
||||
return S.current.tx_rejected_bip68_final;
|
||||
}
|
||||
if (error is TransactionCommitFailedLessThanMin) {
|
||||
return S.current.fee_less_than_min;
|
||||
}
|
||||
if (error is TransactionNoDustOnChangeException) {
|
||||
return S.current.tx_commit_exception_no_dust_on_change(error.min, error.max);
|
||||
}
|
||||
if (error is TransactionInputNotSupported) {
|
||||
return S.current.tx_invalid_input;
|
||||
}
|
||||
|
||||
return errorMessage;
|
||||
|
||||
@@ -44,7 +44,8 @@ abstract class PrivacySettingsViewModelBase with Store {
|
||||
_wallet.type == WalletType.wownero ||
|
||||
_wallet.type == WalletType.bitcoin ||
|
||||
_wallet.type == WalletType.litecoin ||
|
||||
_wallet.type == WalletType.bitcoinCash;
|
||||
_wallet.type == WalletType.bitcoinCash ||
|
||||
_wallet.type == WalletType.decred;
|
||||
|
||||
bool get isMoneroWallet => _wallet.type == WalletType.monero;
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
final dateFormat = DateFormatter.withCurrentLocal();
|
||||
final tx = transactionInfo;
|
||||
|
||||
// TODO: can be cleaned further
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
_addMoneroListItems(tx, dateFormat);
|
||||
@@ -84,8 +85,12 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
case WalletType.zano:
|
||||
_addZanoListItems(tx, dateFormat);
|
||||
break;
|
||||
default:
|
||||
case WalletType.decred:
|
||||
_addDecredListItems(tx, dateFormat);
|
||||
break;
|
||||
case WalletType.none:
|
||||
case WalletType.banano:
|
||||
break;
|
||||
}
|
||||
|
||||
final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
|
||||
@@ -186,6 +191,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
return 'https://explore.wownero.com/tx/${txId}';
|
||||
case WalletType.zano:
|
||||
return 'https://explorer.zano.org/transaction/${txId}';
|
||||
case WalletType.decred:
|
||||
return 'https://${wallet.isTestnet ? "testnet" : "dcrdata"}.decred.org/tx/${txId.split(':')[0]}';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
@@ -218,6 +225,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
return S.current.view_transaction_on + 'Wownero.com';
|
||||
case WalletType.zano:
|
||||
return S.current.view_transaction_on + 'explorer.zano.org';
|
||||
case WalletType.decred:
|
||||
return S.current.view_transaction_on + 'dcrdata.decred.org';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
@@ -672,6 +681,51 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
items.addAll(_items);
|
||||
}
|
||||
|
||||
void _addDecredListItems(TransactionInfo tx, DateFormat dateFormat) {
|
||||
final _items = [
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_transaction_id,
|
||||
value: tx.txHash,
|
||||
key: ValueKey('standard_list_item_transaction_details_id_key'),
|
||||
),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_date,
|
||||
value: dateFormat.format(tx.date),
|
||||
key: ValueKey('standard_list_item_transaction_details_date_key'),
|
||||
),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_height,
|
||||
value: '${tx.height}',
|
||||
key: ValueKey('standard_list_item_transaction_details_height_key'),
|
||||
),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_amount,
|
||||
value: tx.amountFormatted(),
|
||||
key: ValueKey('standard_list_item_transaction_details_amount_key'),
|
||||
),
|
||||
if (tx.feeFormatted()?.isNotEmpty ?? false)
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_fee,
|
||||
value: tx.feeFormatted()!,
|
||||
key: ValueKey('standard_list_item_transaction_details_fee_key'),
|
||||
),
|
||||
if (showRecipientAddress && tx.to != null)
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_recipient_address,
|
||||
value: tx.to!,
|
||||
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
|
||||
),
|
||||
if (tx.from != null)
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_source_address,
|
||||
value: tx.from!,
|
||||
key: ValueKey('standard_list_item_transaction_details_source_address_key'),
|
||||
),
|
||||
];
|
||||
|
||||
items.addAll(_items);
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> _checkForRBF(TransactionInfo tx) async {
|
||||
if (wallet.type == WalletType.bitcoin &&
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
@@ -92,6 +93,8 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
||||
return wownero!.formatterWowneroAmountToString(amount: fullBalance);
|
||||
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type))
|
||||
return bitcoin!.formatterBitcoinAmountToString(amount: fullBalance);
|
||||
if (wallet.type == WalletType.decred)
|
||||
return decred!.formatterDecredAmountToString(amount: fullBalance);
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -105,7 +108,9 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
||||
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type)) {
|
||||
await bitcoin!.updateUnspents(wallet);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.decred) {
|
||||
decred!.updateUnspents(wallet);
|
||||
}
|
||||
_updateUnspentCoinsInfo();
|
||||
}
|
||||
|
||||
@@ -119,6 +124,8 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
return bitcoin!.getUnspents(wallet, coinTypeToSpendFrom: coinTypeToSpendFrom);
|
||||
case WalletType.decred:
|
||||
return decred!.getUnspents(wallet);
|
||||
default:
|
||||
return List.empty();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/haven/haven.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
part 'wallet_address_edit_or_create_view_model.g.dart';
|
||||
@@ -27,13 +28,12 @@ class AddressEditOrCreateStateFailure extends AddressEditOrCreateState {
|
||||
}
|
||||
|
||||
abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||
WalletAddressEditOrCreateViewModelBase(
|
||||
{required WalletBase wallet, WalletAddressListItem? item})
|
||||
WalletAddressEditOrCreateViewModelBase({required WalletBase wallet, WalletAddressListItem? item})
|
||||
: isEdit = item != null,
|
||||
state = AddressEditOrCreateStateInitial(),
|
||||
label = item?.name ?? '',
|
||||
_item = item,
|
||||
_wallet = wallet;
|
||||
_wallet = wallet;
|
||||
|
||||
@observable
|
||||
AddressEditOrCreateState state;
|
||||
@@ -46,7 +46,8 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||
final WalletAddressListItem? _item;
|
||||
final WalletBase _wallet;
|
||||
|
||||
bool get isElectrum => _wallet.type == WalletType.bitcoin ||
|
||||
bool get isElectrum =>
|
||||
_wallet.type == WalletType.bitcoin ||
|
||||
_wallet.type == WalletType.bitcoinCash ||
|
||||
_wallet.type == WalletType.litecoin;
|
||||
|
||||
@@ -69,39 +70,46 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||
Future<void> _createNew() async {
|
||||
final wallet = _wallet;
|
||||
|
||||
if (isElectrum) await bitcoin!.generateNewAddress(wallet, label);
|
||||
if (isElectrum) {
|
||||
await bitcoin!.generateNewAddress(wallet, label);
|
||||
await wallet.save();
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.decred) {
|
||||
await decred!.generateNewAddress(wallet, label);
|
||||
await wallet.save();
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.monero) {
|
||||
await monero
|
||||
!.getSubaddressList(wallet)
|
||||
.addSubaddress(
|
||||
wallet,
|
||||
accountIndex: monero!.getCurrentAccount(wallet).id,
|
||||
label: label);
|
||||
final addr = await monero!.getSubaddressList(wallet).subaddresses.first.address; // first because the order is reversed
|
||||
await monero!
|
||||
.getSubaddressList(wallet)
|
||||
.addSubaddress(wallet, accountIndex: monero!.getCurrentAccount(wallet).id, label: label);
|
||||
final addr = await monero!
|
||||
.getSubaddressList(wallet)
|
||||
.subaddresses
|
||||
.first
|
||||
.address; // first because the order is reversed
|
||||
wallet.walletAddresses.manualAddresses.add(addr);
|
||||
await wallet.save();
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.wownero) {
|
||||
await wownero
|
||||
!.getSubaddressList(wallet)
|
||||
.addSubaddress(
|
||||
wallet,
|
||||
accountIndex: wownero!.getCurrentAccount(wallet).id,
|
||||
label: label);
|
||||
final addr = await wownero!.getSubaddressList(wallet).subaddresses.first.address; // first because the order is reversed
|
||||
await wownero!
|
||||
.getSubaddressList(wallet)
|
||||
.addSubaddress(wallet, accountIndex: wownero!.getCurrentAccount(wallet).id, label: label);
|
||||
final addr = await wownero!
|
||||
.getSubaddressList(wallet)
|
||||
.subaddresses
|
||||
.first
|
||||
.address; // first because the order is reversed
|
||||
wallet.walletAddresses.manualAddresses.add(addr);
|
||||
await wallet.save();
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.haven) {
|
||||
await haven
|
||||
!.getSubaddressList(wallet)
|
||||
.addSubaddress(
|
||||
wallet,
|
||||
accountIndex: haven!.getCurrentAccount(wallet).id,
|
||||
label: label);
|
||||
await haven!
|
||||
.getSubaddressList(wallet)
|
||||
.addSubaddress(wallet, accountIndex: haven!.getCurrentAccount(wallet).id, label: label);
|
||||
await wallet.save();
|
||||
}
|
||||
}
|
||||
@@ -111,6 +119,12 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||
|
||||
if (isElectrum) await bitcoin!.updateAddress(wallet, _item!.address, label);
|
||||
|
||||
if (wallet.type == WalletType.decred) {
|
||||
await decred!.updateAddress(wallet, _item!.address, label);
|
||||
await wallet.save();
|
||||
return;
|
||||
}
|
||||
|
||||
final index = _item?.id;
|
||||
if (index != null) {
|
||||
if (wallet.type == WalletType.monero) {
|
||||
@@ -125,9 +139,7 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||
}
|
||||
if (wallet.type == WalletType.haven) {
|
||||
await haven!.getSubaddressList(wallet).setLabelSubaddress(wallet,
|
||||
accountIndex: haven!.getCurrentAccount(wallet).id,
|
||||
addressIndex: index,
|
||||
label: label);
|
||||
accountIndex: haven!.getCurrentAccount(wallet).id, addressIndex: index, label: label);
|
||||
await wallet.save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:cake_wallet/haven/haven.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
@@ -222,6 +223,22 @@ class ZanoURI extends PaymentURI {
|
||||
}
|
||||
}
|
||||
|
||||
class DecredURI extends PaymentURI {
|
||||
DecredURI({required String amount, required String address})
|
||||
: super(amount: amount, address: address);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var base = 'decred:' + address;
|
||||
|
||||
if (amount.isNotEmpty) {
|
||||
base += '?amount=${amount.replaceAll(',', '.')}';
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
|
||||
WalletAddressListViewModelBase({
|
||||
required AppStore appStore,
|
||||
@@ -313,7 +330,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
case WalletType.wownero:
|
||||
return WowneroURI(amount: amount, address: address.address);
|
||||
case WalletType.zano:
|
||||
return ZanoURI(amount: amount, address: address.address);
|
||||
return ZanoURI(amount: amount, address: address.address);
|
||||
case WalletType.decred:
|
||||
return DecredURI(amount: amount, address: address.address);
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
@@ -468,6 +487,14 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.decred) {
|
||||
final addrInfos = decred!.getAddressInfos(wallet);
|
||||
addrInfos.forEach((info) {
|
||||
addressList.add(new WalletAddressListItem(isPrimary: false, address: info.address,
|
||||
name: info.label));
|
||||
});
|
||||
}
|
||||
|
||||
for (var i = 0; i < addressList.length; i++) {
|
||||
if (!(addressList[i] is WalletAddressListItem)) continue;
|
||||
(addressList[i] as WalletAddressListItem).isHidden = wallet.walletAddresses.hiddenAddresses
|
||||
@@ -561,7 +588,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
WalletType.haven,
|
||||
WalletType.bitcoinCash,
|
||||
WalletType.bitcoin,
|
||||
WalletType.litecoin
|
||||
WalletType.litecoin,
|
||||
WalletType.decred
|
||||
].contains(wallet.type);
|
||||
|
||||
@computed
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_monero/monero_wallet.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:polyseed/polyseed.dart';
|
||||
|
||||
part 'wallet_keys_view_model.g.dart';
|
||||
@@ -155,6 +156,12 @@ abstract class WalletKeysViewModelBase with Store {
|
||||
),
|
||||
]);
|
||||
break;
|
||||
case WalletType.decred:
|
||||
final pubkey = decred!.pubkey(_appStore.wallet!);
|
||||
items.addAll([
|
||||
StandartListItem(title: S.current.view_key_public, value: pubkey),
|
||||
]);
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
@@ -248,6 +255,8 @@ abstract class WalletKeysViewModelBase with Store {
|
||||
return 'wownero-wallet';
|
||||
case WalletType.zano:
|
||||
return 'zano-wallet';
|
||||
case WalletType.decred:
|
||||
return 'decred-wallet';
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${_wallet.type.toString()}');
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import 'package:cake_wallet/tron/tron.dart';
|
||||
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
@@ -83,6 +84,8 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
return 25;
|
||||
case WalletType.zano:
|
||||
return 26;
|
||||
case WalletType.decred:
|
||||
return 15;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +173,8 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
password: walletPassword,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
case WalletType.decred:
|
||||
return decred!.createDecredNewWalletCredentials(name: name);
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
|
||||
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
|
||||
@@ -58,6 +59,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
case WalletType.haven:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.decred:
|
||||
availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys];
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
@@ -77,6 +79,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
}
|
||||
|
||||
static const moneroSeedMnemonicLength = 25;
|
||||
static const decredSeedMnemonicLength = 15;
|
||||
|
||||
late List<WalletRestoreMode> availableModes;
|
||||
final bool hasSeedLanguageSelector;
|
||||
@@ -171,11 +174,18 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
);
|
||||
case WalletType.zano:
|
||||
return zano!.createZanoRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
height: height,
|
||||
name: name,
|
||||
password: password,
|
||||
height: height,
|
||||
passphrase: passphrase??'',
|
||||
mnemonic: seed);
|
||||
mnemonic: seed,
|
||||
);
|
||||
case WalletType.decred:
|
||||
return decred!.createDecredRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
mnemonic: seed,
|
||||
password: password,
|
||||
);
|
||||
case WalletType.none:
|
||||
break;
|
||||
}
|
||||
@@ -251,6 +261,12 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
password: password,
|
||||
language: 'English',
|
||||
);
|
||||
case WalletType.decred:
|
||||
return decred!.createDecredRestoreWalletFromPubkeyCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
pubkey: viewKey!,
|
||||
);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user