finalizing wownero

This commit is contained in:
OmarHatem 2024-06-10 00:28:52 +02:00
parent 2ad76de4be
commit f48023b60c
15 changed files with 205 additions and 25 deletions

View file

@ -61,17 +61,17 @@ android {
}
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
// signingConfigs {
// release {
// keyAlias keystoreProperties['keyAlias']
// keyPassword keystoreProperties['keyPassword']
// storeFile file(keystoreProperties['storeFile'])
// storePassword keystoreProperties['storePassword']
// }
// }
buildTypes {
release {
signingConfig signingConfigs.release
// signingConfig signingConfigs.release
shrinkResources false
minifyEnabled false

View file

@ -91,6 +91,9 @@
<data android:scheme="tron" />
<data android:scheme="tron-wallet" />
<data android:scheme="tron_wallet" />
<data android:scheme="wownero" />
<data android:scheme="wownero-wallet" />
<data android:scheme="wownero_wallet" />
</intent-filter>
<!-- nano-gpt link scheme -->
<intent-filter android:autoVerify="true">

View file

@ -1,2 +1,8 @@
-
uri: electrum.cakewallet.com:50002
uri: electrum.cakewallet.com:50002
useSSL: true
-
uri: btc-electrum.cakewallet.com:50002
isDefault: true
-
uri: electrs.cakewallet.com:50001

View file

@ -214,7 +214,7 @@ Now you can run the codebase and successfully create a wallet for type walletX s
**Restore Wallet**
- Go to `lib/core/seed_validator.dart`
- In the `getWordList` method, add a case to handle `WalletType.walletx` which would return the word list to be used to validate the passed in seeds.
- Next, go to `lib/restore_view_model.dart`
- Next, go to `lib/wallet_restore_view_model.dart`
- Modify the `hasRestoreFromPrivateKey` to reflect if walletx supports restore from Key
- Add a switch case to handle the various restore modes that walletX supports
- Modify the `getCredential` method to handle the restore flows for `WalletType.walletx`

View file

@ -200,7 +200,7 @@
<string>solana-wallet</string>
</array>
</dict>
<dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
@ -220,6 +220,26 @@
<string>tron-wallet</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>wownero</string>
<key>CFBundleURLSchemes</key>
<array>
<string>wownero</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>wownero-wallet</string>
<key>CFBundleURLSchemes</key>
<array>
<string>wownero-wallet</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>

View file

@ -139,6 +139,7 @@ class AddressValidator extends TextValidator {
switch (type) {
case CryptoCurrency.xmr:
case CryptoCurrency.wow:
return null;
case CryptoCurrency.ada:
return null;
@ -266,6 +267,7 @@ class AddressValidator extends TextValidator {
static String? getAddressFromStringPattern(CryptoCurrency type) {
switch (type) {
case CryptoCurrency.xmr:
case CryptoCurrency.wow:
return '([^0-9a-zA-Z]|^)4[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)'
'|([^0-9a-zA-Z]|^)8[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)'
'|([^0-9a-zA-Z]|^)[0-9a-zA-Z]{106}([^0-9a-zA-Z]|\$)';

View file

@ -229,6 +229,10 @@ Future<void> defaultSettingsMigration(
case 34:
await _addElectRsNode(nodes, sharedPreferences);
break;
case 36:
await addWowneroNodeList(nodes: nodes);
await changeWowneroCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
break;
default:
break;
}
@ -488,9 +492,23 @@ Node? getTronDefaultNode({required Box<Node> nodes}) {
nodes.values.firstWhereOrNull((node) => node.type == WalletType.tron);
}
Node? getWowneroDefaultNode({required Box<Node> nodes}) {
return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == wowneroDefaultNodeUri) ??
nodes.values.firstWhereOrNull((node) => node.type == WalletType.wownero);
Node getWowneroDefaultNode({required Box<Node> nodes}) {
final timeZone = DateTime.now().timeZoneOffset.inHours;
var nodeUri = '';
if (timeZone >= 1) {
// Eurasia
nodeUri = 'node2.monerodevs.org.lol:34568';
} else if (timeZone <= -4) {
// America
nodeUri = 'node3.monerodevs.org:34568';
}
try {
return nodes.values.firstWhere((Node node) => node.uriRaw == nodeUri);
} catch (_) {
return nodes.values.first;
}
}
Future<void> insecureStorageMigration({
@ -1021,6 +1039,23 @@ Future<void> changeEthereumCurrentNodeToDefault(
await sharedPreferences.setInt(PreferencesKey.currentEthereumNodeIdKey, nodeId);
}
Future<void> addWowneroNodeList({required Box<Node> nodes}) async {
final nodeList = await loadDefaultWowneroNodes();
for (var node in nodeList) {
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
await nodes.add(node);
}
}
}
Future<void> changeWowneroCurrentNodeToDefault(
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
final node = getWowneroDefaultNode(nodes: nodes);
final nodeId = node?.key as int? ?? 0;
await sharedPreferences.setInt(PreferencesKey.currentWowneroNodeIdKey, nodeId);
}
Future<void> addNanoNodeList({required Box<Node> nodes}) async {
final nodeList = await loadDefaultNanoNodes();
for (var node in nodeList) {

View file

@ -183,6 +183,23 @@ Future<List<Node>> loadDefaultTronNodes() async {
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<void> resetToDefault(Box<Node> nodeSource) async {
final moneroNodes = await loadDefaultNodes();
final bitcoinElectrumServerList = await loadBitcoinElectrumServerList();

View file

@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:io' show Platform;
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/core/auth_service.dart';
@ -42,7 +41,6 @@ import 'package:hive/hive.dart';
import 'package:cw_core/root_dir.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cw_core/window_size.dart';
import 'package:monero/monero.dart' as monero_dart;
final navigatorKey = GlobalKey<NavigatorState>();
final rootKey = GlobalKey<RootState>();
@ -205,7 +203,7 @@ Future<void> initializeAppConfigs() async {
transactionDescriptions: transactionDescriptions,
secureStorage: secureStorage,
anonpayInvoiceInfo: anonpayInvoiceInfo,
initialMigrationVersion: 34,
initialMigrationVersion: 36,
);
}

View file

@ -125,6 +125,7 @@ abstract class BalanceViewModelBase with Store {
String get availableBalanceLabel {
switch (wallet.type) {
case WalletType.monero:
case WalletType.wownero:
case WalletType.haven:
case WalletType.ethereum:
case WalletType.polygon:
@ -142,6 +143,7 @@ abstract class BalanceViewModelBase with Store {
String get additionalBalanceLabel {
switch (wallet.type) {
case WalletType.monero:
case WalletType.wownero:
case WalletType.haven:
case WalletType.ethereum:
case WalletType.polygon:

View file

@ -75,6 +75,15 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
viewKey: restoreWallet.viewKey ?? '',
spendKey: restoreWallet.spendKey ?? '',
height: restoreWallet.height ?? 0);
case WalletType.wownero:
return wownero!.createWowneroRestoreWalletFromKeysCredentials(
name: name,
password: password,
language: 'English',
address: restoreWallet.address ?? '',
viewKey: restoreWallet.viewKey ?? '',
spendKey: restoreWallet.spendKey ?? '',
height: restoreWallet.height ?? 0);
case WalletType.bitcoin:
case WalletType.litecoin:
return bitcoin!.createBitcoinRestoreWalletFromWIFCredentials(

View file

@ -36,6 +36,9 @@ class WalletRestoreFromQRCode {
'tron': WalletType.tron,
'tron-wallet': WalletType.tron,
'tron_wallet': WalletType.tron,
'wownero': WalletType.wownero,
'wownero-wallet': WalletType.wownero,
'wownero_wallet': WalletType.wownero,
};
static bool _containsAssetSpecifier(String code) => _extractWalletType(code) != null;
@ -57,7 +60,9 @@ class WalletRestoreFromQRCode {
RegExp _getPattern(int wordCount) =>
RegExp(r'(?<=\W|^)((?:\w+\s+){' + (wordCount - 1).toString() + r'}\w+)(?=\W|$)');
List<int> patternCounts = walletType == WalletType.monero ? [25, 16, 14, 13] : [24, 18, 12];
List<int> patternCounts = walletType == WalletType.monero || walletType == WalletType.wownero
? [25, 16, 14, 13]
: [24, 18, 12];
for (final count in patternCounts) {
final pattern = _getPattern(count);

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_type.dart';
@ -76,7 +77,7 @@ abstract class TransactionDetailsViewModelBase with Store {
_addTronListItems(tx, dateFormat);
break;
case WalletType.wownero:
// _addWowneroListItems(tx, dateFormat);
_addWowneroListItems(tx, dateFormat);
break;
default:
break;
@ -169,7 +170,9 @@ abstract class TransactionDetailsViewModelBase with Store {
return 'https://solscan.io/tx/${txId}';
case WalletType.tron:
return 'https://tronscan.org/#/transaction/${txId}';
default:
case WalletType.wownero:
return 'https://explore.wownero.com/tx/${txId}';
case WalletType.none:
return '';
}
}
@ -197,7 +200,9 @@ abstract class TransactionDetailsViewModelBase with Store {
return S.current.view_transaction_on + 'solscan.io';
case WalletType.tron:
return S.current.view_transaction_on + 'tronscan.org';
default:
case WalletType.wownero:
return S.current.view_transaction_on + 'Wownero.com';
case WalletType.none:
return '';
}
}
@ -442,4 +447,44 @@ abstract class TransactionDetailsViewModelBase with Store {
String get pendingTransactionFeeFiatAmountFormatted => sendViewModel.isFiatDisabled
? ''
: sendViewModel.pendingTransactionFeeFiatAmount + ' ' + sendViewModel.fiat.title;
void _addWowneroListItems(TransactionInfo tx, DateFormat dateFormat) {
final key = tx.additionalInfo['key'] as String?;
final accountIndex = tx.additionalInfo['accountIndex'] as int;
final addressIndex = tx.additionalInfo['addressIndex'] as int;
final feeFormatted = tx.feeFormatted();
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'),
StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()),
if (feeFormatted != null)
StandartListItem(title: S.current.transaction_details_fee, value: feeFormatted),
if (key?.isNotEmpty ?? false) StandartListItem(title: S.current.transaction_key, value: key!),
];
if (tx.direction == TransactionDirection.incoming) {
try {
final address = wownero!.getTransactionAddress(wallet, accountIndex, addressIndex);
final label = wownero!.getSubaddressLabel(wallet, accountIndex, addressIndex);
if (address.isNotEmpty) {
isRecipientAddressShown = true;
_items.add(StandartListItem(
title: S.current.transaction_details_recipient_address,
value: address,
));
}
if (label.isNotEmpty) {
_items.add(StandartListItem(title: S.current.address_label, value: label));
}
} catch (e) {
print(e.toString());
}
}
items.addAll(_items);
}
}

View file

@ -17,6 +17,7 @@ import 'package:cake_wallet/utils/list_item.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/amount_converter.dart';
import 'package:cw_core/currency.dart';
import 'package:cw_core/wallet_type.dart';
@ -191,6 +192,22 @@ class TronURI extends PaymentURI {
}
}
class WowneroURI extends PaymentURI {
WowneroURI({required String amount, required String address})
: super(amount: amount, address: address);
@override
String toString() {
var base = 'wownero:' + address;
if (amount.isNotEmpty) {
base += '?tx_amount=${amount.replaceAll(',', '.')}';
}
return base;
}
}
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
WalletAddressListViewModelBase({
required AppStore appStore,
@ -293,6 +310,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
return TronURI(amount: amount, address: address.address);
}
if (wallet.type == WalletType.wownero) {
return WowneroURI(amount: amount, address: address.address);
}
throw Exception('Unexpected type: ${type.toString()}');
}
@ -409,6 +430,20 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
}
if (wallet.type == WalletType.wownero) {
final primaryAddress = wownero!.getSubaddressList(wallet).subaddresses.first;
final addressItems = wownero!.getSubaddressList(wallet).subaddresses.map((subaddress) {
final isPrimary = subaddress == primaryAddress;
return WalletAddressListItem(
id: subaddress.id,
isPrimary: isPrimary,
name: subaddress.label,
address: subaddress.address);
});
addressList.addAll(addressItems);
}
if (searchText.isNotEmpty) {
return ObservableList.of(addressList.where((item) {
if (item is WalletAddressListItem) {

View file

@ -29,8 +29,10 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
Box<WalletInfo> walletInfoSource,
{required WalletType type})
: hasSeedLanguageSelector = type == WalletType.monero || type == WalletType.haven,
hasBlockchainHeightLanguageSelector = type == WalletType.monero || type == WalletType.haven,
: hasSeedLanguageSelector =
type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero,
hasBlockchainHeightLanguageSelector =
type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero,
hasRestoreFromPrivateKey = type == WalletType.ethereum ||
type == WalletType.polygon ||
type == WalletType.nano ||
@ -112,6 +114,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials(
name: name, mnemonic: seed, password: password);
case WalletType.nano:
case WalletType.banano:
return nano!.createNanoRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
@ -143,7 +146,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
password: password,
height: height,
);
default:
case WalletType.none:
break;
}
}