Add decred (#1938)

* decred: Add decred. (#1322)

* multi: Add initial decred screens. (#1165)

Use a mock libwallet for now.

* cw_decred: add libdcrwallet dependency and link library for android, ios and macos (#1240)

* change cw_decred from package to plugin

* add libdcrwallet dependency and link library for android, ios and macos

* remove spvwallet, make some libdcrwallet fns async, light refactor

* libdcrwallet: use json payload returns

* use specific libwallet commit hash

* decred: fix Rename wallet.

---------

Co-authored-by: JoeGruff <joegruffins@gmail.com>

* decred: Add sync.

* decred: Add send transaction.

* decred: Fix fee estimation.

* decred: List transactions.

* decred: Add rescan.

* decred: Sign message.

* decred: Add new addr and addrs.

* decred: Add change wallet pass.

* decred: Add restore from seed.

* decred: Add watching only wallets.

* decred: Enable mainnet.

* decred: Allow using blank node address.

This allows a persistent peer to be unset, falling back to decred
seeders.

* decred: Rescan from wallet birthday.

* add and update macos build scripts, update build readme, gitignore macos project.pbxproj

Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com>

* multi: hide decred rescan page if it's not ready

-  move hasRescan method to WalletBase and implement for decred

Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com>

* cw_decred: fix bug where decred wallets are not loaded after app restart

Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com>

* add buy and sell for decred via onramp

Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com>

* bug-fix: account for other send outputs that are part of the same tx

Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com>

* decred: Return address with no peers.

* decred: Update pubspec.

* decred: Add verify message.

* upgrade hive_generator dep in cw_decred

* decred: Clean up code.

---------

Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com>
Co-authored-by: Wisdom Arerosuoghene <wisdom.arerosuoghene@gmail.com>
Co-authored-by: Philemon Ukane <ukanephilemon@gmail.com>

* fix extracted addresses not used
fix conflicts with main

* remove print [skip ci]

* minor formatting

* fix initial migration version

* add build decred script to workflow

* install go before build decred
fix switch cases

* trial 2 to fix decred build

* re-install go

* revert build script change

* refactor/clean nodes functions

* Fix address book issue
Fix send ALL (to be continued with the fees point)

* Fix transactions display issues
Add missing file

* Fix unconfirmed balance not displayed
Change Wallet order
Minor cleanup

* Fix workflow

* Fix workflow

* Fix workflow

* test

* hardcode path for now

* fix + cleanup decred build script to work on mac and linux

* Update decred build script

* Run actions on pull requests, extract commit message

* run after checkout

* add safe directory

* Get commit message from base.sha instead of last commit

* base -> head

* Do not merge main branch into pr

* [skip slack] [run tests] clone by sha

* Proper name for decred library in the build script

* Throw an error when ANDROID_HOME or ANDROID_NDK_VERSION is missing

* Fix conflicts with main

* minor code enhancement

* decred: Add used address history.  (#1941)

* decred: Update pubspec.

* decred testnet

* decred: Add used address history.

* decred: Remove default node list.

* populate transaction history before sync begins

* decred: Add some awaits.

* decred: Fix send all.

* decred: Add clang export to build script.

* decred: Update logo colors.

* cleanup cw_decred.dart

* make decred wallet addresses selectable in receive page

* decred: Always set default addr when used.

* decred: Add back default node list.

* decred: Allow creating addresses manually.

---------

Co-authored-by: Wisdom Arerosuoghene <wisdom.arerosuoghene@gmail.com>
Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* minor fixes and cleanup

* minor fix, feel free to test now

* - Fix transaction details
- Fix Nodes
- Add processing sync status

* Add decred info card

* push missing file

* Add missing text for decred info card

* minor: change docs link [skip ci]

* decred: Update derivation info. (#2013)

* decred: Update derivation info.

* decred: Allow unsynced unused addresses.

* decred: Update dcrwallet dep to 4.3.0.

* Merge main and fix conflicts

* Merge main and fix conflicts

* decred: Fix background sync panic. (#2080)

* decred: Run libwallet in isolate. (#2077)

* decred: Fix contact save inquiry. (#2083)

Also fix tx time and the fee shown on pending transactions.

* Disable send button in view only decred wallets

* - Fix frozen coins
- Add URI support
- Fix fees in tx details
- Handle empty coins send
- Handle wallets in address book

* Merge main

* remove print [skip ci]

* Fix restore from QR

* minor improvement for QR restore

* minor fixes [skip ci]

* decred: Get slip44 addrs before sync completes. (#2092)

* - Fix loading wallet more than one time
- Fix minor UI issue

---------

Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com>
Co-authored-by: JoeGruffins <34998433+JoeGruffins@users.noreply.github.com>
Co-authored-by: Wisdom Arerosuoghene <wisdom.arerosuoghene@gmail.com>
Co-authored-by: Philemon Ukane <ukanephilemon@gmail.com>
Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>
This commit is contained in:
Omar Hatem 2025-03-21 04:18:47 +02:00 committed by GitHub
parent 52a39e29d4
commit 0ba54fa602
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
175 changed files with 7145 additions and 1115 deletions

File diff suppressed because it is too large Load diff

View file

@ -59,6 +59,7 @@ class MainActions {
static MainActions sendAction = MainActions._(
name: (context) => S.of(context).send,
image: 'assets/images/upload.png',
isEnabled: (viewModel) => viewModel.canSend,
onTap: (BuildContext context, DashboardViewModel viewModel) async {
Navigator.pushNamed(context, Routes.send);
},

View file

@ -4,111 +4,62 @@ import "package:yaml/yaml.dart";
import 'package:cw_core/node.dart';
import 'package:cw_core/wallet_type.dart';
Future<List<Node>> loadDefaultNodes() async {
final nodesRaw = await rootBundle.loadString('assets/node_list.yml');
Future<List<Node>> loadDefaultNodes(WalletType type) async {
String path;
switch (type) {
case WalletType.monero:
path = 'assets/node_list.yml';
break;
case WalletType.bitcoin:
path = 'assets/bitcoin_electrum_server_list.yml';
break;
case WalletType.litecoin:
path = 'assets/litecoin_electrum_server_list.yml';
break;
case WalletType.haven:
path = 'assets/haven_node_list.yml';
break;
case WalletType.ethereum:
path = 'assets/ethereum_server_list.yml';
break;
case WalletType.nano:
path = 'assets/nano_node_list.yml';
break;
case WalletType.bitcoinCash:
path = 'assets/bitcoin_cash_electrum_server_list.yml';
break;
case WalletType.polygon:
path = 'assets/polygon_node_list.yml';
break;
case WalletType.solana:
path = 'assets/solana_node_list.yml';
break;
case WalletType.tron:
path = 'assets/tron_node_list.yml';
break;
case WalletType.wownero:
path = 'assets/wownero_node_list.yml';
break;
case WalletType.zano:
path = 'assets/zano_node_list.yml';
break;
case WalletType.decred:
path = 'assets/decred_node_list.yml';
break;
case WalletType.banano:
case WalletType.none:
path = '';
break;
}
final nodesRaw = await rootBundle.loadString(path);
final loadedNodes = loadYaml(nodesRaw) as YamlList;
final nodes = <Node>[];
for (final raw in loadedNodes) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.monero;
nodes.add(node);
}
}
return nodes;
}
Future<List<Node>> loadBitcoinElectrumServerList() async {
final serverListRaw = await rootBundle.loadString('assets/bitcoin_electrum_server_list.yml');
final loadedServerList = loadYaml(serverListRaw) as YamlList;
final serverList = <Node>[];
for (final raw in loadedServerList) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.bitcoin;
serverList.add(node);
}
}
return serverList;
}
Future<List<Node>> loadLitecoinElectrumServerList() async {
final serverListRaw = await rootBundle.loadString('assets/litecoin_electrum_server_list.yml');
final loadedServerList = loadYaml(serverListRaw) as YamlList;
final serverList = <Node>[];
for (final raw in loadedServerList) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.litecoin;
serverList.add(node);
}
}
return serverList;
}
Future<List<Node>> loadDefaultHavenNodes() async {
final nodesRaw = await rootBundle.loadString('assets/haven_node_list.yml');
final loadedNodes = loadYaml(nodesRaw) as YamlList;
final nodes = <Node>[];
for (final raw in loadedNodes) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.haven;
nodes.add(node);
}
}
return nodes;
}
Future<List<Node>> loadDefaultEthereumNodes() async {
final nodesRaw = await rootBundle.loadString('assets/ethereum_server_list.yml');
final loadedNodes = loadYaml(nodesRaw) as YamlList;
final nodes = <Node>[];
for (final raw in loadedNodes) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.ethereum;
nodes.add(node);
}
}
return nodes;
}
Future<List<Node>> loadBitcoinCashElectrumServerList() async {
final serverListRaw = await rootBundle.loadString('assets/bitcoin_cash_electrum_server_list.yml');
final loadedServerList = loadYaml(serverListRaw) as YamlList;
final serverList = <Node>[];
for (final raw in loadedServerList) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.bitcoinCash;
serverList.add(node);
}
}
return serverList;
}
Future<List<Node>> loadDefaultNanoNodes() async {
final nodesRaw = await rootBundle.loadString('assets/nano_node_list.yml');
final loadedNodes = loadYaml(nodesRaw) as YamlList;
final nodes = <Node>[];
for (final raw in loadedNodes) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.nano;
node.type = type;
nodes.add(node);
}
}
@ -132,103 +83,19 @@ Future<List<Node>> loadDefaultNanoPowNodes() async {
return nodes;
}
Future<List<Node>> loadDefaultPolygonNodes() async {
final nodesRaw = await rootBundle.loadString('assets/polygon_node_list.yml');
final loadedNodes = loadYaml(nodesRaw) as YamlList;
final nodes = <Node>[];
for (final raw in loadedNodes) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.polygon;
nodes.add(node);
}
}
return nodes;
}
Future<List<Node>> loadDefaultSolanaNodes() async {
final nodesRaw = await rootBundle.loadString('assets/solana_node_list.yml');
final loadedNodes = loadYaml(nodesRaw) as YamlList;
final nodes = <Node>[];
for (final raw in loadedNodes) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.solana;
nodes.add(node);
}
}
return nodes;
}
Future<List<Node>> loadDefaultTronNodes() async {
final nodesRaw = await rootBundle.loadString('assets/tron_node_list.yml');
final loadedNodes = loadYaml(nodesRaw) as YamlList;
final nodes = <Node>[];
for (final raw in loadedNodes) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.tron;
nodes.add(node);
}
}
return nodes;
}
Future<List<Node>> loadDefaultWowneroNodes() async {
final nodesRaw = await rootBundle.loadString('assets/wownero_node_list.yml');
final loadedNodes = loadYaml(nodesRaw) as YamlList;
final nodes = <Node>[];
for (final raw in loadedNodes) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.wownero;
nodes.add(node);
}
}
return nodes;
}
Future<List<Node>> loadDefaultZanoNodes() async {
final nodesRaw = await rootBundle.loadString('assets/zano_node_list.yml');
final loadedNodes = loadYaml(nodesRaw) as YamlList;
final nodes = <Node>[];
for (final raw in loadedNodes) {
if (raw is Map) {
final node = Node.fromMap(Map<String, Object>.from(raw));
node.type = WalletType.zano;
nodes.add(node);
}
}
return nodes;
}
Future<void> resetToDefault(Box<Node> nodeSource) async {
final moneroNodes = await loadDefaultNodes();
final bitcoinElectrumServerList = await loadBitcoinElectrumServerList();
final litecoinElectrumServerList = await loadLitecoinElectrumServerList();
final bitcoinCashElectrumServerList = await loadBitcoinCashElectrumServerList();
final havenNodes = await loadDefaultHavenNodes();
final ethereumNodes = await loadDefaultEthereumNodes();
final nanoNodes = await loadDefaultNanoNodes();
final polygonNodes = await loadDefaultPolygonNodes();
final solanaNodes = await loadDefaultSolanaNodes();
final tronNodes = await loadDefaultTronNodes();
final zanoNodes = await loadDefaultZanoNodes();
final moneroNodes = await loadDefaultNodes(WalletType.monero);
final bitcoinElectrumServerList = await loadDefaultNodes(WalletType.bitcoin);
final litecoinElectrumServerList = await loadDefaultNodes(WalletType.litecoin);
final bitcoinCashElectrumServerList = await loadDefaultNodes(WalletType.bitcoinCash);
final havenNodes = await loadDefaultNodes(WalletType.haven);
final ethereumNodes = await loadDefaultNodes(WalletType.ethereum);
final nanoNodes = await loadDefaultNodes(WalletType.nano);
final polygonNodes = await loadDefaultNodes(WalletType.polygon);
final solanaNodes = await loadDefaultNodes(WalletType.solana);
final tronNodes = await loadDefaultNodes(WalletType.tron);
final decredNodes = await loadDefaultNodes(WalletType.decred);
final zanoNodes = await loadDefaultNodes(WalletType.zano);
final nodes = moneroNodes +
bitcoinElectrumServerList +
@ -238,7 +105,10 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
bitcoinCashElectrumServerList +
nanoNodes +
polygonNodes +
solanaNodes + tronNodes + zanoNodes;
solanaNodes +
tronNodes +
zanoNodes +
decredNodes;
await nodeSource.clear();
await nodeSource.addAll(nodes);

View file

@ -10,6 +10,7 @@ class PreferencesKey {
static const currentPolygonNodeIdKey = 'current_node_id_matic';
static const currentNanoNodeIdKey = 'current_node_id_nano';
static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
static const currentDecredNodeIdKey = 'current_node_id_decred';
static const currentBananoNodeIdKey = 'current_node_id_banano';
static const currentBananoPowNodeIdKey = 'current_node_id_banano_pow';
static const currentFiatCurrencyKey = 'current_fiat_currency';
@ -48,6 +49,7 @@ class PreferencesKey {
static const bitcoinCashTransactionPriority = 'current_fee_priority_bitcoin_cash';
static const zanoTransactionPriority = 'current_fee_priority_zano';
static const wowneroTransactionPriority = 'current_fee_priority_wownero';
static const decredTransactionPriority = 'current_fee_priority_decred';
static const customBitcoinFeeRate = 'custom_electrum_fee_rate';
static const silentPaymentsCardDisplay = 'silentPaymentsCardDisplay';
static const silentPaymentsAlwaysScan = 'silentPaymentsAlwaysScan';
@ -81,6 +83,7 @@ class PreferencesKey {
static const lookupsENS = 'looks_up_ens';
static const lookupsWellKnown = 'looks_up_well_known';
static const showCameraConsent = 'show_camera_consent';
static const showDecredInfoCard = 'show_decred_info_card';
static String moneroWalletUpdateV1Key(String name) =>
'${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}';

View file

@ -6,6 +6,7 @@ import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cake_wallet/zano/zano.dart';
import 'package:cake_wallet/decred/decred.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_type.dart';
@ -35,6 +36,8 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
return [];
case WalletType.zano:
return zano!.getTransactionPriorities();
case WalletType.decred:
return decred!.getTransactionPriorities();
default:
return [];
}

View file

@ -82,6 +82,7 @@ class ProvidersHelper {
ProviderType.moonpay,
ProviderType.kriptonim
];
case WalletType.decred:
case WalletType.none:
case WalletType.haven:
case WalletType.zano:
@ -113,6 +114,7 @@ class ProvidersHelper {
];
case WalletType.monero:
return [ProviderType.dfx];
case WalletType.decred:
case WalletType.nano:
case WalletType.banano:
case WalletType.none: