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:
Omar Hatem
2025-03-21 04:18:47 +02:00
committed by GitHub
co-authored by JoeGruffins Wisdom Arerosuoghene Philemon Ukane Czarek Nakamoto
parent 52a39e29d4
commit 0ba54fa602
175 changed files with 7145 additions and 1115 deletions
+1 -1
View File
@@ -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:
+11
View File
@@ -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.\-]+\$');
}
+3
View File
@@ -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 [];
}
+4
View File
@@ -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 '';
}
+1
View File
@@ -90,6 +90,7 @@ class WalletCreationService {
case WalletType.nano:
case WalletType.banano:
case WalletType.zano:
case WalletType.decred:
return false;
}
}
+114
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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);
+3
View File
@@ -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 [];
}
+2
View File
@@ -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
View File
@@ -215,7 +215,7 @@ Future<void> initializeAppConfigs() async {
secureStorage: secureStorage,
anonpayInvoiceInfo: anonpayInvoiceInfo,
havenSeedStore: havenSeedStore,
initialMigrationVersion: 48,
initialMigrationVersion: 49,
);
}
+1
View File
@@ -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;
}
+3
View File
@@ -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());
-1
View File
@@ -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';
+2 -1
View File
@@ -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(
+4 -1
View File
@@ -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(),
),
)
],
+54 -17
View File
@@ -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,
+3
View File
@@ -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) {
+36 -2
View File
@@ -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};
+8 -1
View File
@@ -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;
}
}
+8
View File
@@ -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());
}
+48 -47
View File
@@ -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()}');
}
+5
View File
@@ -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()}');
}
+20 -4
View File
@@ -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;
}