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>
This commit is contained in:
Serhii
2025-08-07 22:07:20 +03:00
committed by GitHub
co-authored by Omar Hatem
parent 9053a3d726
commit b6244cdd44
70 changed files with 1088 additions and 38 deletions
+12
View File
@@ -184,6 +184,18 @@ class CWBitcoin extends Bitcoin {
return estimatedTx.amount;
}
if (wallet.type == WalletType.dogecoin) {
final dogeAddr =
sk.getPublic().toP2pkhAddress();
final estimatedTx = await electrumWallet.estimateSendAllTx(
[BitcoinOutput(address: dogeAddr, value: BigInt.zero)],
getFeeRate(wallet, priority as BitcoinTransactionPriority),
coinTypeToSpendFrom: coinTypeToSpendFrom,
);
return estimatedTx.amount;
}
final p2shAddr = sk.getPublic().toP2pkhInP2sh();
final estimatedTx = await electrumWallet.estimateSendAllTx(
[BitcoinOutput(address: p2shAddr, value: BigInt.zero)],
+2
View File
@@ -29,6 +29,8 @@ class SeedValidator extends Validator<MnemonicItem> {
return getBitcoinWordList(language);
case WalletType.litecoin:
return getBitcoinWordList(language);
case WalletType.dogecoin:
return getBitcoinWordList(language);
case WalletType.monero:
return monero!.getMoneroWordList(language);
case WalletType.ethereum:
+1
View File
@@ -82,6 +82,7 @@ class WalletCreationService {
case WalletType.polygon:
case WalletType.solana:
case WalletType.tron:
case WalletType.dogecoin:
return true;
case WalletType.monero:
case WalletType.wownero:
+4
View File
@@ -274,6 +274,7 @@ import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'buy/kryptonim/kryptonim.dart';
import 'buy/meld/meld_buy_provider.dart';
import 'dogecoin/dogecoin.dart';
import 'src/screens/buy/buy_sell_page.dart';
import 'package:cake_wallet/view_model/dev/background_sync_logs_view_model.dart';
import 'package:cake_wallet/src/screens/dev/background_sync_logs_page.dart';
@@ -1147,6 +1148,9 @@ Future<void> setup({
case WalletType.bitcoinCash:
return bitcoinCash!.createBitcoinCashWalletService(_walletInfoSource,
_unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
case WalletType.dogecoin:
return dogecoin!.createDogeCoinWalletService(_walletInfoSource,
_unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
case WalletType.nano:
case WalletType.banano:
return nano!.createNanoWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
+48
View File
@@ -0,0 +1,48 @@
part of 'dogecoin.dart';
class CWDogeCoin extends DogeCoin {
@override
WalletService createDogeCoinWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return DogeCoinWalletService(walletInfoSource, unspentCoinSource, isDirect);
}
@override
WalletCredentials createDogeCoinNewWalletCredentials({
required String name,
WalletInfo? walletInfo,
String? password,
String? passphrase,
String? mnemonic,
}) =>
DogeCoinNewWalletCredentials(
name: name,
walletInfo: walletInfo,
password: password,
passphrase: passphrase,
mnemonic: mnemonic,
);
@override
WalletCredentials createDogeCoinRestoreWalletFromSeedCredentials({
required String name,
required String mnemonic,
required String password,
String? passphrase,
}) =>
DogeCoinRestoreWalletFromSeedCredentials(
name: name, mnemonic: mnemonic, password: password, passphrase: passphrase);
@override
TransactionPriority deserializeDogeCoinTransactionPriority(int raw) =>
DogecoinTransactionPriority.deserialize(raw: raw);
@override
TransactionPriority getDefaultTransactionPriority() => DogecoinTransactionPriority.medium;
@override
List<TransactionPriority> getTransactionPriorities() => DogecoinTransactionPriority.all;
@override
TransactionPriority getDogeCoinTransactionPrioritySlow() => DogecoinTransactionPriority.slow;
}
+32
View File
@@ -0,0 +1,32 @@
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:hive/hive.dart';
import 'package:cw_dogecoin/cw_dogecoin.dart';
part 'cw_dogecoin.dart';
DogeCoin? dogecoin = CWDogeCoin();
abstract class DogeCoin {
WalletService createDogeCoinWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect);
WalletCredentials createDogeCoinNewWalletCredentials(
{required String name, WalletInfo? walletInfo, String? password, String? passphrase, String? mnemonic});
WalletCredentials createDogeCoinRestoreWalletFromSeedCredentials(
{required String name, required String mnemonic, required String password, String? passphrase});
TransactionPriority deserializeDogeCoinTransactionPriority(int raw);
TransactionPriority getDefaultTransactionPriority();
List<TransactionPriority> getTransactionPriorities();
TransactionPriority getDogeCoinTransactionPrioritySlow();
}
@@ -46,6 +46,7 @@ const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568';
const zanoDefaultNodeUri = 'zano.cakewallet.com:11211';
const moneroWorldNodeUri = '.moneroworld.com';
const decredDefaultUri = "default-spv-nodes";
const dogecoinDefaultNodeUri = 'dogecoin.stackwallet.com:50022';
Future<void> defaultSettingsMigration(
{required int version,
@@ -514,6 +515,15 @@ Future<void> defaultSettingsMigration(
case 50:
migrateExistingNodesToUseAutoSwitching(nodes: nodes, powNodes: powNodes);
break;
case 51:
await addWalletNodeList(nodes: nodes, type: WalletType.dogecoin);
await _changeDefaultNode(
nodes: nodes,
sharedPreferences: sharedPreferences,
type: WalletType.dogecoin,
currentNodePreferenceKey: PreferencesKey.currentDogecoinNodeIdKey,
);
break;
default:
break;
}
@@ -620,6 +630,8 @@ String _getDefaultNodeUri(WalletType type) {
return zanoDefaultNodeUri;
case WalletType.decred:
return decredDefaultUri;
case WalletType.dogecoin:
return dogecoinDefaultNodeUri;
case WalletType.banano:
case WalletType.none:
return '';
@@ -1050,6 +1062,8 @@ Future<void> checkCurrentNodes(
final currentDecredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
final currentBitcoinCashNodeId =
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
final currentDogecoinNodeId =
sharedPreferences.getInt(PreferencesKey.currentDogecoinNodeIdKey);
final currentSolanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
final currentTronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey);
final currentWowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
@@ -1074,6 +1088,8 @@ Future<void> checkCurrentNodes(
powNodeSource.values.firstWhereOrNull((node) => node.key == currentNanoPowNodeId);
final currentBitcoinCashNodeServer =
nodeSource.values.firstWhereOrNull((node) => node.key == currentBitcoinCashNodeId);
final currentDogecoinNodeServer =
nodeSource.values.firstWhereOrNull((node) => node.key == currentDogecoinNodeId);
final currentSolanaNodeServer =
nodeSource.values.firstWhereOrNull((node) => node.key == currentSolanaNodeId);
final currentTronNodeServer =
@@ -1143,6 +1159,12 @@ Future<void> checkCurrentNodes(
await sharedPreferences.setInt(PreferencesKey.currentBitcoinCashNodeIdKey, node.key as int);
}
if (currentDogecoinNodeServer == null) {
final node = Node(uri: dogecoinDefaultNodeUri, type: WalletType.dogecoin, useSSL: true);
await nodeSource.add(node);
await sharedPreferences.setInt(PreferencesKey.currentDogecoinNodeIdKey, node.key as int);
}
if (currentPolygonNodeServer == null) {
final node = Node(uri: polygonDefaultNodeUri, type: WalletType.polygon);
await nodeSource.add(node);
+6 -1
View File
@@ -46,6 +46,9 @@ Future<List<Node>> loadDefaultNodes(WalletType type) async {
case WalletType.decred:
path = 'assets/decred_node_list.yml';
break;
case WalletType.dogecoin:
path = 'assets/dogecoin_electrum_server_list.yml';
break;
case WalletType.banano:
case WalletType.none:
path = '';
@@ -96,6 +99,7 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
final tronNodes = await loadDefaultNodes(WalletType.tron);
final decredNodes = await loadDefaultNodes(WalletType.decred);
final zanoNodes = await loadDefaultNodes(WalletType.zano);
final dogecoinElectrumServerList = await loadDefaultNodes(WalletType.dogecoin);
final nodes = moneroNodes +
bitcoinElectrumServerList +
@@ -108,7 +112,8 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
solanaNodes +
tronNodes +
zanoNodes +
decredNodes;
decredNodes +
dogecoinElectrumServerList;
await nodeSource.clear();
await nodeSource.addAll(nodes);
+1
View File
@@ -11,6 +11,7 @@ class PreferencesKey {
static const currentNanoNodeIdKey = 'current_node_id_nano';
static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
static const currentDecredNodeIdKey = 'current_node_id_decred';
static const currentDogecoinNodeIdKey = 'current_node_id_doge';
static const currentBananoNodeIdKey = 'current_node_id_banano';
static const currentBananoPowNodeIdKey = 'current_node_id_banano_pow';
static const currentFiatCurrencyKey = 'current_fiat_currency';
@@ -1,5 +1,6 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/dogecoin/dogecoin.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/polygon/polygon.dart';
@@ -23,6 +24,8 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
return ethereum!.getTransactionPriorities();
case WalletType.bitcoinCash:
return bitcoinCash!.getTransactionPriorities();
case WalletType.dogecoin:
return dogecoin!.getTransactionPriorities();
case WalletType.polygon:
return polygon!.getTransactionPriorities();
// no such thing for nano/banano/solana/tron:
+1 -1
View File
@@ -250,7 +250,7 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
payjoinSessionSource: payjoinSessionSource,
anonpayInvoiceInfo: anonpayInvoiceInfo,
havenSeedStore: havenSeedStore,
initialMigrationVersion: 50,
initialMigrationVersion: 51,
);
}
@@ -76,6 +76,7 @@ void startCurrentWalletChangeReaction(
wallet.type == WalletType.bitcoin ||
wallet.type == WalletType.litecoin ||
wallet.type == WalletType.bitcoinCash ||
wallet.type == WalletType.dogecoin ||
wallet.type == WalletType.decred) {
_setAutoGenerateSubaddressStatus(wallet, settingsStore);
}
+1
View File
@@ -12,6 +12,7 @@ bool isBIP39Wallet(WalletType walletType) {
case WalletType.nano:
case WalletType.banano:
case WalletType.monero:
case WalletType.dogecoin:
return true;
case WalletType.wownero:
case WalletType.haven:
@@ -37,7 +37,8 @@ class MenuWidgetState extends State<MenuWidget> {
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.decredIcon = Image.asset('assets/images/decred_menu.png');
this.decredIcon = Image.asset('assets/images/decred_menu.png'),
this.dogecoinIcon = Image.asset('assets/images/doge_icon.png');
final largeScreen = 731;
@@ -64,6 +65,7 @@ class MenuWidgetState extends State<MenuWidget> {
Image wowneroIcon;
Image zanoIcon;
Image decredIcon;
Image dogecoinIcon;
@override
void initState() {
@@ -255,6 +257,8 @@ class MenuWidgetState extends State<MenuWidget> {
return zanoIcon;
case WalletType.decred:
return decredIcon;
case WalletType.dogecoin:
return dogecoinIcon;
default:
throw Exception('No icon for ${type.toString()}');
}
+17
View File
@@ -1070,6 +1070,7 @@ abstract class SettingsStoreBase with Store {
final wowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
final zanoNodeId = sharedPreferences.getInt(PreferencesKey.currentZanoNodeIdKey);
final decredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
final dogecoinNodeId = sharedPreferences.getInt(PreferencesKey.currentDogecoinNodeIdKey);
/// get the selected node, if null, then use the default
final moneroNode = nodeSource.get(nodeId) ??
@@ -1098,6 +1099,8 @@ abstract class SettingsStoreBase with Store {
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == wowneroDefaultNodeUri);
final zanoNode = nodeSource.get(zanoNodeId) ??
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == zanoDefaultNodeUri);
final dogecoinNode = nodeSource.get(dogecoinNodeId) ??
nodeSource.values.firstWhereOrNull((e) => e.uriRaw == dogecoinDefaultNodeUri);
final packageInfo = await PackageInfo.fromPlatform();
final deviceName = await _getDeviceName() ?? '';
@@ -1185,6 +1188,10 @@ abstract class SettingsStoreBase with Store {
nodes[WalletType.decred] = decredNode;
}
if (dogecoinNode != null) {
nodes[WalletType.dogecoin] = dogecoinNode;
}
final savedSyncMode = SyncMode.all.firstWhere((element) {
return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 2); // default to 2 - daily sync
});
@@ -1549,6 +1556,7 @@ abstract class SettingsStoreBase with Store {
final wowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
final zanoNodeId = sharedPreferences.getInt(PreferencesKey.currentZanoNodeIdKey);
final decredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
final dogecoinNodeId = sharedPreferences.getInt(PreferencesKey.currentDogecoinNodeIdKey);
final moneroNode = nodeSource.get(nodeId);
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
@@ -1562,6 +1570,7 @@ abstract class SettingsStoreBase with Store {
final wowneroNode = nodeSource.get(wowneroNodeId);
final zanoNode = nodeSource.get(zanoNodeId);
final decredNode = nodeSource.get(decredNodeId);
final dogecoinNode = nodeSource.get(dogecoinNodeId);
if (moneroNode != null) {
nodes[WalletType.monero] = moneroNode;
@@ -1616,6 +1625,10 @@ abstract class SettingsStoreBase with Store {
nodes[WalletType.decred] = decredNode;
}
if (dogecoinNode != null) {
nodes[WalletType.dogecoin] = dogecoinNode;
}
// MIGRATED:
useTOTP2FA = await SecureKey.getBool(
@@ -1757,6 +1770,10 @@ abstract class SettingsStoreBase with Store {
break;
case WalletType.zano:
await _sharedPreferences.setInt(PreferencesKey.currentZanoNodeIdKey, node.key as int);
break;
case WalletType.dogecoin:
await _sharedPreferences.setInt(PreferencesKey.currentDogecoinNodeIdKey, node.key as int);
break;
default:
break;
}
@@ -37,6 +37,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
switch (type) {
case WalletType.ethereum:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
case WalletType.polygon:
case WalletType.solana:
case WalletType.tron:
@@ -83,6 +84,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
WalletType.monero,
WalletType.wownero,
WalletType.zano,
WalletType.dogecoin,
].contains(type);
@computed
@@ -865,6 +865,7 @@ abstract class DashboardViewModelBase with Store {
case WalletType.tron:
case WalletType.wownero:
case WalletType.decred:
case WalletType.dogecoin:
return true;
case WalletType.zano:
case WalletType.haven:
@@ -242,6 +242,7 @@ abstract class HomeSettingsViewModelBase with Store {
case WalletType.wownero:
case WalletType.bitcoinCash:
case WalletType.decred:
case WalletType.dogecoin:
return false;
}
@@ -24,6 +24,7 @@ abstract class SignViewModelBase with Store {
WalletType.bitcoin,
WalletType.bitcoinCash,
WalletType.litecoin,
WalletType.dogecoin,
WalletType.haven,
].contains(wallet.type);
@@ -167,6 +167,7 @@ class TransactionListItem extends ActionListItem with Keyable {
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
amount = calculateFiatAmountRaw(
cryptoAmount: bitcoin!.formatterBitcoinAmountToDouble(amount: transaction.amount),
price: price);
@@ -166,7 +166,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
}
bool get isElectrumWallet =>
[WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type);
[WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash,WalletType.dogecoin ].contains(wallet.type);
bool get hideAddressAfterExchange =>
[WalletType.monero, WalletType.wownero].contains(wallet.type);
@@ -314,6 +314,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
WalletType.bitcoin,
WalletType.litecoin,
WalletType.bitcoinCash,
WalletType.dogecoin,
].contains(wallet.type) &&
depositCurrency == wallet.currency;
@@ -673,6 +674,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
WalletType.litecoin,
WalletType.bitcoin,
WalletType.bitcoinCash,
WalletType.dogecoin,
].contains(wallet.type)) {
final priority = _settingsStore.priority[wallet.type]!;
@@ -751,6 +753,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
depositCurrency = CryptoCurrency.bch;
receiveCurrency = CryptoCurrency.xmr;
break;
case WalletType.dogecoin:
depositCurrency = CryptoCurrency.doge;
receiveCurrency = CryptoCurrency.xmr;
break;
case WalletType.haven:
depositCurrency = CryptoCurrency.xhv;
receiveCurrency = CryptoCurrency.btc;
@@ -93,6 +93,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.bitcoin:
case WalletType.dogecoin:
case WalletType.zano:
case WalletType.decred:
return false;
@@ -47,6 +47,9 @@ class WalletRestoreFromQRCode {
'decred': WalletType.decred,
'decred-wallet': WalletType.decred,
'decred_wallet': WalletType.decred,
'dogecoin': WalletType.dogecoin,
'dogecoin-wallet': WalletType.dogecoin,
'dogecoin_wallet': WalletType.dogecoin
};
static WalletType? _extractWalletType(String code) {
+8 -1
View File
@@ -1,5 +1,6 @@
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/decred/decred.dart';
import 'package:cake_wallet/dogecoin/dogecoin.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';
@@ -91,6 +92,8 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
return transactionPriority == polygon!.getPolygonTransactionPrioritySlow();
case WalletType.decred:
return transactionPriority == decred!.getDecredTransactionPrioritySlow();
case WalletType.dogecoin:
return transactionPriority == dogecoin!.getDogeCoinTransactionPrioritySlow();
case WalletType.none:
case WalletType.nano:
case WalletType.banano:
@@ -119,7 +122,8 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
bool get isElectrumWallet =>
wallet.type == WalletType.bitcoin ||
wallet.type == WalletType.litecoin ||
wallet.type == WalletType.bitcoinCash;
wallet.type == WalletType.bitcoinCash ||
wallet.type == WalletType.dogecoin;
String? get walletCurrencyName => wallet.currency.fullName?.toLowerCase() ?? wallet.currency.name;
@@ -188,6 +192,9 @@ abstract class FeesViewModelBase extends WalletChangeListenerViewModel with Stor
case WalletType.bitcoinCash:
_settingsStore.priority[wallet.type] = bitcoinCash!.getDefaultTransactionPriority();
break;
case WalletType.dogecoin:
_settingsStore.priority[wallet.type] = dogecoin!.getDefaultTransactionPriority();
break;
case WalletType.polygon:
_settingsStore.priority[wallet.type] = polygon!.getDefaultTransactionPriority();
break;
+4 -1
View File
@@ -99,6 +99,7 @@ abstract class OutputBase with Store {
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
_amount = bitcoin!.formatterStringDoubleToBitcoinAmount(_cryptoAmount);
break;
case WalletType.decred:
@@ -166,7 +167,8 @@ abstract class OutputBase with Store {
return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
}
if (_wallet.type == WalletType.litecoin || _wallet.type == WalletType.bitcoinCash) {
if (_wallet.type == WalletType.litecoin || _wallet.type == WalletType.bitcoinCash ||
_wallet.type == WalletType.dogecoin) {
return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
}
@@ -316,6 +318,7 @@ abstract class OutputBase with Store {
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
maximumFractionDigits = 8;
break;
case WalletType.wownero:
+5 -2
View File
@@ -269,6 +269,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
case WalletType.monero:
case WalletType.wownero:
case WalletType.decred:
@@ -317,12 +318,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
WalletType.monero,
WalletType.wownero,
WalletType.decred,
WalletType.bitcoinCash
WalletType.bitcoinCash,
WalletType.dogecoin
].contains(wallet.type);
@computed
bool get isElectrumWallet =>
[WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type);
[WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash, WalletType.dogecoin].contains(wallet.type);
@observable
CryptoCurrency selectedCryptoCurrency;
@@ -652,6 +654,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
switch (wallet.type) {
case WalletType.bitcoin:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
return bitcoin!.createBitcoinTransactionCredentials(
outputs,
priority: priority!,
@@ -70,7 +70,8 @@ abstract class OtherSettingsViewModelBase with Store {
if (_wallet.type == WalletType.bitcoin ||
_wallet.type == WalletType.litecoin ||
_wallet.type == WalletType.bitcoinCash) {
_wallet.type == WalletType.bitcoinCash ||
_wallet.type == WalletType.dogecoin) {
final rate = bitcoin!.getFeeRate(_wallet, _priority);
return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate);
}
@@ -83,7 +84,8 @@ abstract class OtherSettingsViewModelBase with Store {
if (_wallet.type == WalletType.bitcoin ||
_wallet.type == WalletType.litecoin ||
_wallet.type == WalletType.bitcoinCash) {
_wallet.type == WalletType.bitcoinCash ||
_wallet.type == WalletType.dogecoin) {
final rate = bitcoin!.getFeeRate(_wallet, _priority);
return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate,
customRate: customValue);
@@ -43,6 +43,7 @@ abstract class PrivacySettingsViewModelBase with Store {
WalletType.bitcoin,
WalletType.litecoin,
WalletType.bitcoinCash,
WalletType.dogecoin,
WalletType.decred
].contains(_wallet.type);
@@ -88,6 +88,9 @@ abstract class TransactionDetailsViewModelBase with Store {
case WalletType.decred:
_addDecredListItems(tx, dateFormat);
break;
case WalletType.dogecoin:
_addDogecoinListItems(tx, dateFormat);
break;
case WalletType.none:
case WalletType.banano:
break;
@@ -193,6 +196,8 @@ abstract class TransactionDetailsViewModelBase with Store {
return 'https://explorer.zano.org/transaction/${txId}';
case WalletType.decred:
return 'https://${wallet.isTestnet ? "testnet" : "dcrdata"}.decred.org/tx/${txId.split(':')[0]}';
case WalletType.dogecoin:
return 'https://blockchair.com/dogecoin/transaction/${txId}';
case WalletType.none:
return '';
}
@@ -206,6 +211,7 @@ abstract class TransactionDetailsViewModelBase with Store {
return S.current.view_transaction_on + 'mempool.space';
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
return S.current.view_transaction_on + 'Blockchair.com';
case WalletType.haven:
return S.current.view_transaction_on + 'explorer.havenprotocol.org';
@@ -750,6 +756,51 @@ abstract class TransactionDetailsViewModelBase with Store {
items.addAll(_items);
}
void _addDogecoinListItems(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 &&
@@ -49,7 +49,7 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
})
];
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(_type)) {
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash, WalletType.dogecoin].contains(_type)) {
items.add(BlockExplorerListItem(
title: S.current.view_in_block_explorer,
value: _explorerDescription(_type),
@@ -71,6 +71,8 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
return 'https://litecoin.earlyordies.com/tx/${txId}';
case WalletType.bitcoinCash:
return 'https://blockchair.com/bitcoin-cash/transaction/${txId}';
case WalletType.dogecoin:
return 'https://dogechain.info/tx/${txId}';
default:
return '';
}
@@ -84,6 +86,8 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
return S.current.view_transaction_on + 'Earlyordies.com';
case WalletType.bitcoinCash:
return S.current.view_transaction_on + 'Blockchair.com';
case WalletType.dogecoin:
return S.current.view_transaction_on + 'Dogechain.info';
default:
return '';
}
@@ -91,7 +91,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
return monero!.formatterMoneroAmountToString(amount: fullBalance);
if (wallet.type == WalletType.wownero)
return wownero!.formatterWowneroAmountToString(amount: fullBalance);
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type))
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash, WalletType.dogecoin].contains(wallet.type))
return bitcoin!.formatterBitcoinAmountToString(amount: fullBalance);
if (wallet.type == WalletType.decred)
return decred!.formatterDecredAmountToString(amount: fullBalance);
@@ -105,7 +105,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
if (wallet.type == WalletType.wownero) {
await wownero!.updateUnspents(wallet);
}
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type)) {
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash, WalletType.dogecoin].contains(wallet.type)) {
await bitcoin!.updateUnspents(wallet);
}
if (wallet.type == WalletType.decred) {
@@ -123,6 +123,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
return bitcoin!.getUnspents(wallet, coinTypeToSpendFrom: coinTypeToSpendFrom);
case WalletType.decred:
return decred!.getUnspents(wallet);
@@ -140,6 +141,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
return bitcoin!.getUnspents(wallet, coinTypeToSpendFrom: overrideCoinTypeToSpendFrom);
case WalletType.decred:
return decred!.getUnspents(wallet);
@@ -48,7 +48,8 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
bool get isElectrum =>
_wallet.type == WalletType.bitcoin ||
_wallet.type == WalletType.bitcoinCash ||
_wallet.type == WalletType.litecoin;
_wallet.type == WalletType.litecoin ||
_wallet.type == WalletType.dogecoin;
Future<void> save() async {
try {
@@ -242,6 +242,22 @@ class DecredURI extends PaymentURI {
}
}
class DogeURI extends PaymentURI {
DogeURI({required String amount, required String address})
: super(amount: amount, address: address);
@override
String toString() {
var base = 'doge:' + address;
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
}
return base;
}
}
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
WalletAddressListViewModelBase({
required AppStore appStore,
@@ -350,6 +366,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
return ZanoURI(amount: amount, address: address.address);
case WalletType.decred:
return DecredURI(amount: amount, address: address.address);
case WalletType.dogecoin:
return DogeURI(amount: amount, address: address.address);
case WalletType.none:
throw Exception('Unexpected type: ${type.toString()}');
}
@@ -586,12 +604,13 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
WalletType.bitcoinCash,
WalletType.bitcoin,
WalletType.litecoin,
WalletType.decred
WalletType.decred,
WalletType.dogecoin,
].contains(wallet.type);
@computed
bool get isElectrumWallet =>
[WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type);
[WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash, WalletType.dogecoin].contains(wallet.type);
@computed
bool get isBalanceAvailable => isElectrumWallet;
@@ -164,6 +164,7 @@ abstract class WalletKeysViewModelBase with Store {
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
if (_wallet.type == WalletType.bitcoin) {
keys = bitcoin!.getSilentPaymentKeys(_appStore.wallet!);
}
@@ -261,6 +262,8 @@ abstract class WalletKeysViewModelBase with Store {
return 'zano-wallet';
case WalletType.decred:
return 'decred-wallet';
case WalletType.dogecoin:
return 'dogecoin-wallet';
default:
throw Exception('Unexpected wallet type: ${_wallet.type.toString()}');
}
+8
View File
@@ -1,4 +1,5 @@
import 'package:cake_wallet/core/new_wallet_arguments.dart';
import 'package:cake_wallet/dogecoin/dogecoin.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/zano/zano.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
@@ -96,6 +97,13 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
passphrase: passphrase,
mnemonic: newWalletArguments!.mnemonic,
);
case WalletType.dogecoin:
return dogecoin!.createDogeCoinNewWalletCredentials(
name: name,
password: walletPassword,
passphrase: passphrase,
mnemonic: newWalletArguments!.mnemonic,
);
case WalletType.nano:
case WalletType.banano:
return nano!.createNanoNewWalletCredentials(
@@ -3,6 +3,7 @@ import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/core/generate_wallet_password.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/dogecoin/dogecoin.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/nano/nano.dart';
@@ -58,6 +59,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
case WalletType.bitcoinCash:
case WalletType.zano:
case WalletType.none:
case WalletType.dogecoin:
availableModes = [WalletRestoreMode.seed];
break;
}
@@ -148,6 +150,13 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
password: password,
passphrase: passphrase,
);
case WalletType.dogecoin:
return dogecoin!.createDogeCoinRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
passphrase: passphrase,
);
case WalletType.nano:
case WalletType.banano:
return nano!.createNanoRestoreWalletFromSeedCredentials(