mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-30 06:48:49 +01:00
Merge upstream Cake Wallet v6.5.0
Catches up from the May 12 fork point (v6.1.2+23) through v6.5.0: 535 upstream commits, including the Monero silent-open fix, Monero on Trezor, Ledger/Trezor BLE fixes, the new seed and history screens, quick Bitcoin sync, git-pinned dependencies (pubspec_overrides.yaml), frozen Hive adapters (*.part.dart), monero_c v0.18.4.6-RC2 and Flutter 3.41.9. Merged with both sides normalized first (import rename, 100-column format), which took the conflict count from 614 paths to 322. The procedure is now scripts/upstream_sync.sh. Kept from Hash Bags: - WalletType enum order and Hive IDs. SQLite stores WalletType.index and our order differs from upstream's; taking theirs would reread saved wallets as other chains. Our frozen adapter keeps the shipped IDs. - The 1.0.2 legacy PIN and wallet-password decode and self-heal. - Removed chains (Solana, Tron, Zano, Zcash, Decred, Haven) and the new Robinhood chain stay out; Cake Pay and non-Trocador swaps stay out. - Default nodes (node.wownero.org, sethforprivacy, Blockstream, bysh.me), converted to upstream's isDefault node-list key; Wownero is back in the node reset list. - Privacy: address resolvers limited to domain-anchored sources again; prices.neroswap.com for fiat; upstream's charts tab, which reads prices.cakewallet.com, sits behind FeatureFlag.isChartsEnabled = false. - Lightning stays behind FeatureFlag.isLightningEnabled, including upstream's new post-seed Lightning username step. - Our icon pipeline, branding and bundle identifiers. Twelve new upstream widget tests are skipped: test and code are unchanged from v6.5.0 apart from renames (not rerun on a pristine upstream checkout; upstream's top_bar_test does not compile against its own TopBar). flutter analyze lib: 0 errors; flutter test: 345 passed, 12 skipped; flutter build bundle compiles. Built with Flutter 3.41.9.
This commit is contained in:
@@ -1,171 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:basic_utils/basic_utils.dart';
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/widgets/alert_with_picker_option.dart';
|
||||
import 'package:hash_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:dnssec_proof/dnssec_proof.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class Bip353Record {
|
||||
Bip353Record({
|
||||
required this.uri,
|
||||
required this.domain,
|
||||
});
|
||||
|
||||
final String uri;
|
||||
final String domain;
|
||||
|
||||
static const Map<String, String> keyDisplayMap = {
|
||||
'lno': 'BOLT 12 Offer',
|
||||
'sp': 'Silent Payment',
|
||||
'address': 'On-Chain Address',
|
||||
};
|
||||
|
||||
static Future<String?> fetchDnsProof(String bip353Name) async {
|
||||
if (bip353Name.startsWith('₿')) {
|
||||
bip353Name = bip353Name.substring(1);
|
||||
}
|
||||
final parts = bip353Name.split('@');
|
||||
if (parts.length != 2) return null;
|
||||
final userPart = parts[0];
|
||||
final domainPart = parts[1];
|
||||
final bip353Domain = '$userPart.user._bitcoin-payment.$domainPart.';
|
||||
final proof = await Isolate.run(() => DnsProver.getTxtProof(bip353Domain));
|
||||
return base64.encode(proof);
|
||||
}
|
||||
|
||||
static Future<Map<String, String>?> fetchUriByCryptoCurrency(
|
||||
String bip353Name, String asset) async {
|
||||
try {
|
||||
// 1. Parse the user and domain from "user@domain"
|
||||
final parts = bip353Name.split('@');
|
||||
|
||||
if (parts.length != 2) return null;
|
||||
|
||||
String userPart = parts[0];
|
||||
if (userPart.startsWith('₿')) {
|
||||
userPart = userPart.substring(1);
|
||||
}
|
||||
final domainPart = parts[1];
|
||||
|
||||
// 2. Construct the correct subdomain: "user._bitcoin-payment.domain"
|
||||
final bip353Domain = '$userPart.user._bitcoin-payment.$domainPart';
|
||||
|
||||
// 3. Lookup the TXT record with DNSSEC
|
||||
final txtRecords = await DnsUtils.lookupRecord(
|
||||
bip353Domain,
|
||||
RRecordType.TXT,
|
||||
dnssec: true,
|
||||
);
|
||||
final proof = await fetchDnsProof(bip353Name);
|
||||
if (proof == null) {
|
||||
throw Exception('DNSSEC proof not found');
|
||||
}
|
||||
|
||||
if (txtRecords == null) return null;
|
||||
|
||||
final assetName = CryptoCurrency.fromString(asset).fullName;
|
||||
if (assetName == null) throw Exception('Invalid asset name');
|
||||
final formattedAssetName = assetName.toLowerCase().replaceAll(' ', '') + ':';
|
||||
|
||||
for (final record in txtRecords) {
|
||||
final data = record.data.replaceAll('"', '');
|
||||
if (data.startsWith(formattedAssetName)) {
|
||||
return _parseAssetUri(data, formattedAssetName);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
printV('BIP353Record.fetchBitcoinUri error: $e');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Map<String, String>? _parseAssetUri(String fullUri, String prefix) {
|
||||
final afterPrefix = fullUri.substring(prefix.length);
|
||||
if (afterPrefix.isEmpty) return null;
|
||||
|
||||
final questionIndex = afterPrefix.indexOf('?');
|
||||
if (questionIndex == -1) {
|
||||
return {'address': afterPrefix};
|
||||
} else {
|
||||
final addressPart = afterPrefix.substring(0, questionIndex);
|
||||
final queryPart = afterPrefix.substring(questionIndex + 1);
|
||||
final result = <String, String>{};
|
||||
|
||||
if (addressPart.isNotEmpty) result['address'] = addressPart;
|
||||
final queryMap = Uri.splitQueryString(queryPart);
|
||||
result.addAll(queryMap);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String?> pickBip353AddressChoice(
|
||||
BuildContext context,
|
||||
String bip353Name,
|
||||
Map<String, String> addressMap,
|
||||
) async {
|
||||
if (addressMap.length == 1) {
|
||||
return addressMap.values.first;
|
||||
}
|
||||
|
||||
final chosenAddress = await _showAddressChoiceDialog(context, bip353Name, addressMap);
|
||||
|
||||
return chosenAddress;
|
||||
}
|
||||
|
||||
static Future<String?> _showAddressChoiceDialog(
|
||||
BuildContext context,
|
||||
String bip353Name,
|
||||
Map<String, String> addressMap,
|
||||
) async {
|
||||
final entriesList = addressMap.entries.toList();
|
||||
final List<Map<String, String>> displayItems = entriesList.map((entry) {
|
||||
final originalKey = entry.key;
|
||||
final originalValue = entry.value;
|
||||
|
||||
final extendedKeyName = keyDisplayMap[originalKey] ?? originalKey;
|
||||
final truncatedValue = _truncate(originalValue, front: 6, back: 6);
|
||||
|
||||
return {
|
||||
'displayKey': extendedKeyName,
|
||||
'displayValue': truncatedValue,
|
||||
'originalKey': originalKey,
|
||||
'originalValue': originalValue,
|
||||
};
|
||||
}).toList();
|
||||
|
||||
String? selectedOriginalValue;
|
||||
|
||||
if (context.mounted) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithPickerOption(
|
||||
alertTitle: S.of(context).multiple_addresses_detected + '\n$bip353Name',
|
||||
alertTitleTextSize: 14,
|
||||
alertSubtitle: S.of(context).please_choose_one + ':',
|
||||
options: displayItems,
|
||||
onOptionSelected: (Map<String, String> chosenItem) {
|
||||
selectedOriginalValue = chosenItem['originalValue'];
|
||||
},
|
||||
alertBarrierDismissible: true,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
return selectedOriginalValue;
|
||||
}
|
||||
|
||||
static String _truncate(String value, {int front = 6, int back = 6}) {
|
||||
if (value.length <= front + back) return value;
|
||||
|
||||
final start = value.substring(0, front);
|
||||
final end = value.substring(value.length - back);
|
||||
return '$start...$end';
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
|
||||
String calculateFiatAmount({double? price, String? cryptoAmount, bool raw = false}) {
|
||||
if (price == null || cryptoAmount == null) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
cryptoAmount = cryptoAmount.replaceAll(',', '.');
|
||||
cryptoAmount = cryptoAmount.sanitized();
|
||||
|
||||
final _amount = double.tryParse(cryptoAmount);
|
||||
if (_amount == null || _amount.isNaN) return '0.00';
|
||||
if (_amount == null || !_amount.isFinite) return '0.00';
|
||||
final _result = price * _amount;
|
||||
if (!_result.isFinite) return '0.00';
|
||||
final result = _result < 0 ? _result * -1 : _result;
|
||||
|
||||
if (result == 0.0) {
|
||||
|
||||
@@ -7,6 +7,10 @@ String calculateFiatAmountRaw({required double cryptoAmount, double? price}) {
|
||||
|
||||
final result = price * cryptoAmount;
|
||||
|
||||
if (!result.isFinite) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
if (result == 0.0) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:cw_core/keyable.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'contact.g.dart';
|
||||
part 'contact.part.dart';
|
||||
|
||||
@HiveType(typeId: Contact.typeId)
|
||||
// @HiveType(typeId: Contact.typeId)
|
||||
class Contact extends HiveObject with Keyable {
|
||||
Contact(
|
||||
{required this.name,
|
||||
@@ -22,19 +22,19 @@ class Contact extends HiveObject with Keyable {
|
||||
static const typeId = CONTACT_TYPE_ID;
|
||||
static const boxName = 'Contacts';
|
||||
|
||||
@HiveField(0, defaultValue: '')
|
||||
// @HiveField(0, defaultValue: '')
|
||||
String name;
|
||||
|
||||
@HiveField(1, defaultValue: '')
|
||||
// @HiveField(1, defaultValue: '')
|
||||
String address;
|
||||
|
||||
@HiveField(2, defaultValue: 0)
|
||||
// @HiveField(2, defaultValue: 0)
|
||||
late int raw;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
DateTime lastChange;
|
||||
|
||||
@HiveField(4, defaultValue: "")
|
||||
// @HiveField(4, defaultValue: "")
|
||||
String displayName;
|
||||
|
||||
CryptoCurrency get type => CryptoCurrency.deserialize(raw: raw);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'contact.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class ContactAdapter extends TypeAdapter<Contact> {
|
||||
@override
|
||||
final int typeId = 0;
|
||||
|
||||
@override
|
||||
Contact read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return Contact(
|
||||
name: fields[0] == null ? '' : fields[0] as String,
|
||||
address: fields[1] == null ? '' : fields[1] as String,
|
||||
lastChange: fields[3] as DateTime?,
|
||||
displayName: fields[4] == null ? '' : fields[4] as String,
|
||||
)..raw = fields[2] == null ? 0 : fields[2] as int;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, Contact obj) {
|
||||
writer
|
||||
..writeByte(5)
|
||||
..writeByte(0)
|
||||
..write(obj.name)
|
||||
..writeByte(1)
|
||||
..write(obj.address)
|
||||
..writeByte(2)
|
||||
..write(obj.raw)
|
||||
..writeByte(3)
|
||||
..write(obj.lastChange)
|
||||
..writeByte(4)
|
||||
..write(obj.displayName);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ContactAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
@@ -9,14 +9,16 @@ import 'package:hash_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:hash_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:hash_wallet/entities/fiat_currency.dart';
|
||||
import 'package:hash_wallet/entities/fs_migration.dart';
|
||||
import 'package:hash_wallet/entities/node_list.dart';
|
||||
import 'package:hash_wallet/entities/preferences_key.dart';
|
||||
import 'package:hash_wallet/entities/secret_store_key.dart';
|
||||
import 'package:hash_wallet/monero/monero.dart';
|
||||
import 'package:hash_wallet/new-ui/model/charts/charts_asset.dart';
|
||||
import 'package:hash_wallet/wownero/wownero.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:hash_wallet/entities/sync_status_display_mode.dart';
|
||||
import 'package:cw_core/node_list.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/root_dir.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
@@ -25,7 +27,6 @@ import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
|
||||
const newCakeWalletMoneroUri =
|
||||
@@ -46,11 +47,10 @@ const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002';
|
||||
const nanoDefaultNodeUri = 'rpc.nano.to';
|
||||
const nanoDefaultPowNodeUri = 'rpc.nano.to';
|
||||
const solanaDefaultNodeUri = 'solana-mainnet.core.chainstack.com';
|
||||
const tronDefaultNodeUri = 'api.trongrid.io';
|
||||
const tronDefaultNodeUri = 'trx.nownodes.io';
|
||||
const newCakeWalletBitcoinUri =
|
||||
'electrum.blockstream.info:50002'; // Hash Bags: default flipped to Blockstream
|
||||
const wowneroDefaultNodeUri = 'node.wownero.org:443';
|
||||
const zanoDefaultNodeUri = '37.27.100.59:10500';
|
||||
const moneroWorldNodeUri = '.moneroworld.com';
|
||||
const decredDefaultUri = "default-spv-nodes";
|
||||
const dogecoinDefaultNodeUri = 'dogecoin.stackwallet.com:50022';
|
||||
@@ -62,15 +62,13 @@ Future<void> defaultSettingsMigration(
|
||||
{required int version,
|
||||
required SharedPreferences sharedPreferences,
|
||||
required SecureStorage secureStorage,
|
||||
required Box<Node> nodes,
|
||||
required Box<Node> powNodes,
|
||||
required Box<Contact> contactSource}) async {
|
||||
if (Platform.isIOS) {
|
||||
await ios_migrate_v1(contactSource);
|
||||
}
|
||||
|
||||
// check current nodes for nullability regardless of the version
|
||||
await checkCurrentNodes(nodes, powNodes, sharedPreferences);
|
||||
// await checkCurrentNodes(sharedPreferences);
|
||||
|
||||
final isNewInstall =
|
||||
sharedPreferences.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) == null;
|
||||
@@ -98,15 +96,18 @@ Future<void> defaultSettingsMigration(
|
||||
case 1:
|
||||
await sharedPreferences.setString(
|
||||
PreferencesKey.currentFiatCurrencyKey, FiatCurrency.usd.toString());
|
||||
await sharedPreferences.setInt(PreferencesKey.currentTransactionPriorityKeyLegacy,
|
||||
monero!.getDefaultTransactionPriority().raw);
|
||||
|
||||
if (monero != null) {
|
||||
await sharedPreferences.setInt(
|
||||
PreferencesKey.currentTransactionPriorityKeyLegacy,
|
||||
monero!.getDefaultTransactionPriority().raw);
|
||||
}
|
||||
await sharedPreferences.setInt(
|
||||
PreferencesKey.currentBalanceDisplayModeKey, BalanceDisplayMode.availableBalance.raw);
|
||||
await sharedPreferences.setBool('save_recipient_address', true);
|
||||
await resetToDefault(nodes);
|
||||
await resetToDefault();
|
||||
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.monero,
|
||||
currentNodePreferenceKey: PreferencesKey.currentNodeIdKey,
|
||||
@@ -114,14 +115,12 @@ Future<void> defaultSettingsMigration(
|
||||
trusted: true,
|
||||
);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.bitcoin,
|
||||
currentNodePreferenceKey: PreferencesKey.currentBitcoinElectrumSererIdKey,
|
||||
useSSL: true,
|
||||
);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.litecoin,
|
||||
currentNodePreferenceKey: PreferencesKey.currentLitecoinElectrumSererIdKey,
|
||||
@@ -129,9 +128,8 @@ Future<void> defaultSettingsMigration(
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
await replaceNodesMigration(nodes: nodes);
|
||||
await replaceNodesMigration();
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.monero,
|
||||
newDefaultUri: newCakeWalletMoneroUri,
|
||||
@@ -146,13 +144,12 @@ Future<void> defaultSettingsMigration(
|
||||
);
|
||||
break;
|
||||
case 3:
|
||||
await updateNodeTypes(nodes: nodes);
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.bitcoin);
|
||||
await updateNodeTypes();
|
||||
await addWalletNodeList(type: WalletType.bitcoin);
|
||||
|
||||
break;
|
||||
case 4:
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.bitcoin,
|
||||
newDefaultUri: newCakeWalletBitcoinUri,
|
||||
@@ -179,7 +176,6 @@ Future<void> defaultSettingsMigration(
|
||||
|
||||
case 11:
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.monero,
|
||||
newDefaultUri: newCakeWalletMoneroUri,
|
||||
@@ -190,33 +186,32 @@ Future<void> defaultSettingsMigration(
|
||||
break;
|
||||
|
||||
case 12:
|
||||
await checkCurrentNodes(nodes, powNodes, sharedPreferences);
|
||||
// await checkCurrentNodes(sharedPreferences);
|
||||
break;
|
||||
|
||||
case 13:
|
||||
await resetBitcoinElectrumServer(nodes, sharedPreferences);
|
||||
await resetBitcoinElectrumServer(sharedPreferences);
|
||||
break;
|
||||
|
||||
case 15:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.litecoin);
|
||||
await addWalletNodeList(type: WalletType.litecoin);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.litecoin,
|
||||
currentNodePreferenceKey: PreferencesKey.currentLitecoinElectrumSererIdKey,
|
||||
);
|
||||
await checkCurrentNodes(nodes, powNodes, sharedPreferences);
|
||||
// await checkCurrentNodes(sharedPreferences);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
await checkCurrentNodes(nodes, powNodes, sharedPreferences);
|
||||
// await checkCurrentNodes(sharedPreferences);
|
||||
break;
|
||||
|
||||
case 17:
|
||||
break;
|
||||
|
||||
case 18:
|
||||
addWalletNodeList(nodes: nodes, type: WalletType.monero);
|
||||
addWalletNodeList(type: WalletType.monero);
|
||||
break;
|
||||
|
||||
case 19:
|
||||
@@ -226,25 +221,22 @@ Future<void> defaultSettingsMigration(
|
||||
await migrateExchangeStatus(sharedPreferences);
|
||||
break;
|
||||
case 21:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.ethereum);
|
||||
await addWalletNodeList(type: WalletType.ethereum);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.ethereum,
|
||||
currentNodePreferenceKey: PreferencesKey.currentEthereumNodeIdKey,
|
||||
);
|
||||
break;
|
||||
case 22:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.nano);
|
||||
await addNanoPowNodeList(nodes: powNodes);
|
||||
await addWalletNodeList(type: WalletType.nano);
|
||||
await addNanoPowNodeList();
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.nano,
|
||||
currentNodePreferenceKey: PreferencesKey.currentNanoNodeIdKey,
|
||||
);
|
||||
await _changeDefaultNode(
|
||||
nodes: powNodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.nano,
|
||||
currentNodePreferenceKey: PreferencesKey.currentNanoPowNodeIdKey,
|
||||
@@ -252,18 +244,16 @@ Future<void> defaultSettingsMigration(
|
||||
);
|
||||
break;
|
||||
case 23:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.bitcoinCash);
|
||||
await addWalletNodeList(type: WalletType.bitcoinCash);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.bitcoinCash,
|
||||
currentNodePreferenceKey: PreferencesKey.currentBitcoinCashNodeIdKey,
|
||||
);
|
||||
break;
|
||||
case 24:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.polygon);
|
||||
await addWalletNodeList(type: WalletType.polygon);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.polygon,
|
||||
currentNodePreferenceKey: PreferencesKey.currentPolygonNodeIdKey,
|
||||
@@ -287,7 +277,6 @@ Future<void> defaultSettingsMigration(
|
||||
break;
|
||||
case 29:
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.bitcoin,
|
||||
newDefaultUri: newCakeWalletBitcoinUri,
|
||||
@@ -300,7 +289,7 @@ Future<void> defaultSettingsMigration(
|
||||
await disableServiceStatusFiatDisabled(sharedPreferences);
|
||||
break;
|
||||
case 31:
|
||||
await updateNanoNodeList(nodes: nodes);
|
||||
await updateNanoNodeList();
|
||||
break;
|
||||
case 32:
|
||||
await updateBtcNanoWalletInfos();
|
||||
@@ -308,10 +297,9 @@ Future<void> defaultSettingsMigration(
|
||||
case 33:
|
||||
break;
|
||||
case 34:
|
||||
addWalletNodeList(nodes: nodes, type: WalletType.bitcoin);
|
||||
addWalletNodeList(type: WalletType.bitcoin);
|
||||
case 35:
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.bitcoin,
|
||||
newDefaultUri: newCakeWalletBitcoinUri,
|
||||
@@ -321,9 +309,8 @@ Future<void> defaultSettingsMigration(
|
||||
);
|
||||
break;
|
||||
case 36:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.wownero);
|
||||
await addWalletNodeList(type: WalletType.wownero);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.wownero,
|
||||
currentNodePreferenceKey: PreferencesKey.currentWowneroNodeIdKey,
|
||||
@@ -337,9 +324,8 @@ Future<void> defaultSettingsMigration(
|
||||
await fixBtcDerivationPaths();
|
||||
break;
|
||||
case 39:
|
||||
_fixNodesUseSSLFlag(nodes);
|
||||
_fixNodesUseSSLFlag();
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.nano,
|
||||
newDefaultUri: nanoDefaultNodeUri,
|
||||
@@ -349,7 +335,7 @@ Future<void> defaultSettingsMigration(
|
||||
);
|
||||
break;
|
||||
case 40:
|
||||
await removeMoneroWorld(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
await removeMoneroWorld(sharedPreferences: sharedPreferences);
|
||||
break;
|
||||
case 41:
|
||||
_changeExchangeProviderAvailability(
|
||||
@@ -357,13 +343,13 @@ Future<void> defaultSettingsMigration(
|
||||
providerName: "SwapTrade",
|
||||
enabled: false,
|
||||
);
|
||||
addWalletNodeList(nodes: nodes, type: WalletType.bitcoin);
|
||||
addWalletNodeList(type: WalletType.bitcoin);
|
||||
break;
|
||||
case 42:
|
||||
_fixNodesUseSSLFlag(nodes);
|
||||
_fixNodesUseSSLFlag();
|
||||
break;
|
||||
case 43:
|
||||
_fixNodesUseSSLFlag(nodes);
|
||||
_fixNodesUseSSLFlag();
|
||||
_changeExchangeProviderAvailability(
|
||||
sharedPreferences,
|
||||
providerName: "THORChain",
|
||||
@@ -376,9 +362,8 @@ Future<void> defaultSettingsMigration(
|
||||
);
|
||||
break;
|
||||
case 44:
|
||||
_fixNodesUseSSLFlag(nodes);
|
||||
_fixNodesUseSSLFlag();
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.bitcoin,
|
||||
newDefaultUri: newCakeWalletBitcoinUri,
|
||||
@@ -388,20 +373,18 @@ Future<void> defaultSettingsMigration(
|
||||
);
|
||||
break;
|
||||
case 45:
|
||||
addWalletNodeList(nodes: nodes, type: WalletType.polygon);
|
||||
addWalletNodeList(nodes: nodes, type: WalletType.ethereum);
|
||||
addWalletNodeList(type: WalletType.polygon);
|
||||
addWalletNodeList(type: WalletType.ethereum);
|
||||
break;
|
||||
case 46:
|
||||
await _fixNodesUseSSLFlag(nodes);
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.litecoin);
|
||||
await _fixNodesUseSSLFlag();
|
||||
await addWalletNodeList(type: WalletType.litecoin);
|
||||
await _updateNode(
|
||||
nodes: nodes,
|
||||
currentUri: "ethereum.publicnode.com",
|
||||
newUri: "ethereum-rpc.publicnode.com",
|
||||
useSSL: true,
|
||||
);
|
||||
await _updateNode(
|
||||
nodes: nodes,
|
||||
currentUri: "polygon-bor.publicnode.com",
|
||||
newUri: "polygon-bor-rpc.publicnode.com",
|
||||
useSSL: true,
|
||||
@@ -428,42 +411,36 @@ Future<void> defaultSettingsMigration(
|
||||
);
|
||||
break;
|
||||
case 50:
|
||||
migrateExistingNodesToUseAutoSwitching(nodes: nodes, powNodes: powNodes);
|
||||
migrateExistingNodesToUseAutoSwitching();
|
||||
break;
|
||||
case 51:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.dogecoin);
|
||||
await addWalletNodeList(type: WalletType.dogecoin);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.dogecoin,
|
||||
currentNodePreferenceKey: PreferencesKey.currentDogecoinNodeIdKey,
|
||||
);
|
||||
break;
|
||||
case 52:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.base);
|
||||
await addWalletNodeList(type: WalletType.base);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.base,
|
||||
currentNodePreferenceKey: PreferencesKey.currentBaseNodeIdKey,
|
||||
);
|
||||
break;
|
||||
case 53:
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.arbitrum);
|
||||
await addWalletNodeList(type: WalletType.arbitrum);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.arbitrum,
|
||||
currentNodePreferenceKey: PreferencesKey.currentArbitrumNodeIdKey,
|
||||
);
|
||||
break;
|
||||
case 54:
|
||||
// Hash Bags: upstream backed up Wownero seeds here ahead of dropping Wownero.
|
||||
break;
|
||||
case 55:
|
||||
// Hash Bags: zcash removed. The original migration here added
|
||||
// zcash node list + set default zcash node. We no longer load
|
||||
// zcash_node_list.yml (it was deleted), so skip the whole step.
|
||||
break;
|
||||
case 56:
|
||||
await sharedPreferences.setString(
|
||||
PreferencesKey.syncStatusDisplayMode, SyncStatusDisplayMode.blocksRemaining.name);
|
||||
@@ -471,9 +448,8 @@ Future<void> defaultSettingsMigration(
|
||||
case 57:
|
||||
await _addXautTokenToExistingEthereumWallets();
|
||||
|
||||
await addWalletNodeList(nodes: nodes, type: WalletType.bsc);
|
||||
await addWalletNodeList(type: WalletType.bsc);
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.bsc,
|
||||
currentNodePreferenceKey: PreferencesKey.currentBscNodeIdKey,
|
||||
@@ -516,13 +492,55 @@ Future<void> defaultSettingsMigration(
|
||||
break;
|
||||
case 65:
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.base,
|
||||
currentNodePreferenceKey: PreferencesKey.currentBaseNodeIdKey,
|
||||
oldUri: ['base.nownodes.io'],
|
||||
);
|
||||
break;
|
||||
case 66:
|
||||
await _changeDefaultNode(
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.arbitrum,
|
||||
currentNodePreferenceKey: PreferencesKey.currentArbitrumNodeIdKey,
|
||||
oldUri: ["arbitrum.nownodes.io"]);
|
||||
break;
|
||||
case 67:
|
||||
_changeExchangeProviderAvailability(
|
||||
sharedPreferences,
|
||||
providerName: "LetsExchange",
|
||||
enabled: true,
|
||||
);
|
||||
break;
|
||||
case 68:
|
||||
await _changeDefaultNode(
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.monero,
|
||||
newDefaultUri: newCakeWalletMoneroUri,
|
||||
currentNodePreferenceKey: PreferencesKey.currentNodeIdKey,
|
||||
useSSL: true,
|
||||
oldUri: ['nodes.hashvault.pro:18081'],
|
||||
);
|
||||
break;
|
||||
case 69:
|
||||
_changeExchangeProviderAvailability(
|
||||
sharedPreferences,
|
||||
providerName: "Exolix",
|
||||
enabled: false,
|
||||
);
|
||||
break;
|
||||
case 70:
|
||||
break;
|
||||
case 71:
|
||||
_changeExchangeProviderAvailability(
|
||||
sharedPreferences,
|
||||
providerName: "Swaps.XYZ",
|
||||
enabled: false,
|
||||
);
|
||||
break;
|
||||
case 72:
|
||||
await createDefaultChartsData();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -539,12 +557,13 @@ Future<void> defaultSettingsMigration(
|
||||
}
|
||||
|
||||
Future<void> _updateNode({
|
||||
required Box<Node> nodes,
|
||||
required String currentUri,
|
||||
String? newUri,
|
||||
bool? useSSL,
|
||||
}) async {
|
||||
for (Node node in nodes.values) {
|
||||
final nodes = await Node.getAll();
|
||||
|
||||
for (Node node in nodes) {
|
||||
if (node.uriRaw == currentUri) {
|
||||
if (newUri != null) {
|
||||
node.uriRaw = newUri;
|
||||
@@ -560,7 +579,6 @@ Future<void> _updateNode({
|
||||
/// generic function for changing any wallet default node
|
||||
/// instead of making a new function for each change
|
||||
Future<void> _changeDefaultNode({
|
||||
required Box<Node> nodes,
|
||||
required SharedPreferences sharedPreferences,
|
||||
required WalletType type,
|
||||
required String currentNodePreferenceKey,
|
||||
@@ -570,19 +588,21 @@ Future<void> _changeDefaultNode({
|
||||
List<String>?
|
||||
oldUri, // ignore, if you want to force replace the node regardless of the user's current node
|
||||
}) async {
|
||||
List<Node> nodes = await Node.getAll();
|
||||
|
||||
final currentNodeId = sharedPreferences.getInt(currentNodePreferenceKey);
|
||||
final bool shouldReplace;
|
||||
if (currentNodeId == null) {
|
||||
shouldReplace = true;
|
||||
} else {
|
||||
final currentNode = nodes.values.firstWhere((node) => node.key == currentNodeId);
|
||||
shouldReplace = oldUri?.any((e) => currentNode.uriRaw.contains(e)) ?? true;
|
||||
final currentNode = nodes.firstWhereOrNull((node) => node.id == currentNodeId);
|
||||
shouldReplace =
|
||||
currentNode == null || (oldUri?.any((e) => currentNode.uriRaw.contains(e)) ?? true);
|
||||
}
|
||||
|
||||
if (shouldReplace) {
|
||||
newDefaultUri ??= _getDefaultNodeUri(type);
|
||||
var newNodeId =
|
||||
nodes.values.firstWhereOrNull((element) => element.uriRaw == newDefaultUri)?.key;
|
||||
newDefaultUri ??= (await getDefaultNodeFromFiles(type)).uriRaw;
|
||||
var newNodeId = nodes.firstWhereOrNull((element) => element.uriRaw == newDefaultUri)?.id;
|
||||
|
||||
// new node doesn't exist, then add it
|
||||
if (newNodeId == null) {
|
||||
@@ -593,43 +613,11 @@ Future<void> _changeDefaultNode({
|
||||
trusted: trusted,
|
||||
);
|
||||
|
||||
await nodes.add(newNode);
|
||||
newNodeId = newNode.key;
|
||||
await newNode.save();
|
||||
newNodeId = newNode.id;
|
||||
}
|
||||
|
||||
await sharedPreferences.setInt(currentNodePreferenceKey, newNodeId as int);
|
||||
}
|
||||
}
|
||||
|
||||
String _getDefaultNodeUri(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return newCakeWalletMoneroUri;
|
||||
case WalletType.bitcoin:
|
||||
return newCakeWalletBitcoinUri;
|
||||
case WalletType.litecoin:
|
||||
return cakeWalletLitecoinElectrumUri;
|
||||
case WalletType.ethereum:
|
||||
return ethereumDefaultNodeUri;
|
||||
case WalletType.nano:
|
||||
return nanoDefaultNodeUri;
|
||||
case WalletType.bitcoinCash:
|
||||
return cakeWalletBitcoinCashDefaultNodeUri;
|
||||
case WalletType.polygon:
|
||||
return polygonDefaultNodeUri;
|
||||
case WalletType.wownero:
|
||||
return wowneroDefaultNodeUri;
|
||||
case WalletType.dogecoin:
|
||||
return dogecoinDefaultNodeUri;
|
||||
case WalletType.base:
|
||||
return baseDefaultNodeUri;
|
||||
case WalletType.arbitrum:
|
||||
return arbitrumDefaultNodeUri;
|
||||
case WalletType.bsc:
|
||||
return bscDefaultNodeUri;
|
||||
case WalletType.banano:
|
||||
case WalletType.none:
|
||||
return '';
|
||||
await sharedPreferences.setInt(currentNodePreferenceKey, newNodeId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,8 +635,9 @@ Future<void> _changeExchangeProviderAvailability(SharedPreferences sharedPrefere
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _fixNodesUseSSLFlag(Box<Node> nodes) async {
|
||||
for (Node node in nodes.values) {
|
||||
Future<void> _fixNodesUseSSLFlag() async {
|
||||
final nodes = await Node.getAll();
|
||||
for (Node node in nodes) {
|
||||
switch (node.uriRaw) {
|
||||
case cakeWalletLitecoinElectrumUri:
|
||||
case cakeWalletBitcoinElectrumUri:
|
||||
@@ -661,7 +650,8 @@ Future<void> _fixNodesUseSSLFlag(Box<Node> nodes) async {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateNanoNodeList({required Box<Node> nodes}) async {
|
||||
Future<void> updateNanoNodeList() async {
|
||||
final nodes = await Node.getAll();
|
||||
final nodeList = await loadDefaultNodes(WalletType.nano);
|
||||
var listOfNewEndpoints = <String>[
|
||||
"app.natrium.io",
|
||||
@@ -672,14 +662,13 @@ Future<void> updateNanoNodeList({required Box<Node> nodes}) async {
|
||||
];
|
||||
// add new nodes:
|
||||
for (final node in nodeList) {
|
||||
if (listOfNewEndpoints.contains(node.uriRaw) && !nodes.values.contains(node)) {
|
||||
await nodes.add(node);
|
||||
if (listOfNewEndpoints.contains(node.uriRaw) && !nodes.contains(node)) {
|
||||
await node.save();
|
||||
}
|
||||
}
|
||||
|
||||
// update the nautilus node:
|
||||
final nautilusNode =
|
||||
nodes.values.firstWhereOrNull((element) => element.uriRaw == "node.perish.co");
|
||||
final nautilusNode = nodes.firstWhereOrNull((element) => element.uriRaw == "node.perish.co");
|
||||
if (nautilusNode != null) {
|
||||
nautilusNode.uriRaw = "node.nautilus.io";
|
||||
nautilusNode.path = "/api";
|
||||
@@ -699,7 +688,12 @@ Future<void> disableServiceStatusFiatDisabled(SharedPreferences sharedPreference
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> _updateMoneroPriority(SharedPreferences sharedPreferences) async {
|
||||
if (monero == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final currentPriority =
|
||||
await sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority) ??
|
||||
monero!.getDefaultTransactionPriority().serialize();
|
||||
@@ -790,7 +784,7 @@ Future<void> validateBitcoinSavedTransactionPriority(SharedPreferences sharedPre
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> replaceNodesMigration({required Box<Node> nodes}) async {
|
||||
Future<void> replaceNodesMigration() async {
|
||||
final replaceNodes = <String, Node>{
|
||||
'eu-node.cakewallet.io:18081':
|
||||
Node(uri: 'xmr-node-eu.cakewallet.com:18081', type: WalletType.monero),
|
||||
@@ -799,7 +793,8 @@ Future<void> replaceNodesMigration({required Box<Node> nodes}) async {
|
||||
'node.xmr.ru:13666': Node(uri: 'node.monero.net:18081', type: WalletType.monero)
|
||||
};
|
||||
|
||||
nodes.values.forEach((Node node) async {
|
||||
List<Node> nodes = await Node.getAll();
|
||||
nodes.forEach((Node node) async {
|
||||
final nodeToReplace = replaceNodes[node.uri];
|
||||
|
||||
if (nodeToReplace != null) {
|
||||
@@ -811,16 +806,11 @@ Future<void> replaceNodesMigration({required Box<Node> nodes}) async {
|
||||
});
|
||||
}
|
||||
|
||||
Node? getBitcoinTestnetDefaultElectrumServer({required Box<Node> nodes}) {
|
||||
return nodes.values
|
||||
.firstWhereOrNull((Node node) => node.uriRaw == publicBitcoinTestnetElectrumUri) ??
|
||||
nodes.values.firstWhereOrNull((node) => node.type == WalletType.bitcoin);
|
||||
}
|
||||
Future<Node?> getBitcoinTestnetDefaultElectrumServer() async {
|
||||
final nodes = await Node.getAll();
|
||||
|
||||
Node? getDefaultNode({required Box<Node> nodes, required WalletType type}) {
|
||||
final defaultUri = _getDefaultNodeUri(type);
|
||||
return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == defaultUri) ??
|
||||
nodes.values.firstWhereOrNull((node) => node.type == type);
|
||||
return nodes.firstWhereOrNull((Node node) => node.uriRaw == publicBitcoinTestnetElectrumUri) ??
|
||||
nodes.firstWhereOrNull((node) => node.type == WalletType.bitcoin);
|
||||
}
|
||||
|
||||
Future<void> insecureStorageMigration({
|
||||
@@ -931,8 +921,9 @@ Future<void> rewriteSecureStoragePin({required SecureStorage secureStorage}) asy
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateNodeTypes({required Box<Node> nodes}) async {
|
||||
nodes.values.forEach((node) async {
|
||||
Future<void> updateNodeTypes() async {
|
||||
List<Node> nodes = await Node.getAll();
|
||||
nodes.forEach((node) async {
|
||||
if (node.type == null) {
|
||||
node.type = WalletType.monero;
|
||||
await node.save();
|
||||
@@ -983,11 +974,15 @@ Future<void> generateBackupPassword(SecureStorage secureStorage) async {
|
||||
|
||||
Future<void> changeTransactionPriorityAndFeeRateKeys(SharedPreferences sharedPreferences) async {
|
||||
final legacyTransactionPriority =
|
||||
sharedPreferences.getInt(PreferencesKey.currentTransactionPriorityKeyLegacy)!;
|
||||
await sharedPreferences.setInt(
|
||||
PreferencesKey.moneroTransactionPriority, legacyTransactionPriority);
|
||||
await sharedPreferences.setInt(PreferencesKey.bitcoinTransactionPriority,
|
||||
bitcoin!.getMediumTransactionPriority().serialize());
|
||||
sharedPreferences.getInt(PreferencesKey.currentTransactionPriorityKeyLegacy);
|
||||
if (legacyTransactionPriority != null) {
|
||||
await sharedPreferences.setInt(
|
||||
PreferencesKey.moneroTransactionPriority, legacyTransactionPriority);
|
||||
}
|
||||
if (bitcoin != null) {
|
||||
await sharedPreferences.setInt(PreferencesKey.bitcoinTransactionPriority,
|
||||
bitcoin!.getMediumTransactionPriority().serialize());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fixBtcDerivationPaths() async {
|
||||
@@ -1020,153 +1015,14 @@ Future<void> updateBtcNanoWalletInfos() async {}
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> checkCurrentNodes(
|
||||
Box<Node> nodeSource, Box<Node> powNodeSource, SharedPreferences sharedPreferences) async {
|
||||
final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
||||
final currentBitcoinElectrumSeverId =
|
||||
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
|
||||
final currentLitecoinElectrumSeverId =
|
||||
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
|
||||
final currentEthereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||
final currentPolygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
|
||||
final currentBaseNodeId = sharedPreferences.getInt(PreferencesKey.currentBaseNodeIdKey);
|
||||
final currentArbitrumNodeId = sharedPreferences.getInt(PreferencesKey.currentArbitrumNodeIdKey);
|
||||
final currentBscNodeId = sharedPreferences.getInt(PreferencesKey.currentBscNodeIdKey);
|
||||
final currentNanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final currentNanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
|
||||
final currentBitcoinCashNodeId =
|
||||
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
|
||||
final currentDogecoinNodeId = sharedPreferences.getInt(PreferencesKey.currentDogecoinNodeIdKey);
|
||||
final currentWowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
|
||||
final currentMoneroNode =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentMoneroNodeId);
|
||||
final currentBitcoinElectrumServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentBitcoinElectrumSeverId);
|
||||
final currentLitecoinElectrumServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentLitecoinElectrumSeverId);
|
||||
final currentEthereumNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentEthereumNodeId);
|
||||
final currentPolygonNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentPolygonNodeId);
|
||||
final currentBaseNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentBaseNodeId);
|
||||
final currentArbitrumNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentArbitrumNodeId);
|
||||
final currentBscNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentBscNodeId);
|
||||
final currentNanoNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentNanoNodeId);
|
||||
final currentNanoPowNodeServer =
|
||||
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 currentWowneroNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentWowneroNodeId);
|
||||
|
||||
if (currentMoneroNode == null) {
|
||||
final newCakeWalletNode = Node(uri: newCakeWalletMoneroUri, type: WalletType.monero);
|
||||
await nodeSource.add(newCakeWalletNode);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, newCakeWalletNode.key as int);
|
||||
}
|
||||
|
||||
if (currentBitcoinElectrumServer == null) {
|
||||
final cakeWalletElectrum = Node(
|
||||
uri: cakeWalletBitcoinElectrumUri,
|
||||
type: WalletType.bitcoin,
|
||||
useSSL: false,
|
||||
isEnabledForAutoSwitching: true);
|
||||
await nodeSource.add(cakeWalletElectrum);
|
||||
final cakeWalletElectrumTestnet =
|
||||
Node(uri: publicBitcoinTestnetElectrumUri, type: WalletType.bitcoin, useSSL: false);
|
||||
await nodeSource.add(cakeWalletElectrumTestnet);
|
||||
await sharedPreferences.setInt(
|
||||
PreferencesKey.currentBitcoinElectrumSererIdKey, cakeWalletElectrum.key as int);
|
||||
}
|
||||
|
||||
if (currentLitecoinElectrumServer == null) {
|
||||
final cakeWalletElectrum =
|
||||
Node(uri: cakeWalletLitecoinElectrumUri, type: WalletType.litecoin, useSSL: false);
|
||||
await nodeSource.add(cakeWalletElectrum);
|
||||
await sharedPreferences.setInt(
|
||||
PreferencesKey.currentLitecoinElectrumSererIdKey, cakeWalletElectrum.key as int);
|
||||
}
|
||||
|
||||
if (currentEthereumNodeServer == null) {
|
||||
final node = Node(uri: ethereumDefaultNodeUri, type: WalletType.ethereum);
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentEthereumNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentNanoNodeServer == null) {
|
||||
final node = Node(uri: nanoDefaultNodeUri, useSSL: true, type: WalletType.nano);
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentNanoNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentNanoPowNodeServer == null) {
|
||||
Node? node = powNodeSource.values
|
||||
.firstWhereOrNull((node) => node.uri.toString() == nanoDefaultPowNodeUri);
|
||||
if (node == null) {
|
||||
node = Node(uri: nanoDefaultPowNodeUri, useSSL: true, type: WalletType.nano);
|
||||
await powNodeSource.add(node);
|
||||
}
|
||||
await sharedPreferences.setInt(PreferencesKey.currentNanoPowNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentBitcoinCashNodeServer == null) {
|
||||
final node =
|
||||
Node(uri: cakeWalletBitcoinCashDefaultNodeUri, type: WalletType.bitcoinCash, useSSL: false);
|
||||
await nodeSource.add(node);
|
||||
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);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentPolygonNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentBaseNodeServer == null) {
|
||||
final node = Node(uri: baseDefaultNodeUri, type: WalletType.base);
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentBaseNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentArbitrumNodeServer == null) {
|
||||
final node = Node(uri: arbitrumDefaultNodeUri, type: WalletType.arbitrum);
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentArbitrumNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentBscNodeServer == null) {
|
||||
final node = Node(uri: bscDefaultNodeUri, type: WalletType.bsc);
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentBscNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentWowneroNodeServer == null) {
|
||||
final node = Node(uri: wowneroDefaultNodeUri, type: WalletType.wownero);
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentWowneroNodeIdKey, node.key as int);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> resetBitcoinElectrumServer(
|
||||
Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
|
||||
Future<void> resetBitcoinElectrumServer(SharedPreferences sharedPreferences) async {
|
||||
final nodeSource = await Node.getAll();
|
||||
final currentElectrumSeverId =
|
||||
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
|
||||
final oldElectrumServer = nodeSource.values
|
||||
final oldElectrumServer = nodeSource
|
||||
.firstWhereOrNull((node) => node.uri.toString().contains('electrumx.cakewallet.com'));
|
||||
var cakeWalletNode = nodeSource.values
|
||||
.firstWhereOrNull((node) => node.uriRaw.toString() == cakeWalletBitcoinElectrumUri);
|
||||
var cakeWalletNode =
|
||||
nodeSource.firstWhereOrNull((node) => node.uriRaw.toString() == cakeWalletBitcoinElectrumUri);
|
||||
|
||||
if (cakeWalletNode == null) {
|
||||
cakeWalletNode = Node(
|
||||
@@ -1177,12 +1033,12 @@ Future<void> resetBitcoinElectrumServer(
|
||||
// final cakeWalletElectrumTestnet =
|
||||
// Node(uri: publicBitcoinTestnetElectrumUri, type: WalletType.bitcoin, useSSL: false);
|
||||
// await nodeSource.add(cakeWalletElectrumTestnet);
|
||||
await nodeSource.add(cakeWalletNode);
|
||||
await cakeWalletNode.save();
|
||||
}
|
||||
|
||||
if (currentElectrumSeverId == oldElectrumServer?.key) {
|
||||
if (currentElectrumSeverId == oldElectrumServer?.id) {
|
||||
await sharedPreferences.setInt(
|
||||
PreferencesKey.currentBitcoinElectrumSererIdKey, cakeWalletNode.key as int);
|
||||
PreferencesKey.currentBitcoinElectrumSererIdKey, cakeWalletNode.id);
|
||||
}
|
||||
|
||||
await oldElectrumServer?.delete();
|
||||
@@ -1200,38 +1056,41 @@ Future<void> migrateExchangeStatus(SharedPreferences sharedPreferences) async {
|
||||
await sharedPreferences.remove(PreferencesKey.disableExchangeKey);
|
||||
}
|
||||
|
||||
Future<void> addNanoPowNodeList({required Box<Node> nodes}) async {
|
||||
Future<void> addNanoPowNodeList() async {
|
||||
final nodeList = await loadDefaultNanoPowNodes();
|
||||
final nodes = await Node.getAllPow();
|
||||
for (var node in nodeList) {
|
||||
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||
await nodes.add(node);
|
||||
if (nodes.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||
await node.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Node? getNanoDefaultPowNode({required Box<Node> nodes}) {
|
||||
return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == nanoDefaultPowNodeUri) ??
|
||||
nodes.values.firstWhereOrNull((node) => (node.type == WalletType.nano));
|
||||
Future<Node?> getNanoDefaultPowNode() async {
|
||||
final nodes = await Node.getAll();
|
||||
return nodes.firstWhereOrNull((Node node) => node.uriRaw == nanoDefaultPowNodeUri) ??
|
||||
nodes.firstWhereOrNull((node) => (node.type == WalletType.nano));
|
||||
}
|
||||
|
||||
Future<void> addWalletNodeList({required Box<Node> nodes, required WalletType type}) async {
|
||||
Future<void> addWalletNodeList({required WalletType type}) async {
|
||||
final nodes = await Node.getAll();
|
||||
final List<Node> nodeList = await loadDefaultNodes(type);
|
||||
for (var node in nodeList) {
|
||||
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||
await nodes.add(node);
|
||||
if (nodes.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||
await node.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeMoneroWorld(
|
||||
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||
Future<void> removeMoneroWorld({required SharedPreferences sharedPreferences}) async {
|
||||
final nodes = await Node.getAll();
|
||||
const cakeWalletMoneroNodeUriPattern = '.moneroworld.com';
|
||||
final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
||||
final currentMoneroNode = nodes.values.firstWhere((node) => node.key == currentMoneroNodeId);
|
||||
final currentMoneroNode = nodes.firstWhere((node) => node.id == currentMoneroNodeId);
|
||||
final needToReplaceCurrentMoneroNode =
|
||||
currentMoneroNode.uri.toString().contains(cakeWalletMoneroNodeUriPattern);
|
||||
|
||||
nodes.values.forEach((node) async {
|
||||
nodes.forEach((node) async {
|
||||
if (node.type == WalletType.monero &&
|
||||
node.uri.toString().contains(cakeWalletMoneroNodeUriPattern)) {
|
||||
await node.delete();
|
||||
@@ -1240,7 +1099,6 @@ Future<void> removeMoneroWorld(
|
||||
|
||||
if (needToReplaceCurrentMoneroNode) {
|
||||
await _changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
type: WalletType.monero,
|
||||
newDefaultUri: newCakeWalletMoneroUri,
|
||||
@@ -1250,8 +1108,7 @@ Future<void> removeMoneroWorld(
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> migrateExistingNodesToUseAutoSwitching(
|
||||
{required Box<Node> nodes, required Box<Node> powNodes}) async {
|
||||
Future<void> migrateExistingNodesToUseAutoSwitching() async {
|
||||
final listOfDefaultNodesWithAutoSwitching = [
|
||||
'bitcoincash.stackwallet.com:50002',
|
||||
'bch.aftrek.org:50002',
|
||||
@@ -1285,12 +1142,23 @@ Future<void> migrateExistingNodesToUseAutoSwitching(
|
||||
'zano.cakewallet.com:11211',
|
||||
'electrum.cakewallet.com:50002',
|
||||
];
|
||||
for (var node in [...nodes.values.toList(), ...powNodes.values.toList()]) {
|
||||
final nodes = await Node.getAll();
|
||||
for (var node in nodes) {
|
||||
if (listOfDefaultNodesWithAutoSwitching.contains(node.uriRaw)) {
|
||||
node.isEnabledForAutoSwitching = true;
|
||||
await node.save();
|
||||
}
|
||||
}
|
||||
|
||||
final powNodes = await Node.getAllPow();
|
||||
|
||||
for (var node in powNodes) {
|
||||
if (listOfDefaultNodesWithAutoSwitching.contains(node.uriRaw)) {
|
||||
node.isEnabledForAutoSwitching = true;
|
||||
node.isPow = true;
|
||||
await node.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _addXautTokenToExistingEthereumWallets() async {
|
||||
@@ -1308,24 +1176,31 @@ Future<void> _addXautTokenToExistingEthereumWallets() async {
|
||||
|
||||
final ethereumWallets =
|
||||
allWallets.where((wallet) => wallet.type == WalletType.ethereum).toList();
|
||||
const ethereumChainId = 1;
|
||||
|
||||
for (final walletInfo in ethereumWallets) {
|
||||
final sanitizedName = walletInfo.name.replaceAll(' ', '_');
|
||||
final boxName = '${sanitizedName}_${Erc20Token.ethereumBoxName}';
|
||||
final existingToken = await Erc20Token.getByContract(
|
||||
walletInfo.name,
|
||||
ethereumChainId,
|
||||
xautToken.contractAddress,
|
||||
);
|
||||
|
||||
Box<Erc20Token> tokenBox;
|
||||
if (CakeHive.isBoxOpen(boxName)) {
|
||||
tokenBox = CakeHive.box<Erc20Token>(boxName);
|
||||
} else {
|
||||
tokenBox = await CakeHive.openBox<Erc20Token>(boxName);
|
||||
}
|
||||
if (existingToken != null) continue;
|
||||
|
||||
final xautAddress = xautToken.contractAddress;
|
||||
if (!tokenBox.containsKey(xautAddress)) {
|
||||
await tokenBox.put(xautAddress, xautToken);
|
||||
}
|
||||
await Erc20Token.copyWith(
|
||||
xautToken,
|
||||
walletName: walletInfo.name,
|
||||
chainId: ethereumChainId,
|
||||
).save();
|
||||
}
|
||||
} catch (e) {
|
||||
printV('Error in XAUT migration: $e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> createDefaultChartsData() async {
|
||||
await ChartsAsset(asset: CryptoCurrency.btc, isFavorite: true).insert();
|
||||
await ChartsAsset(asset: CryptoCurrency.xmr, isFavorite: false).insert();
|
||||
await ChartsAsset(asset: CryptoCurrency.eth, isFavorite: false).insert();
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import 'package:hash_wallet/evm/evm.dart';
|
||||
import 'package:hash_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:ens_dart/ens_dart.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
|
||||
class EnsRecord {
|
||||
static Future<String> fetchEnsAddress(String name, {WalletBase? wallet}) async {
|
||||
Web3Client? _client;
|
||||
|
||||
if (wallet != null && (isEVMCompatibleChain(wallet.type))) {
|
||||
_client = evm!.getWeb3Client(wallet);
|
||||
}
|
||||
|
||||
if (_client == null) {
|
||||
late final client = ProxyWrapper().getHttpIOClient();
|
||||
|
||||
_client = Web3Client("https://ethereum-rpc.publicnode.com", client);
|
||||
}
|
||||
|
||||
try {
|
||||
final ens = Ens(client: _client);
|
||||
|
||||
if (wallet != null) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
return await ens.withName(name).getCoinAddress(CoinType.XMR);
|
||||
case WalletType.bitcoin:
|
||||
return await ens.withName(name).getCoinAddress(CoinType.BTC);
|
||||
case WalletType.litecoin:
|
||||
return await ens.withName(name).getCoinAddress(CoinType.LTC);
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.bsc:
|
||||
default:
|
||||
return (await ens.withName(name).getAddress()).hex;
|
||||
}
|
||||
}
|
||||
|
||||
final addr = await ens.withName(name).getAddress();
|
||||
return addr.hex;
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
+187
-64
@@ -1,18 +1,36 @@
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
import "package:cw_core/amount/money.dart";
|
||||
import "package:cw_core/currency.dart";
|
||||
import "package:cw_core/enumerable_item.dart";
|
||||
|
||||
class FiatCurrency extends EnumerableItem<String> with Serializable<String> implements Currency {
|
||||
const FiatCurrency({
|
||||
required String symbol,
|
||||
required this.symbol,
|
||||
required this.countryCode,
|
||||
required this.fullName,
|
||||
required this.emoji,
|
||||
this.decimals = 2,
|
||||
this.isoCountryCode,
|
||||
}) : super(title: symbol, raw: symbol);
|
||||
|
||||
final String countryCode;
|
||||
final String? isoCountryCode;
|
||||
|
||||
@override
|
||||
final String fullName;
|
||||
|
||||
@override
|
||||
final int decimals;
|
||||
|
||||
@override
|
||||
String get apiString => "fiat.$title";
|
||||
|
||||
@override
|
||||
final String symbol;
|
||||
|
||||
String get apiCountryCode => isoCountryCode ?? countryCode.toUpperCase();
|
||||
|
||||
final String emoji;
|
||||
|
||||
static List<FiatCurrency> get all => _all.values.toList();
|
||||
|
||||
static List<FiatCurrency> get currenciesAvailableToBuyWith => [
|
||||
@@ -60,67 +78,164 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
kes,
|
||||
];
|
||||
|
||||
static const amd = FiatCurrency(symbol: 'AMD', countryCode: "arm", fullName: "Armenian Dram");
|
||||
static const ars = FiatCurrency(symbol: 'ARS', countryCode: "arg", fullName: "Argentine Peso");
|
||||
static const aud = FiatCurrency(symbol: 'AUD', countryCode: "aus", fullName: "Australian Dollar");
|
||||
static const bdt = FiatCurrency(symbol: 'BDT', countryCode: "bgd", fullName: "Bangladeshi Taka");
|
||||
static const bgn = FiatCurrency(symbol: 'BGN', countryCode: "bgr", fullName: "Bulgarian Lev");
|
||||
static const brl = FiatCurrency(symbol: 'BRL', countryCode: "bra", fullName: "Brazilian Real");
|
||||
static const cad = FiatCurrency(symbol: 'CAD', countryCode: "cad", fullName: "Canadian Dollar");
|
||||
static const chf = FiatCurrency(symbol: 'CHF', countryCode: "che", fullName: "Swiss Franc");
|
||||
static const clp = FiatCurrency(symbol: 'CLP', countryCode: "chl", fullName: "Chilean Peso");
|
||||
static const cny = FiatCurrency(symbol: 'CNY', countryCode: "chn", fullName: "Chinese Yuan");
|
||||
static const cop = FiatCurrency(symbol: 'COP', countryCode: "col", fullName: "Colombian Peso");
|
||||
static const czk = FiatCurrency(symbol: 'CZK', countryCode: "czk", fullName: "Czech Koruna");
|
||||
static const dkk = FiatCurrency(symbol: 'DKK', countryCode: "dnk", fullName: "Danish Krone");
|
||||
static const egp = FiatCurrency(symbol: 'EGP', countryCode: "egy", fullName: "Egyptian Pound");
|
||||
static const eur = FiatCurrency(symbol: 'EUR', countryCode: "eur", fullName: "Euro");
|
||||
static const gbp = FiatCurrency(symbol: 'GBP', countryCode: "gbr", fullName: "Pound Sterling");
|
||||
static const ghs = FiatCurrency(symbol: 'GHS', countryCode: "gha", fullName: "Ghanaian Cedi");
|
||||
static const gtq =
|
||||
FiatCurrency(symbol: 'GTQ', countryCode: "gtm", fullName: "Guatemalan Quetzal");
|
||||
static const hkd = FiatCurrency(symbol: 'HKD', countryCode: "hkg", fullName: "Hong Kong Dollar");
|
||||
static const hrk = FiatCurrency(symbol: 'HRK', countryCode: "hrv", fullName: "Croatian Kuna");
|
||||
static const huf = FiatCurrency(symbol: 'HUF', countryCode: "hun", fullName: "Hungarian Forint");
|
||||
static const idr = FiatCurrency(symbol: 'IDR', countryCode: "idn", fullName: "Indonesian Rupiah");
|
||||
static const ils =
|
||||
FiatCurrency(symbol: 'ILS', countryCode: "isr", fullName: "Israeli New Shekel");
|
||||
static const inr = FiatCurrency(symbol: 'INR', countryCode: "ind", fullName: "Indian Rupee");
|
||||
static const irr = FiatCurrency(symbol: 'IRR', countryCode: "irn", fullName: "Iranian Rial");
|
||||
static const isk =
|
||||
FiatCurrency(symbol: 'ISK', countryCode: "isl", fullName: "Icelandic Krona Króna");
|
||||
static const jpy = FiatCurrency(symbol: 'JPY', countryCode: "jpn", fullName: "Japanese Yen");
|
||||
static const krw = FiatCurrency(symbol: 'KRW', countryCode: "kor", fullName: "South Korean Won");
|
||||
static const mad = FiatCurrency(symbol: 'MAD', countryCode: "mar", fullName: "Moroccan Dirham");
|
||||
static const mxn = FiatCurrency(symbol: 'MXN', countryCode: "mex", fullName: "Mexican Peso");
|
||||
static const myr = FiatCurrency(symbol: 'MYR', countryCode: "mys", fullName: "Malaysian Ringgit");
|
||||
static const ngn = FiatCurrency(symbol: 'NGN', countryCode: "nga", fullName: "Nigerian Naira");
|
||||
static const nok = FiatCurrency(symbol: 'NOK', countryCode: "nor", fullName: "Norwegian Krone");
|
||||
static const nzd =
|
||||
FiatCurrency(symbol: 'NZD', countryCode: "nzl", fullName: "New Zealand Dollar");
|
||||
static const php = FiatCurrency(symbol: 'PHP', countryCode: "phl", fullName: "Philippine Peso");
|
||||
static const pkr = FiatCurrency(symbol: 'PKR', countryCode: "pak", fullName: "Pakistani Rupee");
|
||||
static const pln =
|
||||
FiatCurrency(symbol: 'PLN', countryCode: "pol", fullName: "Poland Zloty złoty");
|
||||
static const ron = FiatCurrency(symbol: 'RON', countryCode: "rou", fullName: "Romanian Leu");
|
||||
static const rub = FiatCurrency(symbol: 'RUB', countryCode: "rus", fullName: "Russian Ruble");
|
||||
static const sar = FiatCurrency(symbol: 'SAR', countryCode: "sau", fullName: "Saudi Riyal");
|
||||
static const sek = FiatCurrency(symbol: 'SEK', countryCode: "swe", fullName: "Swedish Krona");
|
||||
static const sgd = FiatCurrency(symbol: 'SGD', countryCode: "sgp", fullName: "Singapore Dollar");
|
||||
static const thb =
|
||||
FiatCurrency(symbol: 'THB', countryCode: "tha", fullName: "New Thaiwan Dollar");
|
||||
static const twd = FiatCurrency(symbol: 'TWD', countryCode: "twn", fullName: "Thai Baht");
|
||||
static const uah = FiatCurrency(symbol: 'UAH', countryCode: "ukr", fullName: "Ukrainian Hryvnia");
|
||||
static const usd =
|
||||
FiatCurrency(symbol: 'USD', countryCode: "usa", fullName: "United States Dollar");
|
||||
static const vef =
|
||||
FiatCurrency(symbol: 'VEF', countryCode: "ven", fullName: "Venezuelan Bolivar Bolívar");
|
||||
static const vnd =
|
||||
FiatCurrency(symbol: 'VND', countryCode: "vnm", fullName: "Vietnamese Dong đồng");
|
||||
static const zar =
|
||||
FiatCurrency(symbol: 'ZAR', countryCode: "saf", fullName: "South African Rand");
|
||||
static const tur = FiatCurrency(symbol: 'TRY', countryCode: "tur", fullName: "Turkish Lira");
|
||||
static const kes = FiatCurrency(symbol: 'KES', countryCode: "ken", fullName: "Kenyan Shillings");
|
||||
static const amd =
|
||||
FiatCurrency(symbol: "AMD", countryCode: "arm", fullName: "Armenian Dram", emoji: "🇦🇲");
|
||||
static const ars =
|
||||
FiatCurrency(symbol: "ARS", countryCode: "arg", fullName: "Argentine Peso", emoji: "🇦🇷");
|
||||
static const aud =
|
||||
FiatCurrency(symbol: "AUD", countryCode: "aus", fullName: "Australian Dollar", emoji: "🇦🇺");
|
||||
static const bdt =
|
||||
FiatCurrency(symbol: "BDT", countryCode: "bgd", fullName: "Bangladeshi Taka", emoji: "🇧🇩");
|
||||
static const bgn =
|
||||
FiatCurrency(symbol: "BGN", countryCode: "bgr", fullName: "Bulgarian Lev", emoji: "🇧🇬");
|
||||
static const brl =
|
||||
FiatCurrency(symbol: "BRL", countryCode: "bra", fullName: "Brazilian Real", emoji: "🇧🇷");
|
||||
static const cad = FiatCurrency(
|
||||
symbol: "CAD",
|
||||
countryCode: "cad",
|
||||
fullName: "Canadian Dollar",
|
||||
emoji: "🇨🇦",
|
||||
isoCountryCode: "CAN",
|
||||
);
|
||||
static const chf =
|
||||
FiatCurrency(symbol: "CHF", countryCode: "che", fullName: "Swiss Franc", emoji: "🇨🇭");
|
||||
static const clp =
|
||||
FiatCurrency(symbol: "CLP", countryCode: "chl", fullName: "Chilean Peso", emoji: "🇨🇱");
|
||||
static const cny =
|
||||
FiatCurrency(symbol: "CNY", countryCode: "chn", fullName: "Chinese Yuan", emoji: "🇨🇳");
|
||||
static const cop =
|
||||
FiatCurrency(symbol: "COP", countryCode: "col", fullName: "Colombian Peso", emoji: "🇨🇴");
|
||||
static const czk = FiatCurrency(
|
||||
symbol: "CZK",
|
||||
countryCode: "czk",
|
||||
fullName: "Czech Koruna",
|
||||
emoji: "🇨🇿",
|
||||
isoCountryCode: "CZE",
|
||||
);
|
||||
static const dkk =
|
||||
FiatCurrency(symbol: "DKK", countryCode: "dnk", fullName: "Danish Krone", emoji: "🇩🇰");
|
||||
static const egp =
|
||||
FiatCurrency(symbol: "EGP", countryCode: "egy", fullName: "Egyptian Pound", emoji: "🇪🇬");
|
||||
static const eur = FiatCurrency(
|
||||
symbol: "EUR",
|
||||
countryCode: "eur",
|
||||
fullName: "Euro",
|
||||
emoji: "🇪🇺",
|
||||
isoCountryCode: "DEU",
|
||||
);
|
||||
static const gbp =
|
||||
FiatCurrency(symbol: "GBP", countryCode: "gbr", fullName: "Pound Sterling", emoji: "🇬🇧");
|
||||
static const ghs =
|
||||
FiatCurrency(symbol: "GHS", countryCode: "gha", fullName: "Ghanaian Cedi", emoji: "🇬🇭");
|
||||
static const gtq = FiatCurrency(
|
||||
symbol: "GTQ",
|
||||
countryCode: "gtm",
|
||||
fullName: "Guatemalan Quetzal",
|
||||
emoji: "🇬🇹",
|
||||
);
|
||||
static const hkd =
|
||||
FiatCurrency(symbol: "HKD", countryCode: "hkg", fullName: "Hong Kong Dollar", emoji: "🇭🇰");
|
||||
static const hrk =
|
||||
FiatCurrency(symbol: "HRK", countryCode: "hrv", fullName: "Croatian Kuna", emoji: "🇭🇷");
|
||||
static const huf =
|
||||
FiatCurrency(symbol: "HUF", countryCode: "hun", fullName: "Hungarian Forint", emoji: "🇭🇺");
|
||||
static const idr =
|
||||
FiatCurrency(symbol: "IDR", countryCode: "idn", fullName: "Indonesian Rupiah", emoji: "🇮🇩");
|
||||
static const ils = FiatCurrency(
|
||||
symbol: "ILS",
|
||||
countryCode: "isr",
|
||||
fullName: "Israeli New Shekel",
|
||||
emoji: "🇮🇱",
|
||||
);
|
||||
static const inr =
|
||||
FiatCurrency(symbol: "INR", countryCode: "ind", fullName: "Indian Rupee", emoji: "🇮🇳");
|
||||
static const irr =
|
||||
FiatCurrency(symbol: "IRR", countryCode: "irn", fullName: "Iranian Rial", emoji: "🇮🇷");
|
||||
static const isk = FiatCurrency(
|
||||
symbol: "ISK",
|
||||
countryCode: "isl",
|
||||
fullName: "Icelandic Krona Króna",
|
||||
emoji: "🇮🇸",
|
||||
);
|
||||
static const jpy =
|
||||
FiatCurrency(symbol: "JPY", countryCode: "jpn", fullName: "Japanese Yen", emoji: "🇯🇵");
|
||||
static const krw =
|
||||
FiatCurrency(symbol: "KRW", countryCode: "kor", fullName: "South Korean Won", emoji: "🇰🇷");
|
||||
static const mad =
|
||||
FiatCurrency(symbol: "MAD", countryCode: "mar", fullName: "Moroccan Dirham", emoji: "🇲🇦");
|
||||
static const mxn =
|
||||
FiatCurrency(symbol: "MXN", countryCode: "mex", fullName: "Mexican Peso", emoji: "🇲🇽");
|
||||
static const myr =
|
||||
FiatCurrency(symbol: "MYR", countryCode: "mys", fullName: "Malaysian Ringgit", emoji: "🇲🇾");
|
||||
static const ngn =
|
||||
FiatCurrency(symbol: "NGN", countryCode: "nga", fullName: "Nigerian Naira", emoji: "🇳🇬");
|
||||
static const nok =
|
||||
FiatCurrency(symbol: "NOK", countryCode: "nor", fullName: "Norwegian Krone", emoji: "🇳🇴");
|
||||
static const nzd = FiatCurrency(
|
||||
symbol: "NZD",
|
||||
countryCode: "nzl",
|
||||
fullName: "New Zealand Dollar",
|
||||
emoji: "🇳🇿",
|
||||
);
|
||||
static const php =
|
||||
FiatCurrency(symbol: "PHP", countryCode: "phl", fullName: "Philippine Peso", emoji: "🇵🇭");
|
||||
static const pkr =
|
||||
FiatCurrency(symbol: "PKR", countryCode: "pak", fullName: "Pakistani Rupee", emoji: "🇵🇰");
|
||||
static const pln = FiatCurrency(
|
||||
symbol: "PLN",
|
||||
countryCode: "pol",
|
||||
fullName: "Poland Zloty złoty",
|
||||
emoji: "🇵🇱",
|
||||
);
|
||||
static const ron =
|
||||
FiatCurrency(symbol: "RON", countryCode: "rou", fullName: "Romanian Leu", emoji: "🇷🇴");
|
||||
static const rub =
|
||||
FiatCurrency(symbol: "RUB", countryCode: "rus", fullName: "Russian Ruble", emoji: "🇷🇺");
|
||||
static const sar =
|
||||
FiatCurrency(symbol: "SAR", countryCode: "sau", fullName: "Saudi Riyal", emoji: "🇸🇦");
|
||||
static const sek =
|
||||
FiatCurrency(symbol: "SEK", countryCode: "swe", fullName: "Swedish Krona", emoji: "🇸🇪");
|
||||
static const sgd =
|
||||
FiatCurrency(symbol: "SGD", countryCode: "sgp", fullName: "Singapore Dollar", emoji: "🇸🇬");
|
||||
static const thb = FiatCurrency(
|
||||
symbol: "THB",
|
||||
countryCode: "tha",
|
||||
fullName: "Thai Baht",
|
||||
emoji: "🇹🇭",
|
||||
);
|
||||
static const twd = FiatCurrency(
|
||||
symbol: "TWD", countryCode: "twn", fullName: "New Thaiwan Dollar", emoji: "🇹🇼");
|
||||
static const uah =
|
||||
FiatCurrency(symbol: "UAH", countryCode: "ukr", fullName: "Ukrainian Hryvnia", emoji: "🇺🇦");
|
||||
static const usd = FiatCurrency(
|
||||
symbol: "USD",
|
||||
countryCode: "usa",
|
||||
fullName: "United States Dollar",
|
||||
emoji: "🇺🇸",
|
||||
);
|
||||
static const vef = FiatCurrency(
|
||||
symbol: "VEF",
|
||||
countryCode: "ven",
|
||||
fullName: "Venezuelan Bolivar Bolívar",
|
||||
emoji: "🇻🇪",
|
||||
);
|
||||
static const vnd = FiatCurrency(
|
||||
symbol: "VND",
|
||||
countryCode: "vnm",
|
||||
fullName: "Vietnamese Dong đồng",
|
||||
emoji: "🇻🇳",
|
||||
);
|
||||
static const zar = FiatCurrency(
|
||||
symbol: "ZAR",
|
||||
countryCode: "saf",
|
||||
fullName: "South African Rand",
|
||||
emoji: "🇿🇦",
|
||||
isoCountryCode: "ZAF",
|
||||
);
|
||||
static const tur =
|
||||
FiatCurrency(symbol: "TRY", countryCode: "tur", fullName: "Turkish Lira", emoji: "🇹🇷");
|
||||
static const kes =
|
||||
FiatCurrency(symbol: "KES", countryCode: "ken", fullName: "Kenyan Shillings", emoji: "🇰🇪");
|
||||
|
||||
static final _all = {
|
||||
FiatCurrency.amd.raw: FiatCurrency.amd,
|
||||
@@ -178,6 +293,8 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
|
||||
static FiatCurrency deserialize({required String raw}) => _all[raw] ?? FiatCurrency.usd;
|
||||
|
||||
static FiatCurrency? tryDeserialize({required String raw}) => _all[raw];
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => other is FiatCurrency && other.raw == raw;
|
||||
|
||||
@@ -192,4 +309,10 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
|
||||
@override
|
||||
String get iconPath => "assets/images/flags/$countryCode.png";
|
||||
|
||||
@override
|
||||
Money parseAmount(String value) => Money.parse(value, this);
|
||||
|
||||
@override
|
||||
Money? tryParseAmount(String value) => Money.tryParse(value, this);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:bitbox_flutter/usb/bitbox_device.dart' as bitbox;
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
|
||||
import 'package:trezor_flutter/trezor_flutter.dart' as trezor;
|
||||
|
||||
abstract class HardwareWalletDevice {
|
||||
String get name;
|
||||
@@ -41,6 +42,21 @@ class BitboxHardwareWalletDevice extends HardwareWalletDevice {
|
||||
HardwareWalletConnectionType get connectionType => HardwareWalletConnectionType.usb;
|
||||
}
|
||||
|
||||
class TrezorHardwareWalletDevice extends HardwareWalletDevice {
|
||||
final trezor.TrezorDevice device;
|
||||
|
||||
TrezorHardwareWalletDevice(this.device);
|
||||
|
||||
@override
|
||||
String get name => device.name;
|
||||
|
||||
@override
|
||||
HardwareWalletDeviceType get type => device.deviceInfo.toGeneric();
|
||||
|
||||
@override
|
||||
HardwareWalletConnectionType get connectionType => device.connectionType.toGeneric();
|
||||
}
|
||||
|
||||
enum HardwareWalletDeviceType {
|
||||
ledgerBlue,
|
||||
ledgerNanoS,
|
||||
@@ -50,7 +66,12 @@ enum HardwareWalletDeviceType {
|
||||
ledgerStax,
|
||||
ledgerFlex,
|
||||
BitBox02,
|
||||
BitBox02Nova;
|
||||
BitBox02Nova,
|
||||
trezorModelOne,
|
||||
trezorModelT,
|
||||
trezorSafe3,
|
||||
trezorSafe5,
|
||||
trezorSafe7;
|
||||
}
|
||||
|
||||
enum HardwareWalletConnectionType {
|
||||
@@ -60,7 +81,7 @@ enum HardwareWalletConnectionType {
|
||||
qr;
|
||||
}
|
||||
|
||||
extension ToGenericHardwareWalletDeviceType on ledger.LedgerDeviceType {
|
||||
extension LedgerToGenericHardwareWalletDeviceType on ledger.LedgerDeviceType {
|
||||
HardwareWalletDeviceType toGeneric() {
|
||||
switch (this) {
|
||||
case ledger.LedgerDeviceType.blue:
|
||||
@@ -81,7 +102,24 @@ extension ToGenericHardwareWalletDeviceType on ledger.LedgerDeviceType {
|
||||
}
|
||||
}
|
||||
|
||||
extension ToGenericHardwareWalletConnectionType on ledger.ConnectionType {
|
||||
extension TrezorToGenericHardwareWalletDeviceType on trezor.TrezorDeviceType {
|
||||
HardwareWalletDeviceType toGeneric() {
|
||||
switch (this) {
|
||||
case trezor.TrezorDeviceType.modelOne:
|
||||
return HardwareWalletDeviceType.trezorModelOne;
|
||||
case trezor.TrezorDeviceType.modelT:
|
||||
return HardwareWalletDeviceType.trezorModelT;
|
||||
case trezor.TrezorDeviceType.safe3:
|
||||
return HardwareWalletDeviceType.trezorSafe3;
|
||||
case trezor.TrezorDeviceType.safe5:
|
||||
return HardwareWalletDeviceType.trezorSafe5;
|
||||
case trezor.TrezorDeviceType.safe7:
|
||||
return HardwareWalletDeviceType.trezorSafe7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension LedgerToGenericHardwareWalletConnectionType on ledger.ConnectionType {
|
||||
HardwareWalletConnectionType toGeneric() {
|
||||
switch (this) {
|
||||
case ledger.ConnectionType.usb:
|
||||
@@ -91,3 +129,14 @@ extension ToGenericHardwareWalletConnectionType on ledger.ConnectionType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension TrezorToGenericHardwareWalletConnectionType on trezor.ConnectionType {
|
||||
HardwareWalletConnectionType toGeneric() {
|
||||
switch (this) {
|
||||
case trezor.ConnectionType.usb:
|
||||
return HardwareWalletConnectionType.usb;
|
||||
case trezor.ConnectionType.ble:
|
||||
return HardwareWalletConnectionType.ble;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import 'package:hash_wallet/generated/locales.dart';
|
||||
import 'package:devicelocale/devicelocale.dart';
|
||||
import "package:flutter/widgets.dart";
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
// Flutter's Material delegates match on the language subtag, so "pt_BR" must be split into two
|
||||
Locale localeFromLanguageCode(String code) {
|
||||
final parts = code.split("_");
|
||||
|
||||
return parts.length == 2 ? Locale(parts[0], parts[1]) : Locale(code);
|
||||
}
|
||||
|
||||
class LanguageService {
|
||||
static const Map<String, String> supportedLocales = {
|
||||
'en': 'English',
|
||||
@@ -20,7 +28,6 @@ class LanguageService {
|
||||
'ru': 'Русский (Russian)',
|
||||
'uk': 'Українська (Ukrainian)',
|
||||
'zh': '简体中文 (Chinese Simplified)',
|
||||
'zh_TW': '繁體中文 (Chinese Traditional)',
|
||||
'hr': 'Hrvatski (Croatian)',
|
||||
'it': 'Italiano (Italian)',
|
||||
'th': 'ภาษาไทย (Thai)',
|
||||
@@ -55,7 +62,6 @@ class LanguageService {
|
||||
'ru': 'rus',
|
||||
'uk': 'ukr',
|
||||
'zh': 'chn',
|
||||
'zh_TW': 'twn',
|
||||
'hr': 'hrv',
|
||||
'it': 'ita',
|
||||
'th': 'tha',
|
||||
@@ -87,12 +93,16 @@ class LanguageService {
|
||||
|
||||
static Future<String> localeDetection() async {
|
||||
try {
|
||||
var locale = await Devicelocale.currentLocale ?? '';
|
||||
locale = Intl.shortLocale(locale);
|
||||
|
||||
final locale = Intl.canonicalizedLocale(await Devicelocale.currentLocale ?? "");
|
||||
if (list.keys.contains(locale)) {
|
||||
return locale;
|
||||
}
|
||||
|
||||
final language = Intl.shortLocale(locale);
|
||||
if (list.keys.contains(language)) {
|
||||
return language;
|
||||
}
|
||||
|
||||
return LanguageService.defaultLocale;
|
||||
} catch (_) {
|
||||
return LanguageService.defaultLocale;
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/lnurl.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
|
||||
class LNUrlPayRecord {
|
||||
LNUrlPayRecord({
|
||||
required this.address,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String address;
|
||||
|
||||
static Future<String?> checkWellKnownUsername(String username, CryptoCurrency currency,
|
||||
{bool allowRethrow = false}) async {
|
||||
if (![CryptoCurrency.btc, CryptoCurrency.btcln].contains(currency)) return null;
|
||||
|
||||
// split the string by the @ symbol:
|
||||
try {
|
||||
final List<String> splitStrs = username.split("@");
|
||||
String name = splitStrs.first.toLowerCase();
|
||||
final String domain = splitStrs.last;
|
||||
|
||||
if (splitStrs.length == 3) {
|
||||
// for username like @alice@domain.org instead of alice@domain.org
|
||||
name = splitStrs[1];
|
||||
}
|
||||
|
||||
if (name.isEmpty) {
|
||||
name = "_";
|
||||
}
|
||||
|
||||
final expectedUrl = "https://$domain/.well-known/lnurlp/$name";
|
||||
final response = await ProxyWrapper().get(
|
||||
clearnetUri: Uri.parse(expectedUrl),
|
||||
headers: <String, String>{"Accept": "application/json"},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return LNURL.encode(expectedUrl);
|
||||
}
|
||||
} catch (e) {
|
||||
if (allowRethrow) rethrow;
|
||||
printV("error checking well-known username: $e");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String formatDomainName(String name) {
|
||||
String formattedName = name;
|
||||
|
||||
if (name.contains("@")) {
|
||||
formattedName = name.replaceAll("@", ".");
|
||||
}
|
||||
|
||||
return formattedName;
|
||||
}
|
||||
|
||||
static Future<LNUrlPayRecord?> fetchAddressAndName({
|
||||
required String formattedName,
|
||||
required CryptoCurrency currency,
|
||||
}) async {
|
||||
final name = formattedName;
|
||||
|
||||
printV("formattedName: $formattedName");
|
||||
|
||||
final address = await checkWellKnownUsername(formattedName, currency);
|
||||
|
||||
if (address == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LNUrlPayRecord(address: address, name: name);
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class MainActions {
|
||||
canShow: (viewModel) => viewModel.hasTradeAction,
|
||||
onTap: (BuildContext context, DashboardViewModel viewModel) async {
|
||||
if (!viewModel.isEnabledTradeAction) return;
|
||||
await Navigator.of(context).pushNamed(Routes.buySellPage, arguments: false);
|
||||
await Navigator.of(context).pushNamed(Routes.buySellPage);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hash_wallet/utils/feature_flag.dart';
|
||||
import "package:hash_wallet/generated/i18n.dart";
|
||||
import "package:hash_wallet/view_model/dashboard/dashboard_view_model.dart";
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
class NewMainActions {
|
||||
final Key key;
|
||||
final String Function(BuildContext context) name;
|
||||
final String image;
|
||||
final VoidCallback onTap;
|
||||
final bool Function(DashboardViewModel viewModel)? isEnabled;
|
||||
final bool Function(DashboardViewModel viewModel)? canShow;
|
||||
|
||||
NewMainActions._({
|
||||
required this.key,
|
||||
@@ -18,48 +13,54 @@ class NewMainActions {
|
||||
this.isEnabled,
|
||||
this.canShow,
|
||||
});
|
||||
final Key key;
|
||||
final String Function(BuildContext context) name;
|
||||
final String image;
|
||||
final VoidCallback onTap;
|
||||
final bool Function(DashboardViewModel viewModel)? isEnabled;
|
||||
final bool Function(DashboardViewModel viewModel)? canShow;
|
||||
|
||||
static List<NewMainActions> all = [
|
||||
homeAction,
|
||||
walletsAction,
|
||||
contactsAction,
|
||||
appsAction,
|
||||
//chartsAction,
|
||||
if (FeatureFlag.isChartsEnabled) chartsAction,
|
||||
];
|
||||
|
||||
static NewMainActions homeAction = NewMainActions._(
|
||||
name: (context) => S.of(context).home,
|
||||
image: 'assets/new-ui/navbar/home.svg',
|
||||
key: ValueKey('dashboard_page_home_action_button_key'),
|
||||
image: "assets/new-ui/navbar/home.svg",
|
||||
key: const ValueKey("dashboard_page_home_action_button_key"),
|
||||
onTap: () {},
|
||||
);
|
||||
|
||||
static NewMainActions walletsAction = NewMainActions._(
|
||||
name: (context) => S.of(context).wallets,
|
||||
image: 'assets/new-ui/navbar/wallets.svg',
|
||||
key: ValueKey('dashboard_page_wallets_action_button_key'),
|
||||
image: "assets/new-ui/navbar/wallets.svg",
|
||||
key: const ValueKey("dashboard_page_wallets_action_button_key"),
|
||||
onTap: () {},
|
||||
);
|
||||
|
||||
static NewMainActions contactsAction = NewMainActions._(
|
||||
name: (context) => S.of(context).contacts,
|
||||
image: 'assets/new-ui/navbar/contacts.svg',
|
||||
key: ValueKey('dashboard_page_contacts_action_button_key'),
|
||||
image: "assets/new-ui/navbar/contacts.svg",
|
||||
key: const ValueKey("dashboard_page_contacts_action_button_key"),
|
||||
onTap: () {},
|
||||
);
|
||||
|
||||
static NewMainActions appsAction = NewMainActions._(
|
||||
name: (context) => S.of(context).apps,
|
||||
image: 'assets/new-ui/navbar/apps.svg',
|
||||
key: ValueKey('dashboard_page_apps_action_button_key'),
|
||||
image: "assets/new-ui/navbar/apps.svg",
|
||||
key: const ValueKey("dashboard_page_apps_action_button_key"),
|
||||
canShow: (dashboardVM) => dashboardVM.showApps,
|
||||
onTap: () {},
|
||||
);
|
||||
|
||||
// static NewMainActions chartsAction = NewMainActions._(
|
||||
// name: (context) => 'Charts', //TODO S.of(context).charts,
|
||||
// image: 'assets/new-ui/navbar/charts.svg',
|
||||
// key: ValueKey('dashboard_page_charts_action_button_key'),
|
||||
// onTap: () {},
|
||||
// );
|
||||
static NewMainActions chartsAction = NewMainActions._(
|
||||
name: (context) => S.of(context).charts,
|
||||
image: "assets/new-ui/navbar/charts.svg",
|
||||
key: const ValueKey("dashboard_page_charts_action_button_key"),
|
||||
onTap: () {},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ class ListItemCheckbox extends ListItem {
|
||||
required super.keyValue,
|
||||
required super.label,
|
||||
this.subtitle,
|
||||
this.subtitleColor,
|
||||
this.iconPath,
|
||||
this.onTap,
|
||||
this.showArrow = false,
|
||||
@@ -18,5 +19,6 @@ class ListItemCheckbox extends ListItem {
|
||||
final bool value;
|
||||
final String? subtitle;
|
||||
final String? iconPath;
|
||||
final Color? subtitleColor;
|
||||
final bool showArrow;
|
||||
}
|
||||
|
||||
@@ -2,32 +2,47 @@ import 'package:hash_wallet/entities/new_ui_entities/list_item/list_item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ListItemRegularRow extends ListItem {
|
||||
const ListItemRegularRow(
|
||||
{required super.keyValue,
|
||||
required super.label,
|
||||
this.subtitle,
|
||||
this.trailingText,
|
||||
this.iconPath,
|
||||
this.onTap,
|
||||
this.trailingIconPath,
|
||||
this.showArrow = true,
|
||||
this.bottomWidget,
|
||||
this.trailingWidget,
|
||||
this.truncateTrailingText = false,
|
||||
this.foregroundColor,
|
||||
this.trailingIconSize,
|
||||
this.copyableText});
|
||||
const ListItemRegularRow({
|
||||
required super.keyValue,
|
||||
required super.label,
|
||||
this.subtitle,
|
||||
this.trailingText,
|
||||
this.iconPath,
|
||||
this.badgeIconPath,
|
||||
this.onTap,
|
||||
this.trailingIconPath,
|
||||
this.showArrow = true,
|
||||
this.bottomWidget,
|
||||
this.trailingWidget,
|
||||
this.truncateTrailingText = false,
|
||||
this.foregroundColor,
|
||||
this.trailingIconSize,
|
||||
this.copyableText,
|
||||
this.leadingIconErrorWidget,
|
||||
this.leadingIconSize,
|
||||
this.badgeIconSize,
|
||||
this.iconColor,
|
||||
this.secondaryLabel,
|
||||
this.subtitleColor,
|
||||
});
|
||||
|
||||
final String? subtitle;
|
||||
final String? trailingText;
|
||||
final String? iconPath;
|
||||
final String? trailingIconPath;
|
||||
final String? badgeIconPath;
|
||||
final String? copyableText;
|
||||
final VoidCallback? onTap;
|
||||
final String? secondaryLabel;
|
||||
final bool showArrow;
|
||||
final Widget? bottomWidget;
|
||||
final Widget? trailingWidget;
|
||||
final bool truncateTrailingText;
|
||||
final Color? subtitleColor;
|
||||
final Color? foregroundColor;
|
||||
final double? trailingIconSize;
|
||||
final Widget? leadingIconErrorWidget;
|
||||
final double? leadingIconSize;
|
||||
final double? badgeIconSize;
|
||||
final Color? iconColor;
|
||||
}
|
||||
|
||||
@@ -5,12 +5,16 @@ class ListItemToggle extends ListItem {
|
||||
const ListItemToggle({
|
||||
required super.keyValue,
|
||||
required super.label,
|
||||
this.subtitle,
|
||||
this.iconPath,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
this.leadingEndWidget,
|
||||
});
|
||||
|
||||
final String? subtitle;
|
||||
final String? iconPath;
|
||||
final ValueChanged<bool> onChanged;
|
||||
final Widget? leadingEndWidget;
|
||||
final bool value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import 'package:hash_wallet/core/utilities.dart';
|
||||
import 'package:hash_wallet/entities/preferences_key.dart';
|
||||
import 'package:hash_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/node_list.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const Map<WalletType, String> nodePreferenceKeys = {
|
||||
WalletType.monero: PreferencesKey.currentNodeIdKey,
|
||||
WalletType.bitcoin: PreferencesKey.currentBitcoinElectrumSererIdKey,
|
||||
WalletType.litecoin: PreferencesKey.currentLitecoinElectrumSererIdKey,
|
||||
WalletType.ethereum: PreferencesKey.currentEthereumNodeIdKey,
|
||||
WalletType.polygon: PreferencesKey.currentPolygonNodeIdKey,
|
||||
WalletType.base: PreferencesKey.currentBaseNodeIdKey,
|
||||
WalletType.arbitrum: PreferencesKey.currentArbitrumNodeIdKey,
|
||||
WalletType.bsc: PreferencesKey.currentBscNodeIdKey,
|
||||
WalletType.nano: PreferencesKey.currentNanoNodeIdKey,
|
||||
WalletType.bitcoinCash: PreferencesKey.currentBitcoinCashNodeIdKey,
|
||||
WalletType.dogecoin: PreferencesKey.currentDogecoinNodeIdKey,
|
||||
WalletType.wownero: PreferencesKey.currentWowneroNodeIdKey,
|
||||
};
|
||||
|
||||
const Map<WalletType, String> powNodePreferenceKeys = {
|
||||
WalletType.nano: PreferencesKey.currentNanoPowNodeIdKey
|
||||
};
|
||||
|
||||
Future<void> checkNodeForWalletType(SharedPreferences sharedPreferences,
|
||||
SettingsStore settingsStore, WalletType type, bool isPow) async {
|
||||
final preferenceKey = isPow ? powNodePreferenceKeys[type] : nodePreferenceKeys[type];
|
||||
|
||||
if (preferenceKey == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final currentId = await sharedPreferences.getInt(preferenceKey);
|
||||
|
||||
final nodes =
|
||||
isPow ? await Node.getAllForWalletTypePow(type) : await Node.getAllForWalletType(type);
|
||||
final currentNode = nodes.firstWhereOrNull((item) => item.id == currentId);
|
||||
|
||||
if (currentNode == null) {
|
||||
final newNode = (isPow
|
||||
? await Node.getDefaultPowForWalletType(type)
|
||||
: await Node.getDefaultForWalletType(type)) ??
|
||||
await getDefaultNodeFromFiles(type, isPow: isPow);
|
||||
final newId = await newNode.save();
|
||||
sharedPreferences.setInt(preferenceKey, newId);
|
||||
settingsStore.nodes[type] = newNode;
|
||||
} else {
|
||||
settingsStore.nodes[type] = currentNode;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkCurrentNodes(
|
||||
SharedPreferences sharedPreferences, SettingsStore settingsStore) async {
|
||||
for (final walletType in nodePreferenceKeys.keys) {
|
||||
await checkNodeForWalletType(sharedPreferences, settingsStore, walletType, false);
|
||||
}
|
||||
|
||||
for (final walletType in powNodePreferenceKeys.keys) {
|
||||
await checkNodeForWalletType(sharedPreferences, settingsStore, walletType, true);
|
||||
}
|
||||
}
|
||||
@@ -1,120 +1 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import "package:yaml/yaml.dart";
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
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;
|
||||
// TODO: (refactoring) each wallet would have its path, so `wallet.nodePath` would be decided based on chain id in Evm wallet
|
||||
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.wownero:
|
||||
path = 'assets/wownero_node_list.yml';
|
||||
break;
|
||||
case WalletType.dogecoin:
|
||||
path = 'assets/dogecoin_electrum_server_list.yml';
|
||||
break;
|
||||
case WalletType.base:
|
||||
path = 'assets/base_node_list.yml';
|
||||
break;
|
||||
case WalletType.arbitrum:
|
||||
path = 'assets/arbitrum_node_list.yml';
|
||||
break;
|
||||
case WalletType.bsc:
|
||||
path = 'assets/bsc_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 = type;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultNanoPowNodes() async {
|
||||
final powNodesRaw = await rootBundle.loadString('assets/nano_pow_node_list.yml');
|
||||
final loadedPowNodes = loadYaml(powNodesRaw) as YamlList;
|
||||
final nodes = <Node>[];
|
||||
|
||||
for (final raw in loadedPowNodes) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||
node.type = WalletType.nano;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
final wowneroNodes = await loadDefaultNodes(WalletType.wownero);
|
||||
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 ethereumNodes = await loadDefaultNodes(WalletType.ethereum);
|
||||
final nanoNodes = await loadDefaultNodes(WalletType.nano);
|
||||
final polygonNodes = await loadDefaultNodes(WalletType.polygon);
|
||||
final dogecoinElectrumServerList = await loadDefaultNodes(WalletType.dogecoin);
|
||||
final baseNodes = await loadDefaultNodes(WalletType.base);
|
||||
final arbitrumNodes = await loadDefaultNodes(WalletType.arbitrum);
|
||||
final bscNodes = await loadDefaultNodes(WalletType.bsc);
|
||||
|
||||
final nodes = wowneroNodes +
|
||||
moneroNodes +
|
||||
bitcoinElectrumServerList +
|
||||
litecoinElectrumServerList +
|
||||
ethereumNodes +
|
||||
bitcoinCashElectrumServerList +
|
||||
nanoNodes +
|
||||
polygonNodes +
|
||||
dogecoinElectrumServerList +
|
||||
baseNodes +
|
||||
arbitrumNodes +
|
||||
bscNodes;
|
||||
|
||||
await nodeSource.clear();
|
||||
await nodeSource.addAll(nodes);
|
||||
}
|
||||
|
||||
Future<void> resetPowToDefault(Box<Node> powNodeSource) async {
|
||||
final nanoPowNodes = await loadDefaultNanoPowNodes();
|
||||
final nodes = nanoPowNodes;
|
||||
await powNodeSource.clear();
|
||||
await powNodeSource.addAll(nodes);
|
||||
}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import 'package:basic_utils/basic_utils.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
|
||||
class OpenaliasRecord {
|
||||
OpenaliasRecord({
|
||||
required this.address,
|
||||
required this.name,
|
||||
required this.description,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String address;
|
||||
final String description;
|
||||
|
||||
static String formatDomainName(String name) {
|
||||
String formattedName = name;
|
||||
|
||||
if (name.contains("@")) {
|
||||
formattedName = name.replaceAll("@", ".");
|
||||
}
|
||||
|
||||
return formattedName;
|
||||
}
|
||||
|
||||
static Future<List<RRecord>?> lookupOpenAliasRecord(String name) async {
|
||||
try {
|
||||
final txtRecord = await DnsUtils.lookupRecord(name, RRecordType.TXT, dnssec: true);
|
||||
|
||||
return txtRecord;
|
||||
} catch (e) {
|
||||
printV("${e.toString()}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static OpenaliasRecord fetchAddressAndName({
|
||||
required String formattedName,
|
||||
required String ticker,
|
||||
required List<RRecord> txtRecord,
|
||||
}) {
|
||||
String address = '';
|
||||
String name = formattedName;
|
||||
String note = '';
|
||||
|
||||
for (RRecord element in txtRecord) {
|
||||
String record = element.data;
|
||||
|
||||
if (record.contains("oa1:$ticker") && record.contains("recipient_address")) {
|
||||
record = record.replaceAll('\"', "");
|
||||
|
||||
final dataList = record.split(";");
|
||||
|
||||
address = dataList
|
||||
.where((item) => (item.contains("recipient_address")))
|
||||
.toString()
|
||||
.replaceAll("oa1:$ticker recipient_address=", "")
|
||||
.replaceAll("(", "")
|
||||
.replaceAll(")", "")
|
||||
.trim();
|
||||
|
||||
final recipientName = dataList
|
||||
.where((item) => (item.contains("recipient_name")))
|
||||
.toString()
|
||||
.replaceAll("(", "")
|
||||
.replaceAll(")", "")
|
||||
.trim();
|
||||
|
||||
if (recipientName.isNotEmpty) {
|
||||
name = recipientName.replaceAll("recipient_name=", "");
|
||||
}
|
||||
|
||||
final description = dataList
|
||||
.where((item) => (item.contains("tx_description")))
|
||||
.toString()
|
||||
.replaceAll("(", "")
|
||||
.replaceAll(")", "")
|
||||
.trim();
|
||||
|
||||
if (description.isNotEmpty) {
|
||||
note = description.replaceAll("tx_description=", "");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return OpenaliasRecord(address: address, name: name, description: note);
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
import 'package:hash_wallet/core/address_validator.dart';
|
||||
import 'package:hash_wallet/entities/ens_record.dart';
|
||||
import 'package:hash_wallet/entities/lnurlpay_record.dart';
|
||||
import 'package:hash_wallet/entities/openalias_record.dart';
|
||||
import 'package:hash_wallet/entities/parsed_address.dart';
|
||||
import 'package:hash_wallet/entities/wellknown_record.dart';
|
||||
import 'package:hash_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import 'bip_353_record.dart';
|
||||
|
||||
/// Resolves human-readable names typed into the address field.
|
||||
///
|
||||
/// Privacy contract: every lookup here either queries DNS (OpenAlias,
|
||||
/// BIP-353), the domain the user explicitly typed (.well-known, LNURL-pay),
|
||||
/// or the wallet's own configured RPC node (ENS). Resolvers that shipped the
|
||||
/// typed input to unrelated third-party services (Twitter, Mastodon, Nostr,
|
||||
/// Yat, Unstoppable Domains, FIO, ThorChain names, zcash.me, Zano aliases)
|
||||
/// were removed deliberately — do not reintroduce one without updating
|
||||
/// PRIVACY.md and gating it behind an off-by-default setting.
|
||||
class AddressResolver {
|
||||
AddressResolver({required this.wallet, required this.settingsStore}) : walletType = wallet.type;
|
||||
|
||||
final WalletType walletType;
|
||||
final WalletBase wallet;
|
||||
final SettingsStore settingsStore;
|
||||
|
||||
static String? extractAddressByType(
|
||||
{required String raw,
|
||||
required CryptoCurrency type,
|
||||
bool requireSurroundingWhitespaces = true}) {
|
||||
var addressPattern = AddressValidator.getAddressFromStringPattern(type);
|
||||
|
||||
if (addressPattern == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (requireSurroundingWhitespaces) addressPattern = "$BEFORE_REGEX$addressPattern$AFTER_REGEX";
|
||||
|
||||
final match = RegExp(addressPattern, multiLine: true).firstMatch(raw);
|
||||
return match?.group(0)?.replaceAllMapped(RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_|ban_'),
|
||||
(Match match) {
|
||||
String group = match.group(0)!;
|
||||
if (group.startsWith('bitcoincash:') ||
|
||||
group.startsWith('nano_') ||
|
||||
group.startsWith('ban_')) {
|
||||
return group;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
}
|
||||
|
||||
bool isEmailFormat(String address) {
|
||||
final RegExp emailRegex = RegExp(
|
||||
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
|
||||
caseSensitive: false,
|
||||
);
|
||||
return emailRegex.hasMatch(address);
|
||||
}
|
||||
|
||||
Future<ParsedAddress> resolve(BuildContext context, String text, CryptoCurrency currency) async {
|
||||
final ticker = currency.title;
|
||||
try {
|
||||
// user@domain identifiers: resolved against the domain the user typed.
|
||||
if (text.contains('.') && text.contains('@')) {
|
||||
if (settingsStore.lookupsWellKnown) {
|
||||
final record =
|
||||
await WellKnownRecord.fetchAddressAndName(formattedName: text, currency: currency);
|
||||
if (record != null) {
|
||||
return ParsedAddress.fetchWellKnownAddress(address: record.address, name: text);
|
||||
}
|
||||
}
|
||||
|
||||
if (walletType == WalletType.bitcoin) {
|
||||
final record =
|
||||
await LNUrlPayRecord.fetchAddressAndName(formattedName: text, currency: currency);
|
||||
if (record != null) {
|
||||
return ParsedAddress.fetchLNUrlPayAddress(address: record.address, name: text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final formattedName = OpenaliasRecord.formatDomainName(text);
|
||||
final domainParts = formattedName.split('.');
|
||||
final name = domainParts.last;
|
||||
|
||||
if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) {
|
||||
return ParsedAddress(addresses: [text]);
|
||||
}
|
||||
|
||||
final bip353AddressMap = await Bip353Record.fetchUriByCryptoCurrency(text, ticker);
|
||||
|
||||
if (bip353AddressMap != null && bip353AddressMap.isNotEmpty) {
|
||||
final chosenAddress =
|
||||
await Bip353Record.pickBip353AddressChoice(context, text, bip353AddressMap);
|
||||
if (chosenAddress != null) {
|
||||
try {
|
||||
final dnsProof = await Bip353Record.fetchDnsProof(text);
|
||||
return ParsedAddress.fetchBip353AddressAddress(
|
||||
address: chosenAddress, name: text, dnsProof: dnsProof);
|
||||
} catch (e) {
|
||||
printV('Bip353Record.fetchBip353AddressAddress error: $e');
|
||||
return ParsedAddress.fetchBip353AddressAddress(address: chosenAddress, name: text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (text.endsWith(".eth")) {
|
||||
if (settingsStore.lookupsENS) {
|
||||
final address = await EnsRecord.fetchEnsAddress(text, wallet: wallet);
|
||||
if (address.isNotEmpty && address != "0x0000000000000000000000000000000000000000") {
|
||||
return ParsedAddress.fetchEnsAddress(name: text, address: address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (formattedName.contains(".")) {
|
||||
if (settingsStore.lookupsOpenAlias) {
|
||||
final txtRecord = await OpenaliasRecord.lookupOpenAliasRecord(formattedName);
|
||||
|
||||
if (txtRecord != null) {
|
||||
final record = await OpenaliasRecord.fetchAddressAndName(
|
||||
formattedName: formattedName, ticker: ticker.toLowerCase(), txtRecord: txtRecord);
|
||||
return ParsedAddress.fetchOpenAliasAddress(record: record, name: text);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
printV(e.toString());
|
||||
}
|
||||
|
||||
return ParsedAddress(addresses: [text]);
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
import 'package:hash_wallet/entities/openalias_record.dart';
|
||||
import 'package:hash_wallet/entities/yat_record.dart';
|
||||
|
||||
enum ParseFrom {
|
||||
unstoppableDomains,
|
||||
openAlias,
|
||||
yatRecord,
|
||||
fio,
|
||||
notParsed,
|
||||
twitter,
|
||||
ens,
|
||||
contact,
|
||||
mastodon,
|
||||
nostr,
|
||||
thorChain,
|
||||
wellKnown,
|
||||
zanoAlias,
|
||||
zcashAddress,
|
||||
bip353,
|
||||
lnurlpay,
|
||||
}
|
||||
|
||||
class ParsedAddress {
|
||||
ParsedAddress({
|
||||
required this.addresses,
|
||||
this.name = '',
|
||||
this.description = '',
|
||||
this.profileImageUrl = '',
|
||||
this.profileName = '',
|
||||
this.parseFrom = ParseFrom.notParsed,
|
||||
this.bip353DnsProof,
|
||||
});
|
||||
|
||||
factory ParsedAddress.fetchEmojiAddress({
|
||||
List<YatRecord>? addresses,
|
||||
required String name,
|
||||
}) {
|
||||
if (addresses?.isEmpty ?? true) {
|
||||
return ParsedAddress(addresses: [name], parseFrom: ParseFrom.yatRecord);
|
||||
}
|
||||
return ParsedAddress(
|
||||
addresses: addresses!.map((e) => e.address).toList(),
|
||||
name: name,
|
||||
parseFrom: ParseFrom.yatRecord,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchUnstoppableDomainAddress({
|
||||
String? address,
|
||||
required String name,
|
||||
}) {
|
||||
if (address?.isEmpty ?? true) {
|
||||
return ParsedAddress(addresses: [name]);
|
||||
}
|
||||
return ParsedAddress(
|
||||
addresses: [address!],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.unstoppableDomains,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchBip353AddressAddress({
|
||||
required String address,
|
||||
required String name,
|
||||
String? dnsProof,
|
||||
}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.bip353,
|
||||
bip353DnsProof: dnsProof,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchOpenAliasAddress(
|
||||
{required OpenaliasRecord record, required String name}) {
|
||||
if (record.address.isEmpty) {
|
||||
return ParsedAddress(addresses: [name]);
|
||||
}
|
||||
return ParsedAddress(
|
||||
addresses: [record.address],
|
||||
name: record.name,
|
||||
description: record.description,
|
||||
parseFrom: ParseFrom.openAlias,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchFioAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.fio,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchTwitterAddress(
|
||||
{required String address,
|
||||
required String name,
|
||||
required String profileImageUrl,
|
||||
required String profileName,
|
||||
String? description}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
description: description ?? '',
|
||||
profileImageUrl: profileImageUrl,
|
||||
profileName: profileName,
|
||||
parseFrom: ParseFrom.twitter,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchMastodonAddress(
|
||||
{required String address,
|
||||
required String name,
|
||||
required String profileImageUrl,
|
||||
required String profileName}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.mastodon,
|
||||
profileImageUrl: profileImageUrl,
|
||||
profileName: profileName,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchContactAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.contact,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchEnsAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.ens,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.nostrAddress(
|
||||
{required String address,
|
||||
required String name,
|
||||
required String profileImageUrl,
|
||||
required String profileName}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.nostr,
|
||||
profileImageUrl: profileImageUrl,
|
||||
profileName: profileName,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.thorChainAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.thorChain,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.zanoAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.zanoAlias,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.zcashAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.zcashAddress,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchWellKnownAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.wellKnown,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchLNUrlPayAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.lnurlpay,
|
||||
);
|
||||
}
|
||||
|
||||
final List<String> addresses;
|
||||
final String name;
|
||||
final String description;
|
||||
final String profileImageUrl;
|
||||
final String profileName;
|
||||
final ParseFrom parseFrom;
|
||||
final String? bip353DnsProof;
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
|
||||
class PreferencesKey {
|
||||
static const currentWalletType = 'current_wallet_type';
|
||||
static const currentWalletName = 'current_wallet_name';
|
||||
static const accountsHomePromoDismissed = 'accounts_home_promo_dismissed';
|
||||
static String educationDismissed(String educationId) => "education_dismissed_$educationId";
|
||||
static const currentNodeIdKey = 'current_node_id';
|
||||
static const currentBitcoinElectrumSererIdKey = 'current_node_id_btc';
|
||||
static const currentLitecoinElectrumSererIdKey = 'current_node_id_ltc';
|
||||
@@ -11,6 +15,7 @@ class PreferencesKey {
|
||||
static const currentBaseNodeIdKey = 'current_node_id_base';
|
||||
static const currentArbitrumNodeIdKey = 'current_node_id_arbitrum';
|
||||
static const currentBscNodeIdKey = 'current_node_id_bsc';
|
||||
static const currentRobinhoodNodeIdKey = "current_node_id_robinhood";
|
||||
static const currentNanoNodeIdKey = 'current_node_id_nano';
|
||||
static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
|
||||
static const currentDecredNodeIdKey = 'current_node_id_decred';
|
||||
@@ -63,7 +68,8 @@ class PreferencesKey {
|
||||
static const zanoTransactionPriority = 'current_fee_priority_zano';
|
||||
static const wowneroTransactionPriority = 'current_fee_priority_wownero';
|
||||
static const decredTransactionPriority = 'current_fee_priority_decred';
|
||||
static const zcashTransactionPriority = 'current_fee_priority_decred';
|
||||
static const zcashTransactionPriority = 'current_fee_priority_zcash';
|
||||
static const dogecoinTransactionPriority = 'current_fee_priority_dogecoin';
|
||||
static const customBitcoinFeeRate = 'custom_electrum_fee_rate';
|
||||
static const silentPaymentsCardDisplay = 'silentPaymentsCardDisplay';
|
||||
static const mwebCardDisplay = 'mwebCardDisplay';
|
||||
@@ -89,6 +95,7 @@ class PreferencesKey {
|
||||
static const useBaseScan = 'use_base_scan';
|
||||
static const useArbiScan = 'use_arbitrum_scan';
|
||||
static const useBscScan = 'use_bscscan';
|
||||
static const useRobinhoodScan = "use_robinhood_scan";
|
||||
static const useTronGrid = 'use_trongrid';
|
||||
static const useMempoolFeeAPI = 'use_mempool_fee_api';
|
||||
static const evmHiddenChainIds = 'evm_hidden_chain_ids';
|
||||
@@ -101,7 +108,14 @@ class PreferencesKey {
|
||||
static const lookupsUnstoppableDomains = 'looks_up_unstoppable_domain';
|
||||
static const lookupsOpenAlias = 'looks_up_open_alias';
|
||||
static const lookupsENS = 'looks_up_ens';
|
||||
static const lookupsZcashNames = 'looks_up_zcash_names';
|
||||
static const lookupsZcashAddress = 'looks_up_zcash_address';
|
||||
static const lookupsWellKnown = 'looks_up_well_known';
|
||||
static const lookupsFio = 'looks_up_fio';
|
||||
static const lookupsNostr = 'looks_up_nostr';
|
||||
static const lookupsThorChain = 'looks_up_thor_chain';
|
||||
static const lookupsBip353 = 'looks_up_bip353';
|
||||
static const lookupsLNUrl = 'looks_up_lnurl';
|
||||
static const useBlinkProtection = 'use_blink_protection';
|
||||
static const usePayjoin = 'use_payjoin';
|
||||
static const showPayjoinCard = 'show_payjoin_card';
|
||||
@@ -109,7 +123,6 @@ class PreferencesKey {
|
||||
static const showDecredInfoCard = 'show_decred_info_card';
|
||||
static const forceDecentralizedExchanges = 'force_decentralized_exchanges';
|
||||
static const decentralizedExchangesPromptDismissed = 'decentralized_exchanges_prompt_dismissed';
|
||||
|
||||
static String moneroWalletUpdateV1Key(String name) =>
|
||||
'${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}';
|
||||
|
||||
@@ -138,4 +151,8 @@ class PreferencesKey {
|
||||
static const backgroundImage = 'background_image';
|
||||
static const mwebAdDismissed = "mweb_ad_dismissed";
|
||||
static const balanceHideCounter = "balance_hide_counter";
|
||||
static const zcashMigrationModalViewed = "zcash_migration_modal_viewed";
|
||||
static const showCiBuildOverlay = "show_ci_build_overlay";
|
||||
static String deprecationPopupViewed(WalletType type) =>
|
||||
"deprecation_popup_viewed_${walletTypeToString(type)}";
|
||||
}
|
||||
|
||||
@@ -1,26 +1,40 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/new-ui/pages/scan_page.dart';
|
||||
import 'package:hash_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:hash_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:fast_scanner/fast_scanner.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:ur/ur_decoder.dart';
|
||||
|
||||
var isQrScannerShown = false;
|
||||
|
||||
Future<String?> presentQRScanner(BuildContext context) async {
|
||||
Future<String?> presentQRScanner(BuildContext context,
|
||||
{bool showHelp = false, bool showManualInput = true, bool useModal = false}) async {
|
||||
isQrScannerShown = true;
|
||||
try {
|
||||
final result = await Navigator.of(context).push<String>(
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return BarcodeScannerSimple();
|
||||
},
|
||||
),
|
||||
);
|
||||
final result = useModal
|
||||
? await CupertinoScaffold.showCupertinoModalBottomSheet<String?>(
|
||||
context: context,
|
||||
builder: (context) => ScanPage(
|
||||
showHelp: showHelp,
|
||||
showManualInput: showManualInput,
|
||||
))
|
||||
: await Navigator.of(context).push<String>(
|
||||
CupertinoPageRoute(
|
||||
builder: (context) {
|
||||
return ScanPage(
|
||||
showHelp: showHelp,
|
||||
showManualInput: showManualInput,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
isQrScannerShown = false;
|
||||
return result;
|
||||
} catch (e) {
|
||||
@@ -55,7 +69,7 @@ class _BarcodeScannerSimpleState extends State<BarcodeScannerSimple> {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: S.of(context).error_dialog_content,
|
||||
buttonText: 'ok',
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import "package:hash_wallet/entities/solana_nft_asset_model.dart";
|
||||
import "package:hash_wallet/entities/wallet_nft_response.dart";
|
||||
import "package:cw_core/imported_nft.dart";
|
||||
|
||||
class SavedNFTs {
|
||||
Future<Map<String, SolanaNFTAssetModel>> solanaNFTs(String walletName) async {
|
||||
final rows = await ImportedNFT.getAllForWallet(walletName, ImportedNFT.solanaChain);
|
||||
|
||||
return {
|
||||
for (final row in rows)
|
||||
row.identifier: SolanaNFTAssetModel(
|
||||
address: row.identifier,
|
||||
mint: row.identifier,
|
||||
name: row.name,
|
||||
symbol: row.symbol,
|
||||
description: row.description,
|
||||
imageOriginalUrl: row.imageUrl,
|
||||
isOwned: row.isOwned,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
Future<void> addSolana(String walletName, SolanaNFTAssetModel asset) async {
|
||||
final mint = asset.mint;
|
||||
|
||||
if (mint == null || mint.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
await _rowForSolana(walletName, mint, asset).save();
|
||||
}
|
||||
|
||||
Future<void> refreshSolana(String walletName, List<SolanaNFTAssetModel> assets) async {
|
||||
for (final asset in assets) {
|
||||
final mint = asset.mint;
|
||||
|
||||
if (mint == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await ImportedNFT.updateMetadata(_rowForSolana(walletName, mint, asset));
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<NFTAssetModel>> evmNFTs(String walletName, String chain) async {
|
||||
final rows = await ImportedNFT.getAllForWallet(walletName, chain);
|
||||
|
||||
return rows
|
||||
.map(
|
||||
(row) => NFTAssetModel(
|
||||
tokenAddress: _tokenAddressOf(row.identifier),
|
||||
tokenId: _tokenIdOf(row.identifier),
|
||||
name: row.name,
|
||||
symbol: row.symbol,
|
||||
normalizedMetadata: NormalizedMetadata(
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
image: row.imageUrl,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<void> addEvm(String walletName, String chain, NFTAssetModel asset) async {
|
||||
final identifier = evmIdentifier(asset);
|
||||
|
||||
if (identifier == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await ImportedNFT(
|
||||
walletName: walletName,
|
||||
chain: chain,
|
||||
identifier: identifier,
|
||||
name: asset.name,
|
||||
symbol: asset.symbol,
|
||||
description: asset.normalizedMetadata?.description,
|
||||
imageUrl: asset.normalizedMetadata?.imageUrl,
|
||||
).save();
|
||||
}
|
||||
|
||||
Future<void> removeSolana(String walletName, String mint) =>
|
||||
ImportedNFT.deleteOne(walletName, ImportedNFT.solanaChain, mint);
|
||||
|
||||
static String? evmIdentifier(NFTAssetModel asset) {
|
||||
final address = asset.tokenAddress;
|
||||
final tokenId = asset.tokenId;
|
||||
|
||||
if (address == null || address.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return "$address:${tokenId ?? ""}";
|
||||
}
|
||||
|
||||
ImportedNFT _rowForSolana(String walletName, String mint, SolanaNFTAssetModel asset) =>
|
||||
ImportedNFT(
|
||||
walletName: walletName,
|
||||
chain: ImportedNFT.solanaChain,
|
||||
identifier: mint,
|
||||
name: asset.name,
|
||||
symbol: asset.symbol,
|
||||
description: asset.description,
|
||||
imageUrl: asset.imageOriginalUrl,
|
||||
isOwned: asset.isOwned,
|
||||
);
|
||||
|
||||
String _tokenAddressOf(String identifier) => identifier.split(":").first;
|
||||
|
||||
String _tokenIdOf(String identifier) {
|
||||
final parts = identifier.split(":");
|
||||
|
||||
return parts.length > 1 ? parts.sublist(1).join(":") : "";
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
import "package:cw_core/utils/ipfs_url.dart";
|
||||
import "package:cw_core/utils/nft_text.dart";
|
||||
|
||||
class SolanaNFTAssetModel {
|
||||
String? address;
|
||||
String? mint;
|
||||
@@ -12,46 +15,50 @@ class SolanaNFTAssetModel {
|
||||
Metaplex? metaplex;
|
||||
Collection? collection;
|
||||
Contract? contract;
|
||||
bool? isOwned;
|
||||
|
||||
SolanaNFTAssetModel({
|
||||
this.address,
|
||||
this.mint,
|
||||
this.standard,
|
||||
this.name,
|
||||
this.symbol,
|
||||
this.description,
|
||||
this.imageOriginalUrl,
|
||||
String? name,
|
||||
String? symbol,
|
||||
String? description,
|
||||
String? imageOriginalUrl,
|
||||
this.externalUrl,
|
||||
this.metadataOriginalUrl,
|
||||
this.totalSupply,
|
||||
this.metaplex,
|
||||
this.collection,
|
||||
this.contract,
|
||||
});
|
||||
this.isOwned,
|
||||
}) : name = sanitizeNFTText(name),
|
||||
symbol = sanitizeNFTText(symbol),
|
||||
description = sanitizeNFTText(description),
|
||||
imageOriginalUrl = tryNormalizeIpfsUrl(imageOriginalUrl);
|
||||
|
||||
factory SolanaNFTAssetModel.fromJson(Map<String, dynamic> json) {
|
||||
return SolanaNFTAssetModel(
|
||||
address: json['address'] as String?,
|
||||
mint: json['mint'] as String?,
|
||||
standard: json['standard'] as String?,
|
||||
name: json['name'] as String?,
|
||||
symbol: json['symbol'] as String?,
|
||||
description: json['description'] as String?,
|
||||
imageOriginalUrl: json['imageOriginalUrl'] as String?,
|
||||
externalUrl: json['externalUrl'] as String?,
|
||||
metadataOriginalUrl: json['metadataOriginalUrl'] as String?,
|
||||
totalSupply: json['totalSupply'] as String?,
|
||||
metaplex: json['metaplex'] != null
|
||||
? Metaplex.fromJson(json['metaplex'] as Map<String, dynamic>)
|
||||
: null,
|
||||
collection: json['collection'] != null
|
||||
? Collection.fromJson(json['collection'] as Map<String, dynamic>)
|
||||
: null,
|
||||
contract: json['contract'] != null
|
||||
? Contract.fromJson(json['contract'] as Map<String, dynamic>)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
factory SolanaNFTAssetModel.fromJson(Map<String, dynamic> json) => SolanaNFTAssetModel(
|
||||
address: json["address"] as String?,
|
||||
mint: json["mint"] as String?,
|
||||
standard: json["standard"] as String?,
|
||||
name: json["name"] as String?,
|
||||
symbol: json["symbol"] as String?,
|
||||
description: json["description"] as String?,
|
||||
imageOriginalUrl: json["imageOriginalUrl"] as String?,
|
||||
externalUrl: json["externalUrl"] as String?,
|
||||
metadataOriginalUrl: json["metadataOriginalUrl"] as String?,
|
||||
totalSupply: json["totalSupply"] as String?,
|
||||
metaplex: json["metaplex"] != null
|
||||
? Metaplex.fromJson(json["metaplex"] as Map<String, dynamic>)
|
||||
: null,
|
||||
collection: json["collection"] != null
|
||||
? Collection.fromJson(json["collection"] as Map<String, dynamic>)
|
||||
: null,
|
||||
contract: json["contract"] != null
|
||||
? Contract.fromJson(json["contract"] as Map<String, dynamic>)
|
||||
: null,
|
||||
isOwned: json["isOwned"] as bool?,
|
||||
);
|
||||
}
|
||||
|
||||
class Metaplex {
|
||||
@@ -100,17 +107,15 @@ class Collection {
|
||||
this.metaplexMint,
|
||||
this.sellerFeeBasisPoints});
|
||||
|
||||
factory Collection.fromJson(Map<String, dynamic> json) {
|
||||
return Collection(
|
||||
collectionAddress: json['collectionAddress'] as String?,
|
||||
name: json['name'] as String?,
|
||||
description: json['description'] as String?,
|
||||
imageOriginalUrl: json['imageOriginalUrl'] as String?,
|
||||
externalUrl: json['externalUrl'] as String?,
|
||||
metaplexMint: json['metaplexMint'] as String?,
|
||||
sellerFeeBasisPoints: json['sellerFeeBasisPoints'] as int?,
|
||||
);
|
||||
}
|
||||
factory Collection.fromJson(Map<String, dynamic> json) => Collection(
|
||||
collectionAddress: json["collectionAddress"] as String?,
|
||||
name: sanitizeNFTText(json["name"] as String?),
|
||||
description: sanitizeNFTText(json["description"] as String?),
|
||||
imageOriginalUrl: tryNormalizeIpfsUrl(json["imageOriginalUrl"] as String?),
|
||||
externalUrl: json["externalUrl"] as String?,
|
||||
metaplexMint: json["metaplexMint"] as String?,
|
||||
sellerFeeBasisPoints: json["sellerFeeBasisPoints"] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
class Contract {
|
||||
@@ -123,8 +128,8 @@ class Contract {
|
||||
factory Contract.fromJson(Map<String, dynamic> json) {
|
||||
return Contract(
|
||||
type: json['type'] as String?,
|
||||
name: json['name'] as String?,
|
||||
symbol: json['symbol'] as String?,
|
||||
name: sanitizeNFTText(json["name"] as String?),
|
||||
symbol: sanitizeNFTText(json["symbol"] as String?),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'template.g.dart';
|
||||
part 'template.part.dart';
|
||||
|
||||
@HiveType(typeId: Template.typeId)
|
||||
// @HiveType(typeId: Template.typeId)
|
||||
class Template extends HiveObject {
|
||||
Template(
|
||||
{required this.nameRaw,
|
||||
@@ -18,28 +18,28 @@ class Template extends HiveObject {
|
||||
static const typeId = TEMPLATE_TYPE_ID;
|
||||
static const boxName = 'Template';
|
||||
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
String? nameRaw;
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
String? addressRaw;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
String? cryptoCurrencyRaw;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
String? amountRaw;
|
||||
|
||||
@HiveField(4)
|
||||
// @HiveField(4)
|
||||
String? fiatCurrencyRaw;
|
||||
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
bool? isCurrencySelectedRaw;
|
||||
|
||||
@HiveField(6)
|
||||
// @HiveField(6)
|
||||
String? amountFiatRaw;
|
||||
|
||||
@HiveField(7)
|
||||
// @HiveField(7)
|
||||
List<Template>? additionalRecipientsRaw;
|
||||
|
||||
bool get isCurrencySelected => isCurrencySelectedRaw ?? false;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'template.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class TemplateAdapter extends TypeAdapter<Template> {
|
||||
@override
|
||||
final int typeId = 6;
|
||||
|
||||
@override
|
||||
Template read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return Template(
|
||||
nameRaw: fields[0] as String?,
|
||||
isCurrencySelectedRaw: fields[5] as bool?,
|
||||
addressRaw: fields[1] as String?,
|
||||
cryptoCurrencyRaw: fields[2] as String?,
|
||||
amountRaw: fields[3] as String?,
|
||||
fiatCurrencyRaw: fields[4] as String?,
|
||||
amountFiatRaw: fields[6] as String?,
|
||||
additionalRecipientsRaw: (fields[7] as List?)?.cast<Template>(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, Template obj) {
|
||||
writer
|
||||
..writeByte(8)
|
||||
..writeByte(0)
|
||||
..write(obj.nameRaw)
|
||||
..writeByte(1)
|
||||
..write(obj.addressRaw)
|
||||
..writeByte(2)
|
||||
..write(obj.cryptoCurrencyRaw)
|
||||
..writeByte(3)
|
||||
..write(obj.amountRaw)
|
||||
..writeByte(4)
|
||||
..write(obj.fiatCurrencyRaw)
|
||||
..writeByte(5)
|
||||
..write(obj.isCurrencySelectedRaw)
|
||||
..writeByte(6)
|
||||
..write(obj.amountFiatRaw)
|
||||
..writeByte(7)
|
||||
..write(obj.additionalRecipientsRaw);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is TemplateAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'transaction_description.g.dart';
|
||||
part 'transaction_description.part.dart';
|
||||
|
||||
@HiveType(typeId: TransactionDescription.typeId)
|
||||
// @HiveType(typeId: TransactionDescription.typeId)
|
||||
class TransactionDescription extends HiveObject {
|
||||
TransactionDescription(
|
||||
{required this.id, this.recipientAddress, this.transactionNote, this.transactionKey});
|
||||
@@ -12,16 +12,16 @@ class TransactionDescription extends HiveObject {
|
||||
static const boxName = 'TransactionDescriptions';
|
||||
static const boxKey = 'transactionDescriptionsBoxKey';
|
||||
|
||||
@HiveField(0, defaultValue: '')
|
||||
// @HiveField(0, defaultValue: '')
|
||||
String id;
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
String? recipientAddress;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
String? transactionNote;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
String? transactionKey;
|
||||
|
||||
String get note => transactionNote ?? '';
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'transaction_description.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class TransactionDescriptionAdapter extends TypeAdapter<TransactionDescription> {
|
||||
@override
|
||||
final int typeId = 2;
|
||||
|
||||
@override
|
||||
TransactionDescription read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return TransactionDescription(
|
||||
id: fields[0] == null ? '' : fields[0] as String,
|
||||
recipientAddress: fields[1] as String?,
|
||||
transactionNote: fields[2] as String?,
|
||||
transactionKey: fields[3] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, TransactionDescription obj) {
|
||||
writer
|
||||
..writeByte(4)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.recipientAddress)
|
||||
..writeByte(2)
|
||||
..write(obj.transactionNote)
|
||||
..writeByte(3)
|
||||
..write(obj.transactionKey);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is TransactionDescriptionAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
import "package:cw_core/utils/ipfs_url.dart";
|
||||
import "package:cw_core/utils/nft_text.dart";
|
||||
|
||||
class WalletNFTsResponseModel {
|
||||
final int? page;
|
||||
final int? pageSize;
|
||||
@@ -40,8 +43,8 @@ class NFTAssetModel {
|
||||
tokenAddress: json['token_address'] as String?,
|
||||
tokenId: json['token_id'] as String?,
|
||||
contractType: json['contract_type'] as String?,
|
||||
name: json['name'] as String?,
|
||||
symbol: json['symbol'] as String?,
|
||||
name: sanitizeNFTText(json["name"] as String?),
|
||||
symbol: sanitizeNFTText(json["symbol"] as String?),
|
||||
normalizedMetadata: json['normalized_metadata'] != null
|
||||
? new NormalizedMetadata.fromJson(json['normalized_metadata'] as Map<String, dynamic>)
|
||||
: null,
|
||||
@@ -61,33 +64,11 @@ class NormalizedMetadata {
|
||||
|
||||
factory NormalizedMetadata.fromJson(Map<String, dynamic> json) {
|
||||
return NormalizedMetadata(
|
||||
name: json['name'] as String?,
|
||||
description: json['description'] as String?,
|
||||
image: json['image'] as String?,
|
||||
name: sanitizeNFTText(json["name"] as String?),
|
||||
description: sanitizeNFTText(json["description"] as String?),
|
||||
image: json["image"] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
String? get imageUrl {
|
||||
if (image == null) return image;
|
||||
|
||||
if (image!.contains('ipfs.io')) return image;
|
||||
|
||||
if (!image!.contains('ipfs')) return image;
|
||||
|
||||
// IPFS public gateway provided by Cloudflare is https://cloudflare-ipfs.com/ipfs/
|
||||
//
|
||||
// Here is an example of an ipfs image link:
|
||||
//
|
||||
// [ipfs://bafkreia2i2ctfexpovgzfff66wqhbmwwpvqjvozan7ioifzcnq76jharwu]
|
||||
|
||||
//https://ipfs.io/ipfs/QmTRcRXo6cXByjHYHTVxGpag6vpocrG3rxjPC9PxKAArR9/1620.png
|
||||
|
||||
const String ipfsPublicGateway = 'https://cloudflare-ipfs.com/ipfs/';
|
||||
|
||||
final ipfsPath = image?.split('//')[1];
|
||||
|
||||
final imageLink = '$ipfsPublicGateway$ipfsPath';
|
||||
|
||||
return imageLink;
|
||||
}
|
||||
String? get imageUrl => tryNormalizeIpfsUrl(image);
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
|
||||
class WellKnownRecord {
|
||||
WellKnownRecord({
|
||||
required this.address,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String address;
|
||||
|
||||
static Future<String?> checkWellKnownUsername(String username, CryptoCurrency currency) async {
|
||||
String jsonLocation = "";
|
||||
switch (currency) {
|
||||
case CryptoCurrency.nano:
|
||||
jsonLocation = "nano-currency";
|
||||
break;
|
||||
// TODO: add other currencies
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
// split the string by the @ symbol:
|
||||
try {
|
||||
final List<String> splitStrs = username.split("@");
|
||||
String name = splitStrs.first.toLowerCase();
|
||||
final String domain = splitStrs.last;
|
||||
|
||||
if (splitStrs.length == 3) {
|
||||
// for username like @alice@domain.org instead of alice@domain.org
|
||||
name = splitStrs[1];
|
||||
}
|
||||
|
||||
if (name.isEmpty) {
|
||||
name = "_";
|
||||
}
|
||||
|
||||
// lookup domain/.well-known/nano-currency.json and check if it has a nano address:
|
||||
final response = await ProxyWrapper().get(
|
||||
clearnetUri: Uri.parse("https://$domain/.well-known/$jsonLocation.json?names=$name"),
|
||||
headers: <String, String>{"Accept": "application/json"},
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
// Access the first element in the names array and retrieve its address
|
||||
final List<dynamic> names = decoded["names"] as List<dynamic>;
|
||||
for (final dynamic item in names) {
|
||||
if (item["name"].toLowerCase() == name) {
|
||||
return item["address"] as String;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
printV("error checking well-known username: $e");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String formatDomainName(String name) {
|
||||
String formattedName = name;
|
||||
|
||||
if (name.contains("@")) {
|
||||
formattedName = name.replaceAll("@", ".");
|
||||
}
|
||||
|
||||
return formattedName;
|
||||
}
|
||||
|
||||
static Future<WellKnownRecord?> fetchAddressAndName({
|
||||
required String formattedName,
|
||||
required CryptoCurrency currency,
|
||||
}) async {
|
||||
String name = formattedName;
|
||||
|
||||
printV("formattedName: $formattedName");
|
||||
|
||||
final address = await checkWellKnownUsername(formattedName, currency);
|
||||
|
||||
if (address == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return WellKnownRecord(address: address, name: name);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
class YatRecord {
|
||||
YatRecord({
|
||||
required this.category,
|
||||
required this.address,
|
||||
});
|
||||
|
||||
YatRecord.fromJson(Map<String, dynamic> json)
|
||||
: address = json['address'] as String,
|
||||
category = json['category'] as String;
|
||||
|
||||
String category;
|
||||
String address;
|
||||
}
|
||||
Reference in New Issue
Block a user