CW-1099-Add-Doge-coin-to-Cake-Wallet (#2396)

* add wallet create, restore and server integration * update transaction fee calculation * Updated tool/configure.dart * Add dogecoin support to app_config * minor fix [skip ci] * Update model_generator.sh * remove cw_bitcoin import from lib/ directory * update bitcoin base * update default node * add passphrase an QR restore option * add Dogecoin support for message signing * add Dogecoin support across wallet features * fix edit subaddress Updated the isElectrum getter to include WalletType.dogecoin, ensuring Dogecoin wallets are correctly identified as Electrum-compatible. * Update configure.dart * fix: address label not saved * remove unused derivationPath Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * address review comments * fix generateInitialAddresses type * enable auto switching for Dogecoin nodes --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

Serhii committed Aug 7, 2025 at 22:07 UTC b6244cdd446c3ea03810448eb8fe3306737e2ce3
70 files changed +1088 -38
.gitignore
+1
@@ -140,6 +140,7 @@ lib/tron/tron.dart
140 lib/wownero/wownero.dart
141 lib/zano/zano.dart
142 lib/decred/decred.dart
143 +lib/dogecoin/dogecoin.dart
144
145 ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png
146 ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png
android/app/src/main/AndroidManifestBase.xml
+3
@@ -103,6 +103,9 @@
103 <data android:scheme="decred" />
104 <data android:scheme="decred-wallet" />
105 <data android:scheme="decred_wallet" />
106 + <data android:scheme="dogecoin" />
107 + <data android:scheme="dogecoin-wallet" />
108 + <data android:scheme="dogecoin_wallet" />
109 </intent-filter>
110 <!-- nano-gpt link scheme -->
111 <intent-filter android:autoVerify="true">
assets/dogecoin_electrum_server_list.yml new
+12
@@ -0,0 +1,12 @@
1 +-
2 + uri: dogecoin.stackwallet.com:50022
3 + is_default: true
4 + useSSL: true
5 + isEnabledForAutoSwitching: true
6 +-
7 + uri: doge.aftrek.org:50002
8 + useSSL: true
9 + isEnabledForAutoSwitching: true
10 +-
11 + uri: electrum1.cipig.net:20060
12 + useSSL: true
\ No newline at end of file
cakewallet.bat
+1 -1
@@ -1,5 +1,5 @@
1 @echo off
2 -set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron
2 +set cw_win_app_config=--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --dogecoin
3 set cw_root=%cd%
4 set cw_archive_name=Cake Wallet.zip
5 set cw_archive_path=%cw_root%\%cw_archive_name%
cw_bitcoin/lib/electrum_wallet.dart
+17 -9
@@ -130,6 +130,8 @@ abstract class ElectrumWalletBase
130 as Bip32Slip10Secp256k1;
131 case CryptoCurrency.bch:
132 return bitcoinCashHDWallet(seedBytes);
133 + case CryptoCurrency.doge:
134 + return dogecoinHDWallet(seedBytes);
135 default:
136 throw Exception("Unsupported currency");
137 }
@@ -141,6 +143,9 @@ abstract class ElectrumWalletBase
143 static Bip32Slip10Secp256k1 bitcoinCashHDWallet(Uint8List seedBytes) =>
144 Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/44'/145'/0'") as Bip32Slip10Secp256k1;
145
146 + static Bip32Slip10Secp256k1 dogecoinHDWallet(Uint8List seedBytes) =>
147 + Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/44'/3'/0'") as Bip32Slip10Secp256k1;
148 +
149 static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
150 inputsCount * 68 + outputsCounts * 34 + 10;
151
@@ -629,9 +634,9 @@ abstract class ElectrumWalletBase
634 }
635 }
636
632 - int get _dustAmount => 546;
637 + int get networkDustAmount => 546;
638
634 - bool _isBelowDust(int amount) => amount <= _dustAmount && network != BitcoinNetwork.testnet;
639 + bool _isBelowDust(int amount) => amount <= networkDustAmount && network != BitcoinNetwork.testnet;
640
641 UtxoDetails _createUTXOS({
642 required bool sendAll,
@@ -1685,7 +1690,7 @@ abstract class ElectrumWalletBase
1690 var currentFee = allInputsAmount - totalOutAmount;
1691
1692 int remainingFee = (newFee - currentFee > 0) ? newFee - currentFee : newFee;
1688 - return totalBalance - receiverAmount - remainingFee >= _dustAmount;
1693 + return totalBalance - receiverAmount - remainingFee >= networkDustAmount;
1694 }
1695
1696 Future<PendingBitcoinTransaction> replaceByFee(String hash, int newFee) async {
@@ -1773,10 +1778,10 @@ abstract class ElectrumWalletBase
1778
1779 if (isChange) {
1780 int outputAmount = output.value.toInt();
1776 - if (outputAmount > _dustAmount) {
1777 - int deduction = (outputAmount - _dustAmount >= remainingFee)
1781 + if (outputAmount > networkDustAmount) {
1782 + int deduction = (outputAmount - networkDustAmount >= remainingFee)
1783 ? remainingFee
1779 - : outputAmount - _dustAmount;
1784 + : outputAmount - networkDustAmount;
1785 outputs[i] = BitcoinOutput(
1786 address: output.address, value: BigInt.from(outputAmount - deduction));
1787 remainingFee -= deduction;
@@ -1845,10 +1850,10 @@ abstract class ElectrumWalletBase
1850 final output = outputs[i];
1851 int outputAmount = output.value.toInt();
1852
1848 - if (outputAmount > _dustAmount) {
1849 - int deduction = (outputAmount - _dustAmount >= remainingFee)
1853 + if (outputAmount > networkDustAmount) {
1854 + int deduction = (outputAmount - networkDustAmount >= remainingFee)
1855 ? remainingFee
1851 - : outputAmount - _dustAmount;
1856 + : outputAmount - networkDustAmount;
1857
1858 outputs[i] = BitcoinOutput(
1859 address: output.address, value: BigInt.from(outputAmount - deduction));
@@ -2051,6 +2056,9 @@ abstract class ElectrumWalletBase
2056 await Future.wait(LITECOIN_ADDRESS_TYPES
2057 .where((type) => type != SegwitAddresType.mweb)
2058 .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
2059 + } else if (type == WalletType.dogecoin) {
2060 + await Future.wait(DOGECOIN_ADDRESS_TYPES.map(
2061 + (type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
2062 }
2063
2064 transactionHistory.transactions.values.forEach((tx) async {
cw_bitcoin/lib/electrum_wallet_addresses.dart
+11 -3
@@ -3,7 +3,6 @@ import 'dart:io' show Platform;
3 import 'package:bitcoin_base/bitcoin_base.dart';
4 import 'package:blockchain_utils/blockchain_utils.dart';
5 import 'package:cw_bitcoin/bitcoin_address_record.dart';
6 -import 'package:cw_bitcoin/electrum_wallet.dart';
6 import 'package:cw_core/unspent_coin_type.dart';
7 import 'package:cw_core/utils/print_verbose.dart';
8 import 'package:cw_bitcoin/bitcoin_unspent.dart';
@@ -33,6 +32,10 @@ const List<BitcoinAddressType> BITCOIN_CASH_ADDRESS_TYPES = [
32 P2pkhAddressType.p2pkh,
33 ];
34
35 +const List<BitcoinAddressType> DOGECOIN_ADDRESS_TYPES = [
36 + P2pkhAddressType.p2pkh,
37 +];
38 +
39 abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
40 ElectrumWalletAddressesBase(
41 WalletInfo walletInfo, {
@@ -253,6 +256,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
256 if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
257 await _generateInitialAddresses(type: SegwitAddresType.mweb);
258 }
259 + } else if (walletInfo.type == WalletType.dogecoin) {
260 + await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
261 } else if (walletInfo.type == WalletType.bitcoin) {
262 await _generateInitialAddresses();
263 if (!isHardwareWallet) {
@@ -458,7 +463,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
463 }
464 }
465
461 - void addBitcoinCashAddressTypes() {
466 + void addP2PKHAddressTypes() {
467 final lastP2pkh = _addresses.firstWhere(
468 (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, P2pkhAddressType.p2pkh));
469 if (lastP2pkh.address != address) {
@@ -487,7 +492,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
492 addLitecoinAddressTypes();
493 break;
494 case WalletType.bitcoinCash:
490 - addBitcoinCashAddressTypes();
495 + addP2PKHAddressTypes();
496 + break;
497 + case WalletType.dogecoin:
498 + addP2PKHAddressTypes();
499 break;
500 default:
501 break;
cw_bitcoin/lib/pending_bitcoin_transaction.dart
+4 -2
@@ -81,12 +81,15 @@ class PendingBitcoinTransaction with PendingTransaction {
81 return "LTC";
82 case WalletType.bitcoinCash:
83 return "BCH";
84 + case WalletType.dogecoin:
85 + return "DOGE";
86 default:
87 return type.name;
88 }
89
90 }
91
92 +
93 @override
94 String get feeFormattedValue => bitcoinAmountToString(amount: fee);
95
@@ -104,8 +107,7 @@ class PendingBitcoinTransaction with PendingTransaction {
107 return PendingChange(
108 changeAddressOverride!, BtcUtils.fromSatoshi(change.amount));
109 }
107 - return PendingChange(
108 - change.scriptPubKey.toAddress(), BtcUtils.fromSatoshi(change.amount));
110 + return PendingChange(change.scriptPubKey.toAddress(network: network), BtcUtils.fromSatoshi(change.amount));
111 } catch (_) {
112 return null;
113 }
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart
+2
@@ -142,6 +142,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
142 addr.address,
143 index: addr.index,
144 isHidden: addr.isHidden,
145 + name: addr.name,
146 type: P2pkhAddressType.p2pkh,
147 network: BitcoinCashNetwork.mainnet,
148 );
@@ -150,6 +151,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
151 AddressUtils.getCashAddrFormat(addr.address),
152 index: addr.index,
153 isHidden: addr.isHidden,
154 + name: addr.name,
155 type: P2pkhAddressType.p2pkh,
156 network: BitcoinCashNetwork.mainnet,
157 );
cw_core/lib/amount_converter.dart
+1
@@ -30,6 +30,7 @@ class AmountConverter {
30 case CryptoCurrency.bch:
31 case CryptoCurrency.ltc:
32 case CryptoCurrency.dcr:
33 + case CryptoCurrency.doge:
34 return _bitcoinAmountToString(amount);
35 case CryptoCurrency.xhv:
36 case CryptoCurrency.xag:
cw_core/lib/currency_for_wallet_type.dart
+2
@@ -34,6 +34,8 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
34 return CryptoCurrency.zano;
35 case WalletType.decred:
36 return CryptoCurrency.dcr;
37 + case WalletType.dogecoin:
38 + return CryptoCurrency.doge;
39 case WalletType.none:
40 throw Exception(
41 'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
cw_core/lib/node.dart
+2
@@ -100,6 +100,7 @@ class Node extends HiveObject with Keyable {
100 case WalletType.bitcoin:
101 case WalletType.litecoin:
102 case WalletType.bitcoinCash:
103 + case WalletType.dogecoin:
104 return createUriFromElectrumAddress(uriRaw, path!);
105 case WalletType.nano:
106 case WalletType.banano:
@@ -170,6 +171,7 @@ class Node extends HiveObject with Keyable {
171 case WalletType.polygon:
172 case WalletType.solana:
173 case WalletType.tron:
174 + case WalletType.dogecoin:
175 return requestElectrumServer();
176 case WalletType.zano:
177 return requestZanoNode();
cw_core/lib/wallet_type.dart
+17 -1
@@ -18,6 +18,7 @@ const walletTypes = [
18 WalletType.tron,
19 WalletType.zano,
20 WalletType.decred,
21 + WalletType.dogecoin,
22 ];
23
24 @HiveType(typeId: WALLET_TYPE_TYPE_ID)
@@ -65,7 +66,10 @@ enum WalletType {
66 zano,
67
68 @HiveField(14)
68 - decred
69 + decred,
70 +
71 + @HiveField(15)
72 + dogecoin
73 }
74
75 int serializeToInt(WalletType type) {
@@ -98,6 +102,8 @@ int serializeToInt(WalletType type) {
102 return 12;
103 case WalletType.decred:
104 return 13;
105 + case WalletType.dogecoin:
106 + return 14;
107 case WalletType.none:
108 return -1;
109 }
@@ -133,6 +139,8 @@ WalletType deserializeFromInt(int raw) {
139 return WalletType.zano;
140 case 13:
141 return WalletType.decred;
142 + case 14:
143 + return WalletType.dogecoin;
144 default:
145 throw Exception(
146 'Unexpected token: $raw for WalletType deserializeFromInt');
@@ -169,6 +177,8 @@ String walletTypeToString(WalletType type) {
177 return 'Zano';
178 case WalletType.decred:
179 return 'Decred';
180 + case WalletType.dogecoin:
181 + return 'Dogecoin';
182 case WalletType.none:
183 return '';
184 }
@@ -204,6 +214,8 @@ String walletTypeToDisplayName(WalletType type) {
214 return 'Zano (ZANO)';
215 case WalletType.decred:
216 return 'Decred (DCR)';
217 + case WalletType.dogecoin:
218 + return 'Dogecoin (DOGE)';
219 case WalletType.none:
220 return '';
221 }
@@ -242,6 +254,8 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = fal
254 return CryptoCurrency.zano;
255 case WalletType.decred:
256 return CryptoCurrency.dcr;
257 + case WalletType.dogecoin:
258 + return CryptoCurrency.doge;
259 case WalletType.none:
260 throw Exception(
261 'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
@@ -278,6 +292,8 @@ WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
292 return WalletType.zano;
293 case CryptoCurrency.dcr:
294 return WalletType.decred;
295 + case CryptoCurrency.doge:
296 + return WalletType.dogecoin;
297 default:
298 return null;
299 }
cw_dogecoin/.gitignore new
+31
@@ -0,0 +1,31 @@
1 +# Miscellaneous
2 +*.class
3 +*.log
4 +*.pyc
5 +*.swp
6 +.DS_Store
7 +.atom/
8 +.buildlog/
9 +.history
10 +.svn/
11 +migrate_working_dir/
12 +
13 +# IntelliJ related
14 +*.iml
15 +*.ipr
16 +*.iws
17 +.idea/
18 +
19 +# The .vscode folder contains launch configuration and tasks you configure in
20 +# VS Code which you may wish to be included in version control, so this line
21 +# is commented out by default.
22 +#.vscode/
23 +
24 +# Flutter/Dart/Pub related
25 +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26 +/pubspec.lock
27 +**/doc/api/
28 +.dart_tool/
29 +.flutter-plugins
30 +.flutter-plugins-dependencies
31 +build/
cw_dogecoin/.metadata new
+10
@@ -0,0 +1,10 @@
1 +# This file tracks properties of this Flutter project.
2 +# Used by Flutter tool to assess capabilities and perform upgrades etc.
3 +#
4 +# This file should be version controlled and should not be manually edited.
5 +
6 +version:
7 + revision: "c23637390482d4cf9598c3ce3f2be31aa7332daf"
8 + channel: "stable"
9 +
10 +project_type: package
cw_dogecoin/CHANGELOG.md new
+3
@@ -0,0 +1,3 @@
1 +## 0.0.1
2 +
3 +* TODO: Describe initial release.
cw_dogecoin/LICENSE new
+1
@@ -0,0 +1 @@
1 +TODO: Add your license here.
cw_dogecoin/README.md new
+39
@@ -0,0 +1,39 @@
1 +<!--
2 +This README describes the package. If you publish this package to pub.dev,
3 +this README's contents appear on the landing page for your package.
4 +
5 +For information about how to write a good package README, see the guide for
6 +[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
7 +
8 +For general information about developing packages, see the Dart guide for
9 +[creating packages](https://dart.dev/guides/libraries/create-packages)
10 +and the Flutter guide for
11 +[developing packages and plugins](https://flutter.dev/to/develop-packages).
12 +-->
13 +
14 +TODO: Put a short description of the package here that helps potential users
15 +know whether this package might be useful for them.
16 +
17 +## Features
18 +
19 +TODO: List what your package can do. Maybe include images, gifs, or videos.
20 +
21 +## Getting started
22 +
23 +TODO: List prerequisites and provide or point to information on how to
24 +start using the package.
25 +
26 +## Usage
27 +
28 +TODO: Include short and useful examples for package users. Add longer examples
29 +to `/example` folder.
30 +
31 +```dart
32 +const like = 'sample';
33 +```
34 +
35 +## Additional information
36 +
37 +TODO: Tell users more about the package: where to find more information, how to
38 +contribute to the package, how to file issues, what response they can expect
39 +from the package authors, and more.
cw_dogecoin/analysis_options.yaml new
+4
@@ -0,0 +1,4 @@
1 +include: package:flutter_lints/flutter.yaml
2 +
3 +# Additional information about this file can be found at
4 +# https://dart.dev/guides/language/analysis-options
cw_dogecoin/lib/cw_dogecoin.dart new
+6
@@ -0,0 +1,6 @@
1 +export 'src/dogecoin_wallet.dart';
2 +export 'src/dogecoin_wallet_addresses.dart';
3 +export 'src/dogecoin_wallet_creation_credentials.dart';
4 +export 'src/dogecoin_wallet_service.dart';
5 +export 'src/dogecoin_transaction_priority.dart';
6 +
cw_dogecoin/lib/src/dogecoin_transaction_priority.dart new
+52
@@ -0,0 +1,52 @@
1 +import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
2 +
3 +class DogecoinTransactionPriority extends BitcoinTransactionPriority {
4 + const DogecoinTransactionPriority({required String title, required int raw})
5 + : super(title: title, raw: raw);
6 +
7 + static const List<DogecoinTransactionPriority> all = [fast, medium, slow];
8 + static const DogecoinTransactionPriority slow =
9 + DogecoinTransactionPriority(title: 'Slow', raw: 0);
10 + static const DogecoinTransactionPriority medium =
11 + DogecoinTransactionPriority(title: 'Medium', raw: 1);
12 + static const DogecoinTransactionPriority fast =
13 + DogecoinTransactionPriority(title: 'Fast', raw: 2);
14 +
15 + static DogecoinTransactionPriority deserialize({required int raw}) {
16 + switch (raw) {
17 + case 0:
18 + return slow;
19 + case 1:
20 + return medium;
21 + case 2:
22 + return fast;
23 + default:
24 + throw Exception('Unexpected token: $raw for DogecoinTransactionPriority deserialize');
25 + }
26 + }
27 +
28 + @override
29 + String get units => 'koinu';
30 +
31 + @override
32 + String toString() {
33 + var label = '';
34 +
35 + switch (this) {
36 + case DogecoinTransactionPriority.slow:
37 + label = 'Slow'; // S.current.transaction_priority_slow;
38 + break;
39 + case DogecoinTransactionPriority.medium:
40 + label = 'Medium'; // S.current.transaction_priority_medium;
41 + break;
42 + case DogecoinTransactionPriority.fast:
43 + label = 'Fast'; // S.current.transaction_priority_fast;
44 + break;
45 + default:
46 + break;
47 + }
48 +
49 + return label;
50 + }
51 +}
52 +
cw_dogecoin/lib/src/dogecoin_wallet.dart new
+164
@@ -0,0 +1,164 @@
1 +import 'package:bitcoin_base/bitcoin_base.dart';
2 +import 'package:blockchain_utils/blockchain_utils.dart';
3 +import 'package:cw_bitcoin/bitcoin_address_record.dart';
4 +import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
5 +import 'package:cw_bitcoin/electrum_balance.dart';
6 +import 'package:cw_bitcoin/electrum_wallet.dart';
7 +import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
8 +import 'package:cw_core/crypto_currency.dart';
9 +import 'package:cw_core/encryption_file_utils.dart';
10 +import 'package:cw_core/unspent_coins_info.dart';
11 +import 'package:cw_core/wallet_info.dart';
12 +import 'package:cw_core/wallet_keys_file.dart';
13 +import 'package:flutter/foundation.dart';
14 +import 'package:hive/hive.dart';
15 +import 'package:mobx/mobx.dart';
16 +
17 +import 'dogecoin_wallet_addresses.dart';
18 +
19 +part 'dogecoin_wallet.g.dart';
20 +
21 +class DogeCoinWallet = DogeCoinWalletBase with _$DogeCoinWallet;
22 +
23 +abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
24 + DogeCoinWalletBase({
25 + required String mnemonic,
26 + required String password,
27 + required WalletInfo walletInfo,
28 + required Box<UnspentCoinsInfo> unspentCoinsInfo,
29 + required Uint8List seedBytes,
30 + required EncryptionFileUtils encryptionFileUtils,
31 + String? passphrase,
32 + BitcoinAddressType? addressPageType,
33 + List<BitcoinAddressRecord>? initialAddresses,
34 + ElectrumBalance? initialBalance,
35 + Map<String, int>? initialRegularAddressIndex,
36 + Map<String, int>? initialChangeAddressIndex,
37 + }) : super(
38 + mnemonic: mnemonic,
39 + password: password,
40 + walletInfo: walletInfo,
41 + unspentCoinsInfo: unspentCoinsInfo,
42 + network: DogecoinNetwork.mainnet,
43 + initialAddresses: initialAddresses,
44 + initialBalance: initialBalance,
45 + seedBytes: seedBytes,
46 + currency: CryptoCurrency.doge,
47 + encryptionFileUtils: encryptionFileUtils,
48 + passphrase: passphrase) {
49 + walletAddresses = DogeCoinWalletAddresses(
50 + walletInfo,
51 + initialAddresses: initialAddresses,
52 + initialRegularAddressIndex: initialRegularAddressIndex,
53 + initialChangeAddressIndex: initialChangeAddressIndex,
54 + mainHd: hd,
55 + sideHd: accountHD.childKey(Bip32KeyIndex(1)),
56 + network: network,
57 + initialAddressPageType: addressPageType,
58 + isHardwareWallet: walletInfo.isHardwareWallet,
59 + );
60 + autorun((_) {
61 + this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
62 + });
63 + }
64 +
65 + @override
66 + int get networkDustAmount => 100000000; // 1 DOGE = 1e8 koinu
67 +
68 + static Future<DogeCoinWallet> create(
69 + {required String mnemonic,
70 + required String password,
71 + required WalletInfo walletInfo,
72 + required Box<UnspentCoinsInfo> unspentCoinsInfo,
73 + required EncryptionFileUtils encryptionFileUtils,
74 + String? passphrase,
75 + String? addressPageType,
76 + List<BitcoinAddressRecord>? initialAddresses,
77 + ElectrumBalance? initialBalance,
78 + Map<String, int>? initialRegularAddressIndex,
79 + Map<String, int>? initialChangeAddressIndex}) async {
80 + return DogeCoinWallet(
81 + mnemonic: mnemonic,
82 + password: password,
83 + walletInfo: walletInfo,
84 + unspentCoinsInfo: unspentCoinsInfo,
85 + initialAddresses: initialAddresses,
86 + initialBalance: initialBalance,
87 + seedBytes: MnemonicBip39.toSeed(mnemonic, passphrase: passphrase),
88 + encryptionFileUtils: encryptionFileUtils,
89 + initialRegularAddressIndex: initialRegularAddressIndex,
90 + initialChangeAddressIndex: initialChangeAddressIndex,
91 + addressPageType: P2pkhAddressType.p2pkh,
92 + passphrase: passphrase,
93 + );
94 + }
95 +
96 + static Future<DogeCoinWallet> open({
97 + required String name,
98 + required WalletInfo walletInfo,
99 + required Box<UnspentCoinsInfo> unspentCoinsInfo,
100 + required String password,
101 + required EncryptionFileUtils encryptionFileUtils,
102 + }) async {
103 + final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
104 +
105 + ElectrumWalletSnapshot? snp = null;
106 +
107 + try {
108 + snp = await ElectrumWalletSnapshot.load(
109 + encryptionFileUtils,
110 + name,
111 + walletInfo.type,
112 + password,
113 + DogecoinNetwork.mainnet,
114 + );
115 + } catch (e) {
116 + if (!hasKeysFile) rethrow;
117 + }
118 +
119 + final WalletKeysData keysData;
120 + // Migrate wallet from the old scheme to then new .keys file scheme
121 + if (!hasKeysFile) {
122 + keysData =
123 + WalletKeysData(mnemonic: snp!.mnemonic, xPub: snp.xpub, passphrase: snp.passphrase);
124 + } else {
125 + keysData = await WalletKeysFile.readKeysFile(
126 + name,
127 + walletInfo.type,
128 + password,
129 + encryptionFileUtils,
130 + );
131 + }
132 +
133 + return DogeCoinWallet(
134 + mnemonic: keysData.mnemonic!,
135 + password: password,
136 + walletInfo: walletInfo,
137 + unspentCoinsInfo: unspentCoinsInfo,
138 + initialAddresses: snp?.addresses,
139 + initialBalance: snp?.balance,
140 + seedBytes: await MnemonicBip39.toSeed(keysData.mnemonic!, passphrase: keysData.passphrase),
141 + encryptionFileUtils: encryptionFileUtils,
142 + initialRegularAddressIndex: snp?.regularAddressIndex,
143 + initialChangeAddressIndex: snp?.changeAddressIndex,
144 + addressPageType: P2pkhAddressType.p2pkh,
145 + passphrase: keysData.passphrase,
146 + );
147 + }
148 +
149 + @override
150 + Future<String> signMessage(String message, {String? address = null}) async {
151 + int? index;
152 + try {
153 + index = address != null
154 + ? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
155 + : null;
156 + } catch (_) {}
157 + final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index));
158 + final priv = ECPrivate.fromWif(
159 + WifEncoder.encode(HD.privateKey.raw, netVer: network.wifNetVer),
160 + netVersion: network.wifNetVer,
161 + );
162 + return priv.signMessage(StringUtils.encode(message));
163 + }
164 +}
cw_dogecoin/lib/src/dogecoin_wallet_addresses.dart new
+29
@@ -0,0 +1,29 @@
1 +import 'package:bitcoin_base/bitcoin_base.dart';
2 +import 'package:blockchain_utils/blockchain_utils.dart';
3 +import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
4 +import 'package:cw_bitcoin/utils.dart';
5 +import 'package:cw_core/wallet_info.dart';
6 +import 'package:mobx/mobx.dart';
7 +
8 +part 'dogecoin_wallet_addresses.g.dart';
9 +
10 +class DogeCoinWalletAddresses = DogeCoinWalletAddressesBase with _$DogeCoinWalletAddresses;
11 +
12 +abstract class DogeCoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
13 + DogeCoinWalletAddressesBase(WalletInfo walletInfo, {
14 + required super.mainHd,
15 + required super.sideHd,
16 + required super.network,
17 + required super.isHardwareWallet,
18 + super.initialAddresses,
19 + super.initialRegularAddressIndex,
20 + super.initialChangeAddressIndex,
21 + super.initialAddressPageType
22 + }) : super(walletInfo);
23 +
24 + @override
25 + String getAddress({required int index,
26 + required Bip32Slip10Secp256k1 hd,
27 + BitcoinAddressType? addressType}) =>
28 + generateP2PKHAddress(hd: hd, index: index, network: network);
29 +}
cw_dogecoin/lib/src/dogecoin_wallet_creation_credentials.dart new
+38
@@ -0,0 +1,38 @@
1 +import 'package:cw_core/wallet_credentials.dart';
2 +import 'package:cw_core/wallet_info.dart';
3 +
4 +class DogeCoinNewWalletCredentials extends WalletCredentials {
5 + DogeCoinNewWalletCredentials({
6 + required String name,
7 + WalletInfo? walletInfo,
8 + String? password,
9 + String? passphrase,
10 + this.mnemonic,
11 + }) : super(
12 + name: name,
13 + walletInfo: walletInfo,
14 + password: password,
15 + passphrase: passphrase,
16 + );
17 + final String? mnemonic;
18 +}
19 +
20 +class DogeCoinRestoreWalletFromSeedCredentials extends WalletCredentials {
21 + DogeCoinRestoreWalletFromSeedCredentials({
22 + required String name,
23 + required String password,
24 + required this.mnemonic,
25 + WalletInfo? walletInfo,
26 + String? passphrase,
27 + }) : super(name: name, password: password, walletInfo: walletInfo, passphrase: passphrase);
28 +
29 + final String mnemonic;
30 +}
31 +
32 +class DogeCoinRestoreWalletFromWIFCredentials extends WalletCredentials {
33 + DogeCoinRestoreWalletFromWIFCredentials(
34 + {required String name, required String password, required this.wif, WalletInfo? walletInfo})
35 + : super(name: name, password: password, walletInfo: walletInfo);
36 +
37 + final String wif;
38 +}
cw_dogecoin/lib/src/dogecoin_wallet_service.dart new
+151
@@ -0,0 +1,151 @@
1 +import 'dart:io';
2 +
3 +import 'package:bip39/bip39.dart';
4 +import 'package:collection/collection.dart';
5 +import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
6 +import 'package:cw_core/encryption_file_utils.dart';
7 +import 'package:cw_core/pathForWallet.dart';
8 +import 'package:cw_core/unspent_coins_info.dart';
9 +import 'package:cw_core/wallet_base.dart';
10 +import 'package:cw_core/wallet_info.dart';
11 +import 'package:cw_core/wallet_service.dart';
12 +import 'package:cw_core/wallet_type.dart';
13 +import 'package:cw_dogecoin/cw_dogecoin.dart';
14 +import 'package:hive/hive.dart';
15 +
16 +class DogeCoinWalletService extends WalletService<
17 + DogeCoinNewWalletCredentials,
18 + DogeCoinRestoreWalletFromSeedCredentials,
19 + DogeCoinRestoreWalletFromWIFCredentials,
20 + DogeCoinNewWalletCredentials> {
21 + DogeCoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
22 +
23 + final Box<WalletInfo> walletInfoSource;
24 + final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
25 + final bool isDirect;
26 +
27 + @override
28 + WalletType getType() => WalletType.dogecoin;
29 +
30 + @override
31 + Future<bool> isWalletExit(String name) async =>
32 + File(await pathForWallet(name: name, type: getType())).existsSync();
33 +
34 + @override
35 + Future<DogeCoinWallet> create(credentials, {bool? isTestnet}) async {
36 + final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
37 +
38 + final wallet = await DogeCoinWalletBase.create(
39 + mnemonic: credentials.mnemonic ?? MnemonicBip39.generate(strength: strength),
40 + password: credentials.password!,
41 + walletInfo: credentials.walletInfo!,
42 + unspentCoinsInfo: unspentCoinsInfoSource,
43 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
44 + passphrase: credentials.passphrase,
45 + );
46 + await wallet.save();
47 + await wallet.init();
48 +
49 + return wallet;
50 + }
51 +
52 + @override
53 + Future<DogeCoinWallet> openWallet(String name, String password) async {
54 + final walletInfo = walletInfoSource.values
55 + .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
56 +
57 + try {
58 + final wallet = await DogeCoinWalletBase.open(
59 + password: password,
60 + name: name,
61 + walletInfo: walletInfo,
62 + unspentCoinsInfo: unspentCoinsInfoSource,
63 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
64 + );
65 + await wallet.init();
66 + saveBackup(name);
67 + return wallet;
68 + } catch (_) {
69 + await restoreWalletFilesFromBackup(name);
70 + final wallet = await DogeCoinWalletBase.open(
71 + password: password,
72 + name: name,
73 + walletInfo: walletInfo,
74 + unspentCoinsInfo: unspentCoinsInfoSource,
75 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
76 + );
77 + await wallet.init();
78 + return wallet;
79 + }
80 + }
81 +
82 + @override
83 + Future<void> remove(String wallet) async {
84 + File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
85 + final walletInfo = walletInfoSource.values
86 + .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
87 + await walletInfoSource.delete(walletInfo.key);
88 +
89 + final unspentCoinsToDelete = unspentCoinsInfoSource.values
90 + .where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
91 + .toList();
92 +
93 + final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
94 +
95 + if (keysToDelete.isNotEmpty) {
96 + await unspentCoinsInfoSource.deleteAll(keysToDelete);
97 + }
98 + }
99 +
100 + @override
101 + Future<void> rename(String currentName, String password, String newName) async {
102 + final currentWalletInfo = walletInfoSource.values
103 + .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
104 + final currentWallet = await DogeCoinWalletBase.open(
105 + password: password,
106 + name: currentName,
107 + walletInfo: currentWalletInfo,
108 + unspentCoinsInfo: unspentCoinsInfoSource,
109 + encryptionFileUtils: encryptionFileUtilsFor(isDirect));
110 +
111 + await currentWallet.renameWalletFiles(newName);
112 + await saveBackup(newName);
113 +
114 + final newWalletInfo = currentWalletInfo;
115 + newWalletInfo.id = WalletBase.idFor(newName, getType());
116 + newWalletInfo.name = newName;
117 +
118 + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
119 + }
120 +
121 + @override
122 + Future<DogeCoinWallet> restoreFromHardwareWallet(DogeCoinNewWalletCredentials credentials) {
123 + throw UnimplementedError(
124 + "Restoring a Bitcoin Cash wallet from a hardware wallet is not yet supported!");
125 + }
126 +
127 + @override
128 + Future<DogeCoinWallet> restoreFromKeys(credentials, {bool? isTestnet}) {
129 + // TODO: implement restoreFromKeys
130 + throw UnimplementedError('restoreFromKeys() is not implemented');
131 + }
132 +
133 + @override
134 + Future<DogeCoinWallet> restoreFromSeed(DogeCoinRestoreWalletFromSeedCredentials credentials,
135 + {bool? isTestnet}) async {
136 + if (!validateMnemonic(credentials.mnemonic)) {
137 + throw Exception('Invalid mnemonic: ${credentials.mnemonic}');
138 + }
139 +
140 + final wallet = await DogeCoinWalletBase.create(
141 + password: credentials.password!,
142 + mnemonic: credentials.mnemonic,
143 + walletInfo: credentials.walletInfo!,
144 + unspentCoinsInfo: unspentCoinsInfoSource,
145 + encryptionFileUtils: encryptionFileUtilsFor(isDirect),
146 + passphrase: credentials.passphrase);
147 + await wallet.save();
148 + await wallet.init();
149 + return wallet;
150 + }
151 +}
cw_dogecoin/pubspec.yaml new
+81
@@ -0,0 +1,81 @@
1 +name: cw_dogecoin
2 +description: "A new Flutter package project."
3 +version: 0.0.1
4 +publish_to: none
5 +author: Cake Wallet
6 +homepage: https://cakewallet.com
7 +
8 +environment:
9 + sdk: '>=2.19.0 <3.0.0'
10 + flutter: ">=1.20.0"
11 +
12 +dependencies:
13 + flutter:
14 + sdk: flutter
15 + bip39: ^1.0.6
16 + bip32: ^2.0.0
17 + path_provider: ^2.0.11
18 + mobx: ^2.0.7+4
19 + flutter_mobx: ^2.0.6+1
20 + cw_core:
21 + path: ../cw_core
22 + cw_bitcoin:
23 + path: ../cw_bitcoin
24 +
25 + blockchain_utils:
26 + git:
27 + url: https://github.com/cake-tech/blockchain_utils
28 + ref: cake-update-v2
29 +
30 +dev_dependencies:
31 + flutter_test:
32 + sdk: flutter
33 + build_runner: ^2.4.15
34 + mobx_codegen: ^2.0.7
35 + hive_generator: ^2.0.1
36 +
37 +dependency_overrides:
38 + watcher: ^1.1.0
39 + bitcoin_base:
40 + git:
41 + url: https://github.com/cake-tech/bitcoin_base
42 + ref: cake-update-v10
43 +
44 +
45 +# For information on the generic Dart part of this file, see the
46 +# following page: https://dart.dev/tools/pub/pubspec
47 +
48 +# The following section is specific to Flutter packages.
49 +flutter:
50 + uses-material-design: true
51 +
52 + # To add assets to your package, add an assets section, like this:
53 + # assets:
54 + # - images/a_dot_burr.jpeg
55 + # - images/a_dot_ham.jpeg
56 + #
57 + # For details regarding assets in packages, see
58 + # https://flutter.dev/to/asset-from-package
59 + #
60 + # An image asset can refer to one or more resolution-specific "variants", see
61 + # https://flutter.dev/to/resolution-aware-images
62 +
63 + # To add custom fonts to your package, add a fonts section here,
64 + # in this "flutter" section. Each entry in this list should have a
65 + # "family" key with the font family name, and a "fonts" key with a
66 + # list giving the asset and other descriptors for the font. For
67 + # example:
68 + # fonts:
69 + # - family: Schyler
70 + # fonts:
71 + # - asset: fonts/Schyler-Regular.ttf
72 + # - asset: fonts/Schyler-Italic.ttf
73 + # style: italic
74 + # - family: Trajan Pro
75 + # fonts:
76 + # - asset: fonts/TrajanPro.ttf
77 + # - asset: fonts/TrajanPro_Bold.ttf
78 + # weight: 700
79 + #
80 + # For details regarding fonts in packages, see
81 + # https://flutter.dev/to/font-from-package
cw_dogecoin/test/cw_dogecoin_test.dart new
+8
@@ -0,0 +1,8 @@
1 +import 'package:flutter_test/flutter_test.dart';
2 +
3 +import 'package:cw_dogecoin/cw_dogecoin.dart';
4 +
5 +void main() {
6 + test('adds one to input values', () {
7 + });
8 +}
ios/Runner/InfoBase.plist
+19
@@ -282,6 +282,25 @@
282 <string>decred-wallet</string>
283 </array>
284 </dict>
285 + <dict>
286 + <key>CFBundleTypeRole</key>
287 + <string>Viewer</string>
288 + <key>CFBundleURLName</key>
289 + <string>dogecoin</string>
290 + <key>CFBundleURLSchemes</key>
291 + <array>
292 + <string>dash</string>
293 + </array>
294 + </dict>
295 + <dict>
296 + <key>CFBundleTypeRole</key>
297 + <string>Viewer</string>
298 + <key>CFBundleURLName</key>
299 + <string>dogecoin-wallet</string>
300 + <key>CFBundleURLSchemes</key>
301 + <array>
302 + <string>dogecoin-wallet</string>
303 + </array>
304 </array>
305 <key>CFBundleVersion</key>
306 <string>$(CURRENT_PROJECT_VERSION)</string>
lib/bitcoin/cw_bitcoin.dart
+12
@@ -184,6 +184,18 @@ class CWBitcoin extends Bitcoin {
184 return estimatedTx.amount;
185 }
186
187 +
188 + if (wallet.type == WalletType.dogecoin) {
189 + final dogeAddr =
190 + sk.getPublic().toP2pkhAddress();
191 + final estimatedTx = await electrumWallet.estimateSendAllTx(
192 + [BitcoinOutput(address: dogeAddr, value: BigInt.zero)],
193 + getFeeRate(wallet, priority as BitcoinTransactionPriority),
194 + coinTypeToSpendFrom: coinTypeToSpendFrom,
195 + );
196 + return estimatedTx.amount;
197 + }
198 +
199 final p2shAddr = sk.getPublic().toP2pkhInP2sh();
200 final estimatedTx = await electrumWallet.estimateSendAllTx(
201 [BitcoinOutput(address: p2shAddr, value: BigInt.zero)],
lib/core/seed_validator.dart
+2
@@ -29,6 +29,8 @@ class SeedValidator extends Validator<MnemonicItem> {
29 return getBitcoinWordList(language);
30 case WalletType.litecoin:
31 return getBitcoinWordList(language);
32 + case WalletType.dogecoin:
33 + return getBitcoinWordList(language);
34 case WalletType.monero:
35 return monero!.getMoneroWordList(language);
36 case WalletType.ethereum:
lib/core/wallet_creation_service.dart
+1
@@ -82,6 +82,7 @@ class WalletCreationService {
82 case WalletType.polygon:
83 case WalletType.solana:
84 case WalletType.tron:
85 + case WalletType.dogecoin:
86 return true;
87 case WalletType.monero:
88 case WalletType.wownero:
lib/di.dart
+4
@@ -274,6 +274,7 @@ import 'package:mobx/mobx.dart';
274 import 'package:shared_preferences/shared_preferences.dart';
275 import 'buy/kryptonim/kryptonim.dart';
276 import 'buy/meld/meld_buy_provider.dart';
277 +import 'dogecoin/dogecoin.dart';
278 import 'src/screens/buy/buy_sell_page.dart';
279 import 'package:cake_wallet/view_model/dev/background_sync_logs_view_model.dart';
280 import 'package:cake_wallet/src/screens/dev/background_sync_logs_page.dart';
@@ -1147,6 +1148,9 @@ Future<void> setup({
1148 case WalletType.bitcoinCash:
1149 return bitcoinCash!.createBitcoinCashWalletService(_walletInfoSource,
1150 _unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1151 + case WalletType.dogecoin:
1152 + return dogecoin!.createDogeCoinWalletService(_walletInfoSource,
1153 + _unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1154 case WalletType.nano:
1155 case WalletType.banano:
1156 return nano!.createNanoWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
lib/dogecoin/cw_dogecoin.dart new
+48
@@ -0,0 +1,48 @@
1 +part of 'dogecoin.dart';
2 +
3 +
4 +class CWDogeCoin extends DogeCoin {
5 +
6 + @override
7 + WalletService createDogeCoinWalletService(
8 + Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
9 + return DogeCoinWalletService(walletInfoSource, unspentCoinSource, isDirect);
10 + }
11 +
12 + @override
13 + WalletCredentials createDogeCoinNewWalletCredentials({
14 + required String name,
15 + WalletInfo? walletInfo,
16 + String? password,
17 + String? passphrase,
18 + String? mnemonic,
19 + }) =>
20 + DogeCoinNewWalletCredentials(
21 + name: name,
22 + walletInfo: walletInfo,
23 + password: password,
24 + passphrase: passphrase,
25 + mnemonic: mnemonic,
26 + );
27 +
28 + @override
29 + WalletCredentials createDogeCoinRestoreWalletFromSeedCredentials({
30 + required String name,
31 + required String mnemonic,
32 + required String password,
33 + String? passphrase,
34 + }) =>
35 + DogeCoinRestoreWalletFromSeedCredentials(
36 + name: name, mnemonic: mnemonic, password: password, passphrase: passphrase);
37 +
38 + @override
39 + TransactionPriority deserializeDogeCoinTransactionPriority(int raw) =>
40 + DogecoinTransactionPriority.deserialize(raw: raw);
41 +
42 + @override
43 + TransactionPriority getDefaultTransactionPriority() => DogecoinTransactionPriority.medium;
44 + @override
45 + List<TransactionPriority> getTransactionPriorities() => DogecoinTransactionPriority.all;
46 + @override
47 + TransactionPriority getDogeCoinTransactionPrioritySlow() => DogecoinTransactionPriority.slow;
48 +}
lib/dogecoin/dogecoin.dart new
+32
@@ -0,0 +1,32 @@
1 +import 'package:cw_core/transaction_priority.dart';
2 +import 'package:cw_core/unspent_coins_info.dart';
3 +import 'package:cw_core/wallet_credentials.dart';
4 +import 'package:cw_core/wallet_info.dart';
5 +import 'package:cw_core/wallet_service.dart';
6 +import 'package:hive/hive.dart';
7 +
8 +import 'package:cw_dogecoin/cw_dogecoin.dart';
9 +
10 +part 'cw_dogecoin.dart';
11 +
12 +DogeCoin? dogecoin = CWDogeCoin();
13 +
14 +abstract class DogeCoin {
15 +
16 + WalletService createDogeCoinWalletService(
17 + Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect);
18 +
19 + WalletCredentials createDogeCoinNewWalletCredentials(
20 + {required String name, WalletInfo? walletInfo, String? password, String? passphrase, String? mnemonic});
21 +
22 + WalletCredentials createDogeCoinRestoreWalletFromSeedCredentials(
23 + {required String name, required String mnemonic, required String password, String? passphrase});
24 +
25 + TransactionPriority deserializeDogeCoinTransactionPriority(int raw);
26 +
27 + TransactionPriority getDefaultTransactionPriority();
28 +
29 + List<TransactionPriority> getTransactionPriorities();
30 +
31 + TransactionPriority getDogeCoinTransactionPrioritySlow();
32 +}
lib/entities/default_settings_migration.dart
+22
@@ -46,6 +46,7 @@ const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568';
46 const zanoDefaultNodeUri = 'zano.cakewallet.com:11211';
47 const moneroWorldNodeUri = '.moneroworld.com';
48 const decredDefaultUri = "default-spv-nodes";
49 +const dogecoinDefaultNodeUri = 'dogecoin.stackwallet.com:50022';
50
51 Future<void> defaultSettingsMigration(
52 {required int version,
@@ -514,6 +515,15 @@ Future<void> defaultSettingsMigration(
515 case 50:
516 migrateExistingNodesToUseAutoSwitching(nodes: nodes, powNodes: powNodes);
517 break;
518 + case 51:
519 + await addWalletNodeList(nodes: nodes, type: WalletType.dogecoin);
520 + await _changeDefaultNode(
521 + nodes: nodes,
522 + sharedPreferences: sharedPreferences,
523 + type: WalletType.dogecoin,
524 + currentNodePreferenceKey: PreferencesKey.currentDogecoinNodeIdKey,
525 + );
526 + break;
527 default:
528 break;
529 }
@@ -620,6 +630,8 @@ String _getDefaultNodeUri(WalletType type) {
630 return zanoDefaultNodeUri;
631 case WalletType.decred:
632 return decredDefaultUri;
633 + case WalletType.dogecoin:
634 + return dogecoinDefaultNodeUri;
635 case WalletType.banano:
636 case WalletType.none:
637 return '';
@@ -1050,6 +1062,8 @@ Future<void> checkCurrentNodes(
1062 final currentDecredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
1063 final currentBitcoinCashNodeId =
1064 sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
1065 + final currentDogecoinNodeId =
1066 + sharedPreferences.getInt(PreferencesKey.currentDogecoinNodeIdKey);
1067 final currentSolanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
1068 final currentTronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey);
1069 final currentWowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
@@ -1074,6 +1088,8 @@ Future<void> checkCurrentNodes(
1088 powNodeSource.values.firstWhereOrNull((node) => node.key == currentNanoPowNodeId);
1089 final currentBitcoinCashNodeServer =
1090 nodeSource.values.firstWhereOrNull((node) => node.key == currentBitcoinCashNodeId);
1091 + final currentDogecoinNodeServer =
1092 + nodeSource.values.firstWhereOrNull((node) => node.key == currentDogecoinNodeId);
1093 final currentSolanaNodeServer =
1094 nodeSource.values.firstWhereOrNull((node) => node.key == currentSolanaNodeId);
1095 final currentTronNodeServer =
@@ -1143,6 +1159,12 @@ Future<void> checkCurrentNodes(
1159 await sharedPreferences.setInt(PreferencesKey.currentBitcoinCashNodeIdKey, node.key as int);
1160 }
1161
1162 + if (currentDogecoinNodeServer == null) {
1163 + final node = Node(uri: dogecoinDefaultNodeUri, type: WalletType.dogecoin, useSSL: true);
1164 + await nodeSource.add(node);
1165 + await sharedPreferences.setInt(PreferencesKey.currentDogecoinNodeIdKey, node.key as int);
1166 + }
1167 +
1168 if (currentPolygonNodeServer == null) {
1169 final node = Node(uri: polygonDefaultNodeUri, type: WalletType.polygon);
1170 await nodeSource.add(node);
lib/entities/node_list.dart
+6 -1
@@ -46,6 +46,9 @@ Future<List<Node>> loadDefaultNodes(WalletType type) async {
46 case WalletType.decred:
47 path = 'assets/decred_node_list.yml';
48 break;
49 + case WalletType.dogecoin:
50 + path = 'assets/dogecoin_electrum_server_list.yml';
51 + break;
52 case WalletType.banano:
53 case WalletType.none:
54 path = '';
@@ -96,6 +99,7 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
99 final tronNodes = await loadDefaultNodes(WalletType.tron);
100 final decredNodes = await loadDefaultNodes(WalletType.decred);
101 final zanoNodes = await loadDefaultNodes(WalletType.zano);
102 + final dogecoinElectrumServerList = await loadDefaultNodes(WalletType.dogecoin);
103
104 final nodes = moneroNodes +
105 bitcoinElectrumServerList +
@@ -108,7 +112,8 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
112 solanaNodes +
113 tronNodes +
114 zanoNodes +
111 - decredNodes;
115 + decredNodes +
116 + dogecoinElectrumServerList;
117
118 await nodeSource.clear();
119 await nodeSource.addAll(nodes);
lib/entities/preferences_key.dart
+1
@@ -11,6 +11,7 @@ class PreferencesKey {
11 static const currentNanoNodeIdKey = 'current_node_id_nano';
12 static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
13 static const currentDecredNodeIdKey = 'current_node_id_decred';
14 + static const currentDogecoinNodeIdKey = 'current_node_id_doge';
15 static const currentBananoNodeIdKey = 'current_node_id_banano';
16 static const currentBananoPowNodeIdKey = 'current_node_id_banano_pow';
17 static const currentFiatCurrencyKey = 'current_fiat_currency';
lib/entities/priority_for_wallet_type.dart
+3
@@ -1,5 +1,6 @@
1 import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
3 +import 'package:cake_wallet/dogecoin/dogecoin.dart';
4 import 'package:cake_wallet/ethereum/ethereum.dart';
5 import 'package:cake_wallet/monero/monero.dart';
6 import 'package:cake_wallet/polygon/polygon.dart';
@@ -23,6 +24,8 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
24 return ethereum!.getTransactionPriorities();
25 case WalletType.bitcoinCash:
26 return bitcoinCash!.getTransactionPriorities();
27 + case WalletType.dogecoin:
28 + return dogecoin!.getTransactionPriorities();
29 case WalletType.polygon:
30 return polygon!.getTransactionPriorities();
31 // no such thing for nano/banano/solana/tron:
lib/main.dart
+1 -1
@@ -250,7 +250,7 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
250 payjoinSessionSource: payjoinSessionSource,
251 anonpayInvoiceInfo: anonpayInvoiceInfo,
252 havenSeedStore: havenSeedStore,
253 - initialMigrationVersion: 50,
253 + initialMigrationVersion: 51,
254 );
255 }
256
lib/reactions/on_current_wallet_change.dart
+1
@@ -76,6 +76,7 @@ void startCurrentWalletChangeReaction(
76 wallet.type == WalletType.bitcoin ||
77 wallet.type == WalletType.litecoin ||
78 wallet.type == WalletType.bitcoinCash ||
79 + wallet.type == WalletType.dogecoin ||
80 wallet.type == WalletType.decred) {
81 _setAutoGenerateSubaddressStatus(wallet, settingsStore);
82 }
lib/reactions/wallet_utils.dart
+1
@@ -12,6 +12,7 @@ bool isBIP39Wallet(WalletType walletType) {
12 case WalletType.nano:
13 case WalletType.banano:
14 case WalletType.monero:
15 + case WalletType.dogecoin:
16 return true;
17 case WalletType.wownero:
18 case WalletType.haven:
lib/src/screens/dashboard/widgets/menu_widget.dart
+5 -1
@@ -37,7 +37,8 @@ class MenuWidgetState extends State<MenuWidget> {
37 this.tronIcon = Image.asset('assets/images/trx_icon.png'),
38 this.wowneroIcon = Image.asset('assets/images/wownero_icon.png'),
39 this.zanoIcon = Image.asset('assets/images/zano_icon.png'),
40 - this.decredIcon = Image.asset('assets/images/decred_menu.png');
40 + this.decredIcon = Image.asset('assets/images/decred_menu.png'),
41 + this.dogecoinIcon = Image.asset('assets/images/doge_icon.png');
42
43 final largeScreen = 731;
44
@@ -64,6 +65,7 @@ class MenuWidgetState extends State<MenuWidget> {
65 Image wowneroIcon;
66 Image zanoIcon;
67 Image decredIcon;
68 + Image dogecoinIcon;
69
70 @override
71 void initState() {
@@ -255,6 +257,8 @@ class MenuWidgetState extends State<MenuWidget> {
257 return zanoIcon;
258 case WalletType.decred:
259 return decredIcon;
260 + case WalletType.dogecoin:
261 + return dogecoinIcon;
262 default:
263 throw Exception('No icon for ${type.toString()}');
264 }
lib/store/settings_store.dart
+17
@@ -1070,6 +1070,7 @@ abstract class SettingsStoreBase with Store {
1070 final wowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
1071 final zanoNodeId = sharedPreferences.getInt(PreferencesKey.currentZanoNodeIdKey);
1072 final decredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
1073 + final dogecoinNodeId = sharedPreferences.getInt(PreferencesKey.currentDogecoinNodeIdKey);
1074
1075 /// get the selected node, if null, then use the default
1076 final moneroNode = nodeSource.get(nodeId) ??
@@ -1098,6 +1099,8 @@ abstract class SettingsStoreBase with Store {
1099 nodeSource.values.firstWhereOrNull((e) => e.uriRaw == wowneroDefaultNodeUri);
1100 final zanoNode = nodeSource.get(zanoNodeId) ??
1101 nodeSource.values.firstWhereOrNull((e) => e.uriRaw == zanoDefaultNodeUri);
1102 + final dogecoinNode = nodeSource.get(dogecoinNodeId) ??
1103 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == dogecoinDefaultNodeUri);
1104
1105 final packageInfo = await PackageInfo.fromPlatform();
1106 final deviceName = await _getDeviceName() ?? '';
@@ -1185,6 +1188,10 @@ abstract class SettingsStoreBase with Store {
1188 nodes[WalletType.decred] = decredNode;
1189 }
1190
1191 + if (dogecoinNode != null) {
1192 + nodes[WalletType.dogecoin] = dogecoinNode;
1193 + }
1194 +
1195 final savedSyncMode = SyncMode.all.firstWhere((element) {
1196 return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 2); // default to 2 - daily sync
1197 });
@@ -1549,6 +1556,7 @@ abstract class SettingsStoreBase with Store {
1556 final wowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
1557 final zanoNodeId = sharedPreferences.getInt(PreferencesKey.currentZanoNodeIdKey);
1558 final decredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
1559 + final dogecoinNodeId = sharedPreferences.getInt(PreferencesKey.currentDogecoinNodeIdKey);
1560 final moneroNode = nodeSource.get(nodeId);
1561 final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
1562 final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
@@ -1562,6 +1570,7 @@ abstract class SettingsStoreBase with Store {
1570 final wowneroNode = nodeSource.get(wowneroNodeId);
1571 final zanoNode = nodeSource.get(zanoNodeId);
1572 final decredNode = nodeSource.get(decredNodeId);
1573 + final dogecoinNode = nodeSource.get(dogecoinNodeId);
1574
1575 if (moneroNode != null) {
1576 nodes[WalletType.monero] = moneroNode;
@@ -1616,6 +1625,10 @@ abstract class SettingsStoreBase with Store {
1625 nodes[WalletType.decred] = decredNode;
1626 }
1627
1628 + if (dogecoinNode != null) {
1629 + nodes[WalletType.dogecoin] = dogecoinNode;
1630 + }
1631 +
1632 // MIGRATED:
1633
1634 useTOTP2FA = await SecureKey.getBool(
@@ -1757,6 +1770,10 @@ abstract class SettingsStoreBase with Store {
1770 break;
1771 case WalletType.zano:
1772 await _sharedPreferences.setInt(PreferencesKey.currentZanoNodeIdKey, node.key as int);
1773 + break;
1774 + case WalletType.dogecoin:
1775 + await _sharedPreferences.setInt(PreferencesKey.currentDogecoinNodeIdKey, node.key as int);
1776 + break;
1777 default:
1778 break;
1779 }
lib/view_model/advanced_privacy_settings_view_model.dart
+2
@@ -37,6 +37,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
37 switch (type) {
38 case WalletType.ethereum:
39 case WalletType.bitcoinCash:
40 + case WalletType.dogecoin:
41 case WalletType.polygon:
42 case WalletType.solana:
43 case WalletType.tron:
@@ -83,6 +84,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
84 WalletType.monero,
85 WalletType.wownero,
86 WalletType.zano,
87 + WalletType.dogecoin,
88 ].contains(type);
89
90 @computed
lib/view_model/dashboard/dashboard_view_model.dart
+1
@@ -865,6 +865,7 @@ abstract class DashboardViewModelBase with Store {
865 case WalletType.tron:
866 case WalletType.wownero:
867 case WalletType.decred:
868 + case WalletType.dogecoin:
869 return true;
870 case WalletType.zano:
871 case WalletType.haven:
lib/view_model/dashboard/home_settings_view_model.dart
+1
@@ -242,6 +242,7 @@ abstract class HomeSettingsViewModelBase with Store {
242 case WalletType.wownero:
243 case WalletType.bitcoinCash:
244 case WalletType.decred:
245 + case WalletType.dogecoin:
246 return false;
247 }
248
lib/view_model/dashboard/sign_view_model.dart
+1
@@ -24,6 +24,7 @@ abstract class SignViewModelBase with Store {
24 WalletType.bitcoin,
25 WalletType.bitcoinCash,
26 WalletType.litecoin,
27 + WalletType.dogecoin,
28 WalletType.haven,
29 ].contains(wallet.type);
30
lib/view_model/dashboard/transaction_list_item.dart
+1
@@ -167,6 +167,7 @@ class TransactionListItem extends ActionListItem with Keyable {
167 case WalletType.bitcoin:
168 case WalletType.litecoin:
169 case WalletType.bitcoinCash:
170 + case WalletType.dogecoin:
171 amount = calculateFiatAmountRaw(
172 cryptoAmount: bitcoin!.formatterBitcoinAmountToDouble(amount: transaction.amount),
173 price: price);
lib/view_model/exchange/exchange_view_model.dart
+7 -1
@@ -166,7 +166,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
166 }
167
168 bool get isElectrumWallet =>
169 - [WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type);
169 + [WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash,WalletType.dogecoin ].contains(wallet.type);
170
171 bool get hideAddressAfterExchange =>
172 [WalletType.monero, WalletType.wownero].contains(wallet.type);
@@ -314,6 +314,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
314 WalletType.bitcoin,
315 WalletType.litecoin,
316 WalletType.bitcoinCash,
317 + WalletType.dogecoin,
318 ].contains(wallet.type) &&
319 depositCurrency == wallet.currency;
320
@@ -673,6 +674,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
674 WalletType.litecoin,
675 WalletType.bitcoin,
676 WalletType.bitcoinCash,
677 + WalletType.dogecoin,
678 ].contains(wallet.type)) {
679 final priority = _settingsStore.priority[wallet.type]!;
680
@@ -751,6 +753,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
753 depositCurrency = CryptoCurrency.bch;
754 receiveCurrency = CryptoCurrency.xmr;
755 break;
756 + case WalletType.dogecoin:
757 + depositCurrency = CryptoCurrency.doge;
758 + receiveCurrency = CryptoCurrency.xmr;
759 + break;
760 case WalletType.haven:
761 depositCurrency = CryptoCurrency.xhv;
762 receiveCurrency = CryptoCurrency.btc;
lib/view_model/node_list/node_create_or_edit_view_model.dart
+1
@@ -93,6 +93,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
93 case WalletType.litecoin:
94 case WalletType.bitcoinCash:
95 case WalletType.bitcoin:
96 + case WalletType.dogecoin:
97 case WalletType.zano:
98 case WalletType.decred:
99 return false;
lib/view_model/restore/wallet_restore_from_qr_code.dart
+3
@@ -47,6 +47,9 @@ class WalletRestoreFromQRCode {
47 'decred': WalletType.decred,
48 'decred-wallet': WalletType.decred,
49 'decred_wallet': WalletType.decred,
50 + 'dogecoin': WalletType.dogecoin,
51 + 'dogecoin-wallet': WalletType.dogecoin,
52 + 'dogecoin_wallet': WalletType.dogecoin
53 };
54
55 static WalletType? _extractWalletType(String code) {
lib/view_model/send/fees_view_model.dart
+8 -1
@@ -1,5 +1,6 @@
1 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
2 import 'package:cake_wallet/decred/decred.dart';
3 +import 'package:cake_wallet/dogecoin/dogecoin.dart';
4 import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
5 import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
6 import 'package:cake_wallet/ethereum/ethereum.dart';
@@ -91,6 +92,8 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
92 return transactionPriority == polygon!.getPolygonTransactionPrioritySlow();
93 case WalletType.decred:
94 return transactionPriority == decred!.getDecredTransactionPrioritySlow();
95 + case WalletType.dogecoin:
96 + return transactionPriority == dogecoin!.getDogeCoinTransactionPrioritySlow();
97 case WalletType.none:
98 case WalletType.nano:
99 case WalletType.banano:
@@ -119,7 +122,8 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
122 bool get isElectrumWallet =>
123 wallet.type == WalletType.bitcoin ||
124 wallet.type == WalletType.litecoin ||
122 - wallet.type == WalletType.bitcoinCash;
125 + wallet.type == WalletType.bitcoinCash ||
126 + wallet.type == WalletType.dogecoin;
127
128 String? get walletCurrencyName => wallet.currency.fullName?.toLowerCase() ?? wallet.currency.name;
129
@@ -188,6 +192,9 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
192 case WalletType.bitcoinCash:
193 _settingsStore.priority[wallet.type] = bitcoinCash!.getDefaultTransactionPriority();
194 break;
195 + case WalletType.dogecoin:
196 + _settingsStore.priority[wallet.type] = dogecoin!.getDefaultTransactionPriority();
197 + break;
198 case WalletType.polygon:
199 _settingsStore.priority[wallet.type] = polygon!.getDefaultTransactionPriority();
200 break;
lib/view_model/send/output.dart
+4 -1
@@ -99,6 +99,7 @@ abstract class OutputBase with Store {
99 case WalletType.bitcoin:
100 case WalletType.litecoin:
101 case WalletType.bitcoinCash:
102 + case WalletType.dogecoin:
103 _amount = bitcoin!.formatterStringDoubleToBitcoinAmount(_cryptoAmount);
104 break;
105 case WalletType.decred:
@@ -166,7 +167,8 @@ abstract class OutputBase with Store {
167 return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
168 }
169
169 - if (_wallet.type == WalletType.litecoin || _wallet.type == WalletType.bitcoinCash) {
170 + if (_wallet.type == WalletType.litecoin || _wallet.type == WalletType.bitcoinCash ||
171 + _wallet.type == WalletType.dogecoin) {
172 return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
173 }
174
@@ -316,6 +318,7 @@ abstract class OutputBase with Store {
318 case WalletType.bitcoin:
319 case WalletType.litecoin:
320 case WalletType.bitcoinCash:
321 + case WalletType.dogecoin:
322 maximumFractionDigits = 8;
323 break;
324 case WalletType.wownero:
lib/view_model/send/send_view_model.dart
+5 -2
@@ -269,6 +269,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
269 case WalletType.bitcoin:
270 case WalletType.litecoin:
271 case WalletType.bitcoinCash:
272 + case WalletType.dogecoin:
273 case WalletType.monero:
274 case WalletType.wownero:
275 case WalletType.decred:
@@ -317,12 +318,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
318 WalletType.monero,
319 WalletType.wownero,
320 WalletType.decred,
320 - WalletType.bitcoinCash
321 + WalletType.bitcoinCash,
322 + WalletType.dogecoin
323 ].contains(wallet.type);
324
325 @computed
326 bool get isElectrumWallet =>
325 - [WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type);
327 + [WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash, WalletType.dogecoin].contains(wallet.type);
328
329 @observable
330 CryptoCurrency selectedCryptoCurrency;
@@ -652,6 +654,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
654 switch (wallet.type) {
655 case WalletType.bitcoin:
656 case WalletType.bitcoinCash:
657 + case WalletType.dogecoin:
658 return bitcoin!.createBitcoinTransactionCredentials(
659 outputs,
660 priority: priority!,
lib/view_model/settings/other_settings_view_model.dart
+4 -2
@@ -70,7 +70,8 @@ abstract class OtherSettingsViewModelBase with Store {
70
71 if (_wallet.type == WalletType.bitcoin ||
72 _wallet.type == WalletType.litecoin ||
73 - _wallet.type == WalletType.bitcoinCash) {
73 + _wallet.type == WalletType.bitcoinCash ||
74 + _wallet.type == WalletType.dogecoin) {
75 final rate = bitcoin!.getFeeRate(_wallet, _priority);
76 return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate);
77 }
@@ -83,7 +84,8 @@ abstract class OtherSettingsViewModelBase with Store {
84
85 if (_wallet.type == WalletType.bitcoin ||
86 _wallet.type == WalletType.litecoin ||
86 - _wallet.type == WalletType.bitcoinCash) {
87 + _wallet.type == WalletType.bitcoinCash ||
88 + _wallet.type == WalletType.dogecoin) {
89 final rate = bitcoin!.getFeeRate(_wallet, _priority);
90 return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate,
91 customRate: customValue);
lib/view_model/settings/privacy_settings_view_model.dart
+1
@@ -43,6 +43,7 @@ abstract class PrivacySettingsViewModelBase with Store {
43 WalletType.bitcoin,
44 WalletType.litecoin,
45 WalletType.bitcoinCash,
46 + WalletType.dogecoin,
47 WalletType.decred
48 ].contains(_wallet.type);
49
lib/view_model/transaction_details_view_model.dart
+51
@@ -88,6 +88,9 @@ abstract class TransactionDetailsViewModelBase with Store {
88 case WalletType.decred:
89 _addDecredListItems(tx, dateFormat);
90 break;
91 + case WalletType.dogecoin:
92 + _addDogecoinListItems(tx, dateFormat);
93 + break;
94 case WalletType.none:
95 case WalletType.banano:
96 break;
@@ -193,6 +196,8 @@ abstract class TransactionDetailsViewModelBase with Store {
196 return 'https://explorer.zano.org/transaction/${txId}';
197 case WalletType.decred:
198 return 'https://${wallet.isTestnet ? "testnet" : "dcrdata"}.decred.org/tx/${txId.split(':')[0]}';
199 + case WalletType.dogecoin:
200 + return 'https://blockchair.com/dogecoin/transaction/${txId}';
201 case WalletType.none:
202 return '';
203 }
@@ -206,6 +211,7 @@ abstract class TransactionDetailsViewModelBase with Store {
211 return S.current.view_transaction_on + 'mempool.space';
212 case WalletType.litecoin:
213 case WalletType.bitcoinCash:
214 + case WalletType.dogecoin:
215 return S.current.view_transaction_on + 'Blockchair.com';
216 case WalletType.haven:
217 return S.current.view_transaction_on + 'explorer.havenprotocol.org';
@@ -750,6 +756,51 @@ abstract class TransactionDetailsViewModelBase with Store {
756 items.addAll(_items);
757 }
758
759 + void _addDogecoinListItems(TransactionInfo tx, DateFormat dateFormat) {
760 + final _items = [
761 + StandartListItem(
762 + title: S.current.transaction_details_transaction_id,
763 + value: tx.txHash,
764 + key: ValueKey('standard_list_item_transaction_details_id_key'),
765 + ),
766 + StandartListItem(
767 + title: S.current.transaction_details_date,
768 + value: dateFormat.format(tx.date),
769 + key: ValueKey('standard_list_item_transaction_details_date_key'),
770 + ),
771 + StandartListItem(
772 + title: S.current.transaction_details_height,
773 + value: '${tx.height}',
774 + key: ValueKey('standard_list_item_transaction_details_height_key'),
775 + ),
776 + StandartListItem(
777 + title: S.current.transaction_details_amount,
778 + value: tx.amountFormatted(),
779 + key: ValueKey('standard_list_item_transaction_details_amount_key'),
780 + ),
781 + if (tx.feeFormatted()?.isNotEmpty ?? false)
782 + StandartListItem(
783 + title: S.current.transaction_details_fee,
784 + value: tx.feeFormatted()!,
785 + key: ValueKey('standard_list_item_transaction_details_fee_key'),
786 + ),
787 + if (showRecipientAddress && tx.to != null)
788 + StandartListItem(
789 + title: S.current.transaction_details_recipient_address,
790 + value: tx.to!,
791 + key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
792 + ),
793 + if (tx.from != null)
794 + StandartListItem(
795 + title: S.current.transaction_details_source_address,
796 + value: tx.from!,
797 + key: ValueKey('standard_list_item_transaction_details_source_address_key'),
798 + ),
799 + ];
800 +
801 + items.addAll(_items);
802 + }
803 +
804 @action
805 Future<void> _checkForRBF(TransactionInfo tx) async {
806 if (wallet.type == WalletType.bitcoin &&
lib/view_model/unspent_coins/unspent_coins_details_view_model.dart
+5 -1
@@ -49,7 +49,7 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
49 })
50 ];
51
52 - if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(_type)) {
52 + if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash, WalletType.dogecoin].contains(_type)) {
53 items.add(BlockExplorerListItem(
54 title: S.current.view_in_block_explorer,
55 value: _explorerDescription(_type),
@@ -71,6 +71,8 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
71 return 'https://litecoin.earlyordies.com/tx/${txId}';
72 case WalletType.bitcoinCash:
73 return 'https://blockchair.com/bitcoin-cash/transaction/${txId}';
74 + case WalletType.dogecoin:
75 + return 'https://dogechain.info/tx/${txId}';
76 default:
77 return '';
78 }
@@ -84,6 +86,8 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
86 return S.current.view_transaction_on + 'Earlyordies.com';
87 case WalletType.bitcoinCash:
88 return S.current.view_transaction_on + 'Blockchair.com';
89 + case WalletType.dogecoin:
90 + return S.current.view_transaction_on + 'Dogechain.info';
91 default:
92 return '';
93 }
lib/view_model/unspent_coins/unspent_coins_list_view_model.dart
+4 -2
@@ -91,7 +91,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
91 return monero!.formatterMoneroAmountToString(amount: fullBalance);
92 if (wallet.type == WalletType.wownero)
93 return wownero!.formatterWowneroAmountToString(amount: fullBalance);
94 - if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type))
94 + if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash, WalletType.dogecoin].contains(wallet.type))
95 return bitcoin!.formatterBitcoinAmountToString(amount: fullBalance);
96 if (wallet.type == WalletType.decred)
97 return decred!.formatterDecredAmountToString(amount: fullBalance);
@@ -105,7 +105,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
105 if (wallet.type == WalletType.wownero) {
106 await wownero!.updateUnspents(wallet);
107 }
108 - if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type)) {
108 + if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash, WalletType.dogecoin].contains(wallet.type)) {
109 await bitcoin!.updateUnspents(wallet);
110 }
111 if (wallet.type == WalletType.decred) {
@@ -123,6 +123,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
123 case WalletType.bitcoin:
124 case WalletType.litecoin:
125 case WalletType.bitcoinCash:
126 + case WalletType.dogecoin:
127 return bitcoin!.getUnspents(wallet, coinTypeToSpendFrom: coinTypeToSpendFrom);
128 case WalletType.decred:
129 return decred!.getUnspents(wallet);
@@ -140,6 +141,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
141 case WalletType.bitcoin:
142 case WalletType.litecoin:
143 case WalletType.bitcoinCash:
144 + case WalletType.dogecoin:
145 return bitcoin!.getUnspents(wallet, coinTypeToSpendFrom: overrideCoinTypeToSpendFrom);
146 case WalletType.decred:
147 return decred!.getUnspents(wallet);
lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart
+2 -1
@@ -48,7 +48,8 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
48 bool get isElectrum =>
49 _wallet.type == WalletType.bitcoin ||
50 _wallet.type == WalletType.bitcoinCash ||
51 - _wallet.type == WalletType.litecoin;
51 + _wallet.type == WalletType.litecoin ||
52 + _wallet.type == WalletType.dogecoin;
53
54 Future<void> save() async {
55 try {
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+21 -2
@@ -242,6 +242,22 @@ class DecredURI extends PaymentURI {
242 }
243 }
244
245 +class DogeURI extends PaymentURI {
246 + DogeURI({required String amount, required String address})
247 + : super(amount: amount, address: address);
248 +
249 + @override
250 + String toString() {
251 + var base = 'doge:' + address;
252 +
253 + if (amount.isNotEmpty) {
254 + base += '?amount=${amount.replaceAll(',', '.')}';
255 + }
256 +
257 + return base;
258 + }
259 +}
260 +
261 abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
262 WalletAddressListViewModelBase({
263 required AppStore appStore,
@@ -350,6 +366,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
366 return ZanoURI(amount: amount, address: address.address);
367 case WalletType.decred:
368 return DecredURI(amount: amount, address: address.address);
369 + case WalletType.dogecoin:
370 + return DogeURI(amount: amount, address: address.address);
371 case WalletType.none:
372 throw Exception('Unexpected type: ${type.toString()}');
373 }
@@ -586,12 +604,13 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
604 WalletType.bitcoinCash,
605 WalletType.bitcoin,
606 WalletType.litecoin,
589 - WalletType.decred
607 + WalletType.decred,
608 + WalletType.dogecoin,
609 ].contains(wallet.type);
610
611 @computed
612 bool get isElectrumWallet =>
594 - [WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type);
613 + [WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash, WalletType.dogecoin].contains(wallet.type);
614
615 @computed
616 bool get isBalanceAvailable => isElectrumWallet;
lib/view_model/wallet_keys_view_model.dart
+3
@@ -164,6 +164,7 @@ abstract class WalletKeysViewModelBase with Store {
164 case WalletType.bitcoin:
165 case WalletType.litecoin:
166 case WalletType.bitcoinCash:
167 + case WalletType.dogecoin:
168 if (_wallet.type == WalletType.bitcoin) {
169 keys = bitcoin!.getSilentPaymentKeys(_appStore.wallet!);
170 }
@@ -261,6 +262,8 @@ abstract class WalletKeysViewModelBase with Store {
262 return 'zano-wallet';
263 case WalletType.decred:
264 return 'decred-wallet';
265 + case WalletType.dogecoin:
266 + return 'dogecoin-wallet';
267 default:
268 throw Exception('Unexpected wallet type: ${_wallet.type.toString()}');
269 }
lib/view_model/wallet_new_vm.dart
+8
@@ -1,4 +1,5 @@
1 import 'package:cake_wallet/core/new_wallet_arguments.dart';
2 +import 'package:cake_wallet/dogecoin/dogecoin.dart';
3 import 'package:cake_wallet/ethereum/ethereum.dart';
4 import 'package:cake_wallet/zano/zano.dart';
5 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
@@ -96,6 +97,13 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
97 passphrase: passphrase,
98 mnemonic: newWalletArguments!.mnemonic,
99 );
100 + case WalletType.dogecoin:
101 + return dogecoin!.createDogeCoinNewWalletCredentials(
102 + name: name,
103 + password: walletPassword,
104 + passphrase: passphrase,
105 + mnemonic: newWalletArguments!.mnemonic,
106 + );
107 case WalletType.nano:
108 case WalletType.banano:
109 return nano!.createNanoNewWalletCredentials(
lib/view_model/wallet_restore_view_model.dart
+9
@@ -3,6 +3,7 @@ import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
3 import 'package:cake_wallet/core/generate_wallet_password.dart';
4 import 'package:cake_wallet/core/wallet_creation_service.dart';
5 import 'package:cake_wallet/di.dart';
6 +import 'package:cake_wallet/dogecoin/dogecoin.dart';
7 import 'package:cake_wallet/ethereum/ethereum.dart';
8 import 'package:cake_wallet/monero/monero.dart';
9 import 'package:cake_wallet/nano/nano.dart';
@@ -58,6 +59,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
59 case WalletType.bitcoinCash:
60 case WalletType.zano:
61 case WalletType.none:
62 + case WalletType.dogecoin:
63 availableModes = [WalletRestoreMode.seed];
64 break;
65 }
@@ -148,6 +150,13 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
150 password: password,
151 passphrase: passphrase,
152 );
153 + case WalletType.dogecoin:
154 + return dogecoin!.createDogeCoinRestoreWalletFromSeedCredentials(
155 + name: name,
156 + mnemonic: seed,
157 + password: password,
158 + passphrase: passphrase,
159 + );
160 case WalletType.nano:
161 case WalletType.banano:
162 return nano!.createNanoRestoreWalletFromSeedCredentials(
model_generator.sh
+1 -1
@@ -1,7 +1,7 @@
1 #!/bin/bash
2 set -x -e
3
4 -for cwcoin in cw_{core,evm,monero,bitcoin,nano,bitcoin_cash,solana,tron,wownero,zano,decred}
4 +for cwcoin in cw_{core,evm,monero,bitcoin,nano,bitcoin_cash,solana,tron,wownero,zano,decred,dogecoin}
5 do
6 if [[ "x$1" == "xasync" ]];
7 then
pubspec_base.yaml
+2 -1
@@ -177,7 +177,7 @@ dependency_overrides:
177 bitcoin_base:
178 git:
179 url: https://github.com/cake-tech/bitcoin_base
180 - ref: cake-update-v10
180 + ref: cake-update-v11
181 ffi: 2.1.0
182 ledger_flutter_plus:
183 git:
@@ -223,6 +223,7 @@ flutter:
223 - assets/wownero_node_list.yml
224 - assets/zano_node_list.yml
225 - assets/decred_node_list.yml
226 + - assets/dogecoin_electrum_server_list.yml
227 - assets/text/
228 - assets/faq/
229 - assets/animation/
scripts/android/pubspec_gen.sh
+1 -1
@@ -10,7 +10,7 @@ case $APP_ANDROID_TYPE in
10 CONFIG_ARGS="--monero"
11 ;;
12 $CAKEWALLET)
13 - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred"
13 + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred --dogecoin"
14 ;;
15 esac
16
scripts/ios/app_config.sh
+1 -1
@@ -31,7 +31,7 @@ case $APP_IOS_TYPE in
31 ;;
32
33 $CAKEWALLET)
34 - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred"
34 + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano --decred --dogecoin"
35 ;;
36 esac
37
scripts/linux/app_config.sh
+1 -1
@@ -13,7 +13,7 @@ CONFIG_ARGS=""
13
14 case $APP_LINUX_TYPE in
15 $CAKEWALLET)
16 - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --excludeFlutterSecureStorage";;
16 + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --dogecoin --excludeFlutterSecureStorage";;
17 esac
18
19 cp -rf pubspec_description.yaml pubspec.yaml
scripts/macos/app_config.sh
+1 -1
@@ -36,7 +36,7 @@ case $APP_MACOS_TYPE in
36 $MONERO_COM)
37 CONFIG_ARGS="--monero";;
38 $CAKEWALLET)
39 - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero";;
39 + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --dogecoin";;
40 esac
41
42 cp -rf pubspec_description.yaml pubspec.yaml
tool/configure.dart
+74
@@ -11,6 +11,7 @@ const tronOutputPath = 'lib/tron/tron.dart';
11 const wowneroOutputPath = 'lib/wownero/wownero.dart';
12 const zanoOutputPath = 'lib/zano/zano.dart';
13 const decredOutputPath = 'lib/decred/decred.dart';
14 +const dogecoinOutputPath = 'lib/dogecoin/dogecoin.dart';
15 const walletTypesPath = 'lib/wallet_types.g.dart';
16 const secureStoragePath = 'lib/core/secure_storage.dart';
17 const pubspecDefaultPath = 'pubspec_default.yaml';
@@ -30,6 +31,7 @@ Future<void> main(List<String> args) async {
31 final hasWownero = args.contains('${prefix}wownero');
32 final hasZano = args.contains('${prefix}zano');
33 final hasDecred = args.contains('${prefix}decred');
34 + final hasDogecoin = args.contains('${prefix}dogecoin');
35 final excludeFlutterSecureStorage = args.contains('${prefix}excludeFlutterSecureStorage');
36
37 await generateBitcoin(hasBitcoin);
@@ -44,6 +46,7 @@ Future<void> main(List<String> args) async {
46 await generateZano(hasZano);
47 // await generateBanano(hasEthereum);
48 await generateDecred(hasDecred);
49 + await generateDogecoin(hasDogecoin);
50
51 await generatePubspec(
52 hasMonero: hasMonero,
@@ -59,6 +62,7 @@ Future<void> main(List<String> args) async {
62 hasWownero: hasWownero,
63 hasZano: hasZano,
64 hasDecred: hasDecred,
65 + hasDogecoin: hasDogecoin,
66 );
67 await generateWalletTypes(
68 hasMonero: hasMonero,
@@ -73,6 +77,7 @@ Future<void> main(List<String> args) async {
77 hasWownero: hasWownero,
78 hasZano: hasZano,
79 hasDecred: hasDecred,
80 + hasDogecoin: hasDogecoin,
81 );
82 await injectSecureStorage(!excludeFlutterSecureStorage);
83 }
@@ -1414,6 +1419,61 @@ abstract class Decred {
1419 await outputFile.writeAsString(output);
1420 }
1421
1422 +Future<void> generateDogecoin(bool hasImplementation) async {
1423 + final outputFile = File(dogecoinOutputPath);
1424 + const dogecoinCommonHeaders = """
1425 +import 'package:cw_core/transaction_priority.dart';
1426 +import 'package:cw_core/unspent_coins_info.dart';
1427 +import 'package:cw_core/wallet_credentials.dart';
1428 +import 'package:cw_core/wallet_info.dart';
1429 +import 'package:cw_core/wallet_service.dart';
1430 +import 'package:hive/hive.dart';
1431 +""";
1432 + const dogecoinCWHeaders = """
1433 +import 'package:cw_dogecoin/cw_dogecoin.dart';
1434 +""";
1435 + const dogecoinCwPart = "part 'cw_dogecoin.dart';";
1436 + const dogecoinContent = """
1437 +abstract class DogeCoin {
1438 +
1439 + WalletService createDogeCoinWalletService(
1440 + Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect);
1441 +
1442 + WalletCredentials createDogeCoinNewWalletCredentials(
1443 + {required String name, WalletInfo? walletInfo, String? password, String? passphrase, String? mnemonic});
1444 +
1445 + WalletCredentials createDogeCoinRestoreWalletFromSeedCredentials(
1446 + {required String name, required String mnemonic, required String password, String? passphrase});
1447 +
1448 + TransactionPriority deserializeDogeCoinTransactionPriority(int raw);
1449 +
1450 + TransactionPriority getDefaultTransactionPriority();
1451 +
1452 + List<TransactionPriority> getTransactionPriorities();
1453 +
1454 + TransactionPriority getDogeCoinTransactionPrioritySlow();
1455 +}
1456 +""";
1457 +
1458 + const dogecoinEmptyDefinition = 'DogeCoin? dogecoin;\n';
1459 + const dogecoinCWDefinition = 'DogeCoin? dogecoin = CWDogeCoin();\n';
1460 +
1461 + final output = '$dogecoinCommonHeaders\n' +
1462 + (hasImplementation ? '$dogecoinCWHeaders\n' : '\n') +
1463 + (hasImplementation ? '$dogecoinCwPart\n\n' : '\n') +
1464 + (hasImplementation
1465 + ? dogecoinCWDefinition
1466 + : dogecoinEmptyDefinition) +
1467 + '\n' +
1468 + dogecoinContent;
1469 +
1470 + if (outputFile.existsSync()) {
1471 + await outputFile.delete();
1472 + }
1473 +
1474 + await outputFile.writeAsString(output);
1475 +}
1476 +
1477 Future<void> generatePubspec({
1478 required bool hasMonero,
1479 required bool hasBitcoin,
@@ -1428,6 +1488,7 @@ Future<void> generatePubspec({
1488 required bool hasWownero,
1489 required bool hasZano,
1490 required bool hasDecred,
1491 + required bool hasDogecoin,
1492 }) async {
1493 const cwCore = """
1494 cw_core:
@@ -1492,6 +1553,10 @@ Future<void> generatePubspec({
1553 cw_decred:
1554 path: ./cw_decred
1555 """;
1556 + const cwDogecoin = """
1557 + cw_dogecoin:
1558 + path: ./cw_dogecoin
1559 + """;
1560 final inputFile = File(pubspecOutputPath);
1561 final inputText = await inputFile.readAsString();
1562 final inputLines = inputText.split('\n');
@@ -1557,6 +1622,10 @@ Future<void> generatePubspec({
1622 output += '\n$cwZano';
1623 }
1624
1625 + if (hasDogecoin) {
1626 + output += '\n$cwDogecoin';
1627 + }
1628 +
1629 final outputLines = output.split('\n');
1630 inputLines.insertAll(dependenciesIndex + 1, outputLines);
1631 final outputContent = inputLines.join('\n');
@@ -1582,6 +1651,7 @@ Future<void> generateWalletTypes({
1651 required bool hasWownero,
1652 required bool hasZano,
1653 required bool hasDecred,
1654 + required bool hasDogecoin,
1655 }) async {
1656 final walletTypesFile = File(walletTypesPath);
1657
@@ -1609,6 +1679,10 @@ Future<void> generateWalletTypes({
1679 outputContent += '\tWalletType.litecoin,\n';
1680 }
1681
1682 + if (hasDogecoin) {
1683 + outputContent += '\tWalletType.dogecoin,\n';
1684 + }
1685 +
1686 if (hasBitcoinCash) {
1687 outputContent += '\tWalletType.bitcoinCash,\n';
1688 }