mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Reformat the whole codebase with 100 line width (#3403)
This commit is contained in:
@@ -5,7 +5,6 @@ import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
|
||||
abstract class AddressLookupProvider {
|
||||
|
||||
AddressSource get source;
|
||||
|
||||
List<CryptoCurrency> get supportedCurrencies;
|
||||
|
||||
@@ -104,7 +104,8 @@ class AddressResolverService {
|
||||
|
||||
if (results.isNotEmpty) {
|
||||
final parsedAddress = results.first.parsedAddressByCurrencyMap[currency];
|
||||
final parsedAddressStr = parsedAddress == null || parsedAddress.isEmpty ? 'N/A' : parsedAddress;
|
||||
final parsedAddressStr =
|
||||
parsedAddress == null || parsedAddress.isEmpty ? 'N/A' : parsedAddress;
|
||||
printV('[address resolver service] resolved $src - $cur - $parsedAddressStr');
|
||||
return [results.first];
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ class AddressResolverUtils {
|
||||
final result = <CryptoCurrency, String>{};
|
||||
|
||||
for (final currency in currencies) {
|
||||
final address = await UnstoppableAddressProvider.fetchUnstoppableDomainAddress(domain, currency.title);
|
||||
final address =
|
||||
await UnstoppableAddressProvider.fetchUnstoppableDomainAddress(domain, currency.title);
|
||||
if (address.isNotEmpty) {
|
||||
result[currency] = address;
|
||||
}
|
||||
|
||||
@@ -54,15 +54,17 @@ class Bip353AddressProvider extends AddressLookupProvider {
|
||||
printV('Bip353Record.fetchDnsProof error: $e');
|
||||
}
|
||||
|
||||
return [ParsedAddress(
|
||||
parsedAddressByCurrencyMap: result,
|
||||
addressSource: AddressSource.bip353,
|
||||
handle: query,
|
||||
bip353DnsProof: dnsProof ?? '',
|
||||
)];
|
||||
return [
|
||||
ParsedAddress(
|
||||
parsedAddressByCurrencyMap: result,
|
||||
addressSource: AddressSource.bip353,
|
||||
handle: query,
|
||||
bip353DnsProof: dnsProof ?? '',
|
||||
)
|
||||
];
|
||||
} catch (e) {
|
||||
printV('[address resolver] Error resolving BIP353 address: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,5 +99,4 @@ class Bip353Record {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import 'package:web3dart/web3dart.dart';
|
||||
|
||||
class EnsRecord {
|
||||
static Future<String> fetchEnsAddress(
|
||||
String name,
|
||||
CryptoCurrency cur, {
|
||||
WalletBase? wallet,
|
||||
}) async {
|
||||
String name,
|
||||
CryptoCurrency cur, {
|
||||
WalletBase? wallet,
|
||||
}) async {
|
||||
Web3Client? _client;
|
||||
|
||||
if (wallet?.type == WalletType.ethereum || wallet?.type == WalletType.polygon) {
|
||||
@@ -45,11 +45,11 @@ class EnsRecord {
|
||||
}
|
||||
|
||||
static CoinType? getEnsCoinType(CryptoCurrency cur) => switch (cur) {
|
||||
CryptoCurrency.xmr => CoinType.XMR,
|
||||
CryptoCurrency.btc => CoinType.BTC,
|
||||
CryptoCurrency.ltc => CoinType.LTC,
|
||||
CryptoCurrency.eth => CoinType.ETH,
|
||||
CryptoCurrency.matic => CoinType.MATIC,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
CryptoCurrency.xmr => CoinType.XMR,
|
||||
CryptoCurrency.btc => CoinType.BTC,
|
||||
CryptoCurrency.ltc => CoinType.LTC,
|
||||
CryptoCurrency.eth => CoinType.ETH,
|
||||
CryptoCurrency.matic => CoinType.MATIC,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,23 +34,23 @@ class FioAddressProvider extends AddressLookupProvider {
|
||||
}) async {
|
||||
final Map<CryptoCurrency, String> result = {};
|
||||
try {
|
||||
final bool isFioRegistered = await FioAddressProvider.checkAvail(query);
|
||||
if (!isFioRegistered) return [];
|
||||
final bool isFioRegistered = await FioAddressProvider.checkAvail(query);
|
||||
if (!isFioRegistered) return [];
|
||||
|
||||
for (final cur in currencies) {
|
||||
final address = await FioAddressProvider.getPubAddress(query, cur.title);
|
||||
if (address != null && address.isNotEmpty) {
|
||||
result[cur] = address;
|
||||
for (final cur in currencies) {
|
||||
final address = await FioAddressProvider.getPubAddress(query, cur.title);
|
||||
if (address != null && address.isNotEmpty) {
|
||||
result[cur] = address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.isNotEmpty) {
|
||||
return [
|
||||
ParsedAddress(
|
||||
parsedAddressByCurrencyMap: result, addressSource: AddressSource.fio, handle: query)
|
||||
];
|
||||
}
|
||||
return [];
|
||||
if (result.isNotEmpty) {
|
||||
return [
|
||||
ParsedAddress(
|
||||
parsedAddressByCurrencyMap: result, addressSource: AddressSource.fio, handle: query)
|
||||
];
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
printV('FioAddressProvider.resolve error: $e');
|
||||
return [];
|
||||
|
||||
@@ -16,7 +16,8 @@ class LNUrlPayAddressProvider extends AddressLookupProvider {
|
||||
List<CryptoCurrency> get supportedCurrencies => [CryptoCurrency.btcln];
|
||||
|
||||
@override
|
||||
bool canHandle(String q) => q.contains('.') && q.contains('@'); // LNURL-pay handle example: user@domain.com
|
||||
bool canHandle(String q) =>
|
||||
q.contains('.') && q.contains('@'); // LNURL-pay handle example: user@domain.com
|
||||
|
||||
@override
|
||||
bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsLNUrl;
|
||||
@@ -63,4 +64,4 @@ class LNUrlPayAddressProvider extends AddressLookupProvider {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class LNUrlPayRecord {
|
||||
final String address;
|
||||
|
||||
static Future<String?> checkWellKnownUsername(String username, CryptoCurrency currency,
|
||||
{bool allowRethrow = false}) async {
|
||||
{bool allowRethrow = false}) async {
|
||||
if (![CryptoCurrency.btc, CryptoCurrency.btcln].contains(currency)) return null;
|
||||
|
||||
// split the string by the @ symbol:
|
||||
|
||||
@@ -21,7 +21,6 @@ class MastodonAPI {
|
||||
);
|
||||
|
||||
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||
|
||||
|
||||
if (response.statusCode != 200) return null;
|
||||
|
||||
@@ -49,7 +48,6 @@ class MastodonAPI {
|
||||
);
|
||||
|
||||
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('Unexpected HTTP status: ${response.statusCode}');
|
||||
|
||||
@@ -15,12 +15,11 @@ class MastodonUser {
|
||||
|
||||
factory MastodonUser.fromJson(Map<String, dynamic> json) {
|
||||
return MastodonUser(
|
||||
id: json['id'] as String,
|
||||
username: json['username'] as String? ?? '',
|
||||
acct: json['acct'] as String,
|
||||
note: json['note'] as String,
|
||||
profileImageUrl: json['avatar'] as String? ?? ''
|
||||
);
|
||||
id: json['id'] as String,
|
||||
username: json['username'] as String? ?? '',
|
||||
acct: json['acct'] as String,
|
||||
note: json['note'] as String,
|
||||
profileImageUrl: json['avatar'] as String? ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ class NostrProfileHandler {
|
||||
}
|
||||
|
||||
static Future<UserMetadata?> processRelays(
|
||||
ProfilePointer profile,
|
||||
String nip05Address,
|
||||
) async {
|
||||
ProfilePointer profile,
|
||||
String nip05Address,
|
||||
) async {
|
||||
final userDomain = _extractDomain(nip05Address);
|
||||
const int metaKind = 0;
|
||||
|
||||
@@ -124,4 +124,4 @@ class NostrProfileHandler {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
class UserMetadata {
|
||||
final String name;
|
||||
final String lnurl;
|
||||
@@ -32,4 +31,4 @@ class UserMetadata {
|
||||
website: json['website'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,4 +66,4 @@ class OpenaliasRecord {
|
||||
|
||||
return OpenaliasRecord(address: address, name: name, description: note);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@ class ParsedAddress {
|
||||
final String profileName;
|
||||
final String description;
|
||||
final String? bip353DnsProof;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
|
||||
class TwitterAddressProvider extends AddressLookupProvider {
|
||||
|
||||
@override
|
||||
AddressSource get source => AddressSource.twitter;
|
||||
|
||||
|
||||
@@ -23,14 +23,14 @@ class TwitterApi {
|
||||
path: userPath + userName,
|
||||
queryParameters: queryParams);
|
||||
|
||||
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers).catchError((error) {
|
||||
final response =
|
||||
await ProxyWrapper().get(clearnetUri: uri, headers: headers).catchError((error) {
|
||||
throw Exception('HTTP request failed: $error');
|
||||
});
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
|
||||
final Map<String, dynamic> responseJSON = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
if (responseJSON['errors'] != null &&
|
||||
|
||||
@@ -211,13 +211,15 @@ class UnstoppableAddressProvider extends AddressLookupProvider {
|
||||
var address = '';
|
||||
|
||||
try {
|
||||
final uri = Uri.parse("https://api.unstoppabledomains.com/profile/public/${Uri.encodeQueryComponent(domain)}?fields=records");
|
||||
final uri = Uri.parse(
|
||||
"https://api.unstoppabledomains.com/profile/public/${Uri.encodeQueryComponent(domain)}?fields=records");
|
||||
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||
|
||||
final jsonParsed = json.decode(response.body) as Map<String, dynamic>;
|
||||
if (jsonParsed["records"] == null) {
|
||||
throw Exception(".records response from $uri is empty");
|
||||
};
|
||||
}
|
||||
;
|
||||
final records = jsonParsed["records"] as Map<String, dynamic>;
|
||||
final key = "crypto.${ticker.toUpperCase()}.address";
|
||||
if (records[key] == null) {
|
||||
|
||||
@@ -20,9 +20,9 @@ class WellKnownRecord {
|
||||
static const _jsonLocation = {CryptoCurrency.nano: 'nano-currency'};
|
||||
|
||||
static Future<WellKnownRecord?> fetch(
|
||||
String username,
|
||||
CryptoCurrency currency,
|
||||
) async {
|
||||
String username,
|
||||
CryptoCurrency currency,
|
||||
) async {
|
||||
final location = _jsonLocation[currency];
|
||||
if (location == null) {
|
||||
printV('well-known: unsupported coin $currency');
|
||||
@@ -75,4 +75,4 @@ class WellKnownRecord {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,4 +16,4 @@ class YatRecord {
|
||||
|
||||
@override
|
||||
String toString() => 'YatRecord(tag: $tag, address: $address)';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ class YatService {
|
||||
static const apiReleaseUrl = "https://a.y.at";
|
||||
static const apiDevUrl = 'https://a.yat.fyi';
|
||||
|
||||
static String lookupEmojiUrl(String emojiId) =>
|
||||
"$apiUrl/emoji_id/$emojiId/payment";
|
||||
static String lookupEmojiUrl(String emojiId) => "$apiUrl/emoji_id/$emojiId/payment";
|
||||
|
||||
static const String MONERO_SUB_ADDRESS = '0x1002';
|
||||
static const String MONERO_STD_ADDRESS = '0x1001';
|
||||
@@ -23,9 +22,9 @@ class YatService {
|
||||
};
|
||||
|
||||
static Future<List<YatRecord>> fetchYatAddress(
|
||||
String emojiId,
|
||||
String ticker,
|
||||
) async {
|
||||
String emojiId,
|
||||
String ticker,
|
||||
) async {
|
||||
final tagQuery = tags[ticker.toUpperCase()];
|
||||
if (tagQuery == null) return const [];
|
||||
|
||||
@@ -48,9 +47,7 @@ class YatService {
|
||||
records.add(YatRecord.fromJson(data, tag.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
else if (res is List) {
|
||||
} else if (res is List) {
|
||||
for (final item in res) {
|
||||
if (item is Map<String, dynamic>) {
|
||||
final tag = item['tag']?.toString() ?? '';
|
||||
@@ -64,4 +61,4 @@ class YatService {
|
||||
return const [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,29 +25,28 @@ class YatLink {
|
||||
static const requestReleaseUrl = ''; //'https://a.y.at/emoji_id/';
|
||||
static const startFlowUrl = ''; // 'https://www.y03btrk.com/4RQSJ/6JHXF/';
|
||||
static const isDevMode = true;
|
||||
static const tags = <String, List<String>>{"XMR" : ['0x1001', '0x1002'],
|
||||
"BTC" : ['0x1003'], "LTC" : ['0x1019']};
|
||||
static const tags = <String, List<String>>{
|
||||
"XMR": ['0x1001', '0x1002'],
|
||||
"BTC": ['0x1003'],
|
||||
"LTC": ['0x1019']
|
||||
};
|
||||
|
||||
static String get apiUrl => YatLink.isDevMode
|
||||
? YatLink.apiDevUrl
|
||||
: YatLink.apiReleaseUrl;
|
||||
static String get apiUrl => YatLink.isDevMode ? YatLink.apiDevUrl : YatLink.apiReleaseUrl;
|
||||
|
||||
static String get emojiIdUrl => apiUrl + '/emoji_id/';
|
||||
|
||||
static String get baseUrl => YatLink.isDevMode
|
||||
? YatLink.baseDevUrl
|
||||
: YatLink.baseReleaseUrl;
|
||||
static String get baseUrl => YatLink.isDevMode ? YatLink.baseDevUrl : YatLink.baseReleaseUrl;
|
||||
}
|
||||
|
||||
Future<List<String>> fetchYatAddress(String emojiId, String ticker) async {
|
||||
throw Exception();
|
||||
//final url = YatLink.emojiIdUrl + emojiId + '/payment';
|
||||
//final response = await get(url);
|
||||
|
||||
|
||||
//if (response.statusCode != 200) {
|
||||
// throw YatException(text: response.body.toString());
|
||||
//}
|
||||
|
||||
|
||||
//final addresses = <String>[];
|
||||
//final currency = ticker.toUpperCase();
|
||||
//final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
@@ -55,7 +54,7 @@ Future<List<String>> fetchYatAddress(String emojiId, String ticker) async {
|
||||
//result.forEach((dynamic key, dynamic value) {
|
||||
// final tag = key as String ?? '';
|
||||
// final record = value as Map<String, dynamic>;
|
||||
|
||||
|
||||
// if (YatLink.tags[currency]?.contains(tag) ?? false) {
|
||||
// final address = record['address'] as String;
|
||||
// if (address?.isNotEmpty ?? false) {
|
||||
@@ -80,11 +79,11 @@ Future<String> fetchYatAccessToken(String refreshToken) async {
|
||||
// 'Accept': '*/*'
|
||||
// },
|
||||
// body: bodyJson);
|
||||
|
||||
|
||||
// if (response.statusCode != 200) {
|
||||
// throw YatException(text: response.body.toString());
|
||||
// }
|
||||
|
||||
|
||||
// final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
// return responseJSON['access_token'] as String;
|
||||
//}catch(_) {
|
||||
@@ -110,7 +109,7 @@ Future<String> fetchYatApiKey(String accessKey) async {
|
||||
// if (response.statusCode != 200) {
|
||||
// throw YatException(text: response.body.toString());
|
||||
// }
|
||||
|
||||
|
||||
// final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
// return responseJSON['api_key'] as String;
|
||||
//}catch(_) {
|
||||
@@ -119,7 +118,8 @@ Future<String> fetchYatApiKey(String accessKey) async {
|
||||
return '';
|
||||
}
|
||||
|
||||
Future<void> updateEmojiIdAddress(String emojiId, String address, String apiKey, WalletType type) async {
|
||||
Future<void> updateEmojiIdAddress(
|
||||
String emojiId, String address, String apiKey, WalletType type) async {
|
||||
throw Exception();
|
||||
//final url = YatLink.emojiIdUrl + emojiId;
|
||||
//final cur = walletTypeToCryptoCurrency(type);
|
||||
@@ -166,15 +166,13 @@ Future<String> visualisationForEmojiId(String emojiId) async {
|
||||
class YatStore = YatStoreBase with _$YatStore;
|
||||
|
||||
abstract class YatStoreBase with Store {
|
||||
YatStoreBase({
|
||||
required this.appStore,
|
||||
required this.secureStorage})
|
||||
: _wallet = appStore.wallet,
|
||||
emoji = appStore.wallet?.walletInfo?.yatEmojiId ?? '',
|
||||
refreshToken = '',
|
||||
accessToken = '',
|
||||
apiKey = '',
|
||||
emojiIncommingSC = StreamController<String>.broadcast() {
|
||||
YatStoreBase({required this.appStore, required this.secureStorage})
|
||||
: _wallet = appStore.wallet,
|
||||
emoji = appStore.wallet?.walletInfo?.yatEmojiId ?? '',
|
||||
refreshToken = '',
|
||||
accessToken = '',
|
||||
apiKey = '',
|
||||
emojiIncommingSC = StreamController<String>.broadcast() {
|
||||
//reaction((_) => appStore.wallet, _onWalletChange);
|
||||
//reaction((_) => emoji, (String _) => _onEmojiChange());
|
||||
//reaction((_) => refreshToken, (String _) => _onRefreshTokenChange());
|
||||
@@ -209,24 +207,22 @@ abstract class YatStoreBase with Store {
|
||||
Stream<String> get emojiIncommingStream => emojiIncommingSC.stream;
|
||||
|
||||
@observable
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
|
||||
_wallet;
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>? _wallet;
|
||||
|
||||
Future<void> init() async {
|
||||
if (_wallet == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
refreshToken = await secureStorage.read(key: yatRefreshTokenKey(_wallet!.walletInfo.name)) ?? '';
|
||||
refreshToken =
|
||||
await secureStorage.read(key: yatRefreshTokenKey(_wallet!.walletInfo.name)) ?? '';
|
||||
accessToken = await secureStorage.read(key: yatAccessTokenKey(_wallet!.walletInfo.name)) ?? '';
|
||||
apiKey = await secureStorage.read(key: yatApiKey(_wallet!.walletInfo.name)) ?? '';
|
||||
}
|
||||
|
||||
|
||||
@action
|
||||
void _onWalletChange(
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
|
||||
TransactionInfo>
|
||||
wallet) {
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet) {
|
||||
this._wallet = wallet;
|
||||
emoji = wallet?.walletInfo?.yatEmojiId ?? '';
|
||||
init();
|
||||
@@ -252,7 +248,7 @@ abstract class YatStoreBase with Store {
|
||||
@action
|
||||
Future<void> _onRefreshTokenChange() async {
|
||||
throw Exception();
|
||||
|
||||
|
||||
//try {
|
||||
// await secureStorage.write(key: yatRefreshTokenKey(_wallet.walletInfo.name), value: refreshToken);
|
||||
// accessToken = await fetchYatAccessToken(refreshToken);
|
||||
@@ -269,7 +265,7 @@ abstract class YatStoreBase with Store {
|
||||
//final result = <String, String>{};
|
||||
//final tags = YatLink.tags[_wallet.currency.toString().toUpperCase()];
|
||||
//String tag = tags.first;
|
||||
|
||||
|
||||
//if (_wallet.type == WalletType.monero
|
||||
// && _wallet.walletAddresses.address.startsWith('4')) {
|
||||
// tag = tags.last;
|
||||
@@ -277,7 +273,7 @@ abstract class YatStoreBase with Store {
|
||||
//result[tag] = '${_wallet.walletAddresses.address}|${_wallet.name}';
|
||||
//final addressJson = json.encode([result]);
|
||||
//final addressJsonBytes = utf8.encode(addressJson);
|
||||
|
||||
|
||||
//return base64.encode(addressJsonBytes);
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
|
||||
class ZanoAliasAddressProvider extends AddressLookupProvider {
|
||||
|
||||
@override
|
||||
AddressSource get source => AddressSource.zanoAlias;
|
||||
|
||||
|
||||
@@ -67,8 +67,7 @@ class ZcashNamesRecord {
|
||||
}
|
||||
|
||||
static bool _isValidZcashAddress(String address) {
|
||||
final pattern =
|
||||
AddressValidator.getAddressFromStringPattern(CryptoCurrency.zec);
|
||||
final pattern = AddressValidator.getAddressFromStringPattern(CryptoCurrency.zec);
|
||||
if (pattern == null) return false;
|
||||
return RegExp('^(?:$pattern)\$').hasMatch(address);
|
||||
}
|
||||
|
||||
@@ -12,29 +12,29 @@ const AFTER_REGEX = '(\$|\\s)';
|
||||
class AddressValidator extends TextValidator {
|
||||
AddressValidator({required CryptoCurrency type, bool isTestnet = false})
|
||||
: super(
|
||||
errorMessage: S.current.error_text_address,
|
||||
useAdditionalValidation: [CryptoCurrency.btc, CryptoCurrency.ltc].contains(type)
|
||||
? (String txt) {
|
||||
final RegExp lightningInvoiceRegex = RegExp(
|
||||
r'^(lightning:)?(lnbc|lntb|lnbs|lnbcrt|lnurl)[a-z0-9]+$',
|
||||
caseSensitive: false);
|
||||
if (lightningInvoiceRegex.hasMatch(txt)) return true;
|
||||
errorMessage: S.current.error_text_address,
|
||||
useAdditionalValidation: [CryptoCurrency.btc, CryptoCurrency.ltc].contains(type)
|
||||
? (String txt) {
|
||||
final RegExp lightningInvoiceRegex = RegExp(
|
||||
r'^(lightning:)?(lnbc|lntb|lnbs|lnbcrt|lnurl)[a-z0-9]+$',
|
||||
caseSensitive: false);
|
||||
if (lightningInvoiceRegex.hasMatch(txt)) return true;
|
||||
|
||||
return BitcoinAddressUtils.validateAddress(
|
||||
address: txt,
|
||||
network: type == CryptoCurrency.btc
|
||||
? isTestnet
|
||||
? BitcoinNetwork.testnet
|
||||
: BitcoinNetwork.mainnet
|
||||
: LitecoinNetwork.mainnet,
|
||||
);
|
||||
}
|
||||
: type == CryptoCurrency.zano
|
||||
? zano?.validateAddress
|
||||
: null,
|
||||
pattern: getPattern(type, isTestnet: isTestnet),
|
||||
length: getLength(type),
|
||||
);
|
||||
return BitcoinAddressUtils.validateAddress(
|
||||
address: txt,
|
||||
network: type == CryptoCurrency.btc
|
||||
? isTestnet
|
||||
? BitcoinNetwork.testnet
|
||||
: BitcoinNetwork.mainnet
|
||||
: LitecoinNetwork.mainnet,
|
||||
);
|
||||
}
|
||||
: type == CryptoCurrency.zano
|
||||
? zano?.validateAddress
|
||||
: null,
|
||||
pattern: getPattern(type, isTestnet: isTestnet),
|
||||
length: getLength(type),
|
||||
);
|
||||
|
||||
static const List<CryptoCurrency> reliableValidateCurrencies = [
|
||||
CryptoCurrency.xmr,
|
||||
@@ -170,8 +170,7 @@ class AddressValidator extends TextValidator {
|
||||
case CryptoCurrency.hbar:
|
||||
pattern = '[0-9a-zA-Z.]+';
|
||||
case CryptoCurrency.zec:
|
||||
pattern =
|
||||
'(?:'
|
||||
pattern = '(?:'
|
||||
't1[0-9A-Za-z]{33}'
|
||||
'|t3[0-9A-Za-z]{33}'
|
||||
'|zs[a-z0-9]{76}'
|
||||
@@ -195,7 +194,8 @@ class AddressValidator extends TextValidator {
|
||||
case CryptoCurrency.pivx:
|
||||
pattern = 'D([1-9a-km-zA-HJ-NP-Z]){33}';
|
||||
case CryptoCurrency.btcln:
|
||||
pattern = r'(lightning:)?(lnbc|lntb|lnbs|lnbcrt|lnurl|LNBC|LNTB|LNBS|LNBCRT|LNURL)[a-zA-Z0-9]+';
|
||||
pattern =
|
||||
r'(lightning:)?(lnbc|lntb|lnbs|lnbcrt|lnurl|LNBC|LNTB|LNBS|LNBCRT|LNURL)[a-zA-Z0-9]+';
|
||||
case CryptoCurrency.zano:
|
||||
pattern = r'([1-9A-HJ-NP-Za-km-z]{90,200})|(@[\w\d.-]+)';
|
||||
case CryptoCurrency.doge:
|
||||
@@ -369,7 +369,7 @@ class AddressValidator extends TextValidator {
|
||||
'|([0-9a-zA-Z]{106})';
|
||||
case CryptoCurrency.btc:
|
||||
pattern =
|
||||
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
|
||||
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
|
||||
case CryptoCurrency.ltc:
|
||||
pattern = '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'
|
||||
@@ -394,8 +394,7 @@ class AddressValidator extends TextValidator {
|
||||
case CryptoCurrency.zano:
|
||||
pattern = '([1-9A-HJ-NP-Za-km-z]{90,200})|(@[\w\d.-]+)';
|
||||
case CryptoCurrency.zec:
|
||||
pattern =
|
||||
'(?:'
|
||||
pattern = '(?:'
|
||||
't1[0-9A-Za-z]{33}'
|
||||
'|t3[0-9A-Za-z]{33}'
|
||||
'|zs[a-z0-9]{76}'
|
||||
@@ -419,4 +418,4 @@ class AddressValidator extends TextValidator {
|
||||
|
||||
return pattern != null ? "($pattern)" : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ class AmountValidator extends TextValidator {
|
||||
String? minValue,
|
||||
String? maxValue,
|
||||
}) {
|
||||
symbolsAmountValidator =
|
||||
SymbolsAmountValidator(isAutovalidate: isAutovalidate);
|
||||
decimalAmountValidator = DecimalAmountValidator(currency: currency,isAutovalidate: isAutovalidate);
|
||||
symbolsAmountValidator = SymbolsAmountValidator(isAutovalidate: isAutovalidate);
|
||||
decimalAmountValidator =
|
||||
DecimalAmountValidator(currency: currency, isAutovalidate: isAutovalidate);
|
||||
|
||||
amountMinValidator = AmountMinValidator(
|
||||
minValue: minValue,
|
||||
|
||||
@@ -17,4 +17,3 @@ class AuthenticationBanned extends AuthState {
|
||||
|
||||
final String error;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ import 'package:cake_wallet/wallet_types.g.dart';
|
||||
import 'package:cake_backup/backup.dart' as cake_backup;
|
||||
|
||||
class $BackupService {
|
||||
$BackupService(this._secureStorage, this.transactionDescriptionBox,
|
||||
this.keyService, this.sharedPreferences)
|
||||
$BackupService(
|
||||
this._secureStorage, this.transactionDescriptionBox, this.keyService, this.sharedPreferences)
|
||||
: cipher = Cryptography.instance.chacha20Poly1305Aead(),
|
||||
correctWallets = <WalletInfo>[];
|
||||
|
||||
@@ -42,7 +42,6 @@ class $BackupService {
|
||||
final KeyService keyService;
|
||||
List<WalletInfo> correctWallets;
|
||||
|
||||
|
||||
Future<void> importBackupV1(Uint8List data, String password, {required String nonce}) async {
|
||||
final appDir = await getAppDir();
|
||||
final decryptedData = await _decryptV1(data, password, nonce);
|
||||
@@ -59,7 +58,8 @@ class $BackupService {
|
||||
} else {
|
||||
Directory('${appDir.path}/' + filename)..create(recursive: true);
|
||||
}
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
await verifyWallets();
|
||||
await _importKeychainDumpV1(password, nonce: nonce);
|
||||
@@ -101,7 +101,8 @@ class $BackupService {
|
||||
dir.createSync(recursive: true);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
await verifyWallets();
|
||||
await importKeychainDumpV2(password);
|
||||
@@ -112,7 +113,9 @@ class $BackupService {
|
||||
Future<void> verifyWallets() async {
|
||||
await performHiveMigration(); // for backups made before sqlite migration
|
||||
await performTradeHiveMigration(_secureStorage);
|
||||
correctWallets = (await WalletInfo.getAll()).where((info) => availableWalletTypes.contains(info.type)).toList();
|
||||
correctWallets = (await WalletInfo.getAll())
|
||||
.where((info) => availableWalletTypes.contains(info.type))
|
||||
.toList();
|
||||
|
||||
if (correctWallets.isEmpty) {
|
||||
printV('Correct wallets not detected');
|
||||
@@ -133,12 +136,11 @@ class $BackupService {
|
||||
MapEntry(key, TransactionDescription.fromJson(value as Map<String, dynamic>)));
|
||||
var box = transactionDescriptionBox;
|
||||
if (!box.isOpen) {
|
||||
final transactionDescriptionsBoxKey =
|
||||
await getEncryptionKey(secureStorage: _secureStorage, forKey: TransactionDescription.boxKey);
|
||||
box = await CakeHive.openBox<TransactionDescription>(
|
||||
TransactionDescription.boxName,
|
||||
encryptionKey: transactionDescriptionsBoxKey);
|
||||
}
|
||||
final transactionDescriptionsBoxKey = await getEncryptionKey(
|
||||
secureStorage: _secureStorage, forKey: TransactionDescription.boxKey);
|
||||
box = await CakeHive.openBox<TransactionDescription>(TransactionDescription.boxName,
|
||||
encryptionKey: transactionDescriptionsBoxKey);
|
||||
}
|
||||
await box.putAll(descriptionsMap);
|
||||
}
|
||||
|
||||
@@ -152,7 +154,8 @@ class $BackupService {
|
||||
|
||||
final data = json.decode(preferencesFile.readAsStringSync()) as Map<String, dynamic>;
|
||||
|
||||
try { // shouldn't throw an error but just in case, so it doesn't stop the backup restore
|
||||
try {
|
||||
// shouldn't throw an error but just in case, so it doesn't stop the backup restore
|
||||
for (var entry in data.entries) {
|
||||
String key = entry.key;
|
||||
dynamic value = entry.value;
|
||||
@@ -170,7 +173,8 @@ class $BackupService {
|
||||
await sharedPreferences.setStringList(key, value);
|
||||
} else {
|
||||
if (kDebugMode) {
|
||||
printV('Skipping individual save for key "$key": Unsupported type (${value.runtimeType}). Value: $value');
|
||||
printV(
|
||||
'Skipping individual save for key "$key": Unsupported type (${value.runtimeType}). Value: $value');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,9 +191,7 @@ class $BackupService {
|
||||
currentWalletName = correctWallets.first.name;
|
||||
currentWalletType = serializeToInt(correctWallets.first.type);
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (DeviceInfo.instance.isDesktop) {
|
||||
await sharedPreferences.setInt(PreferencesKey.currentTheme, ThemeList.darkTheme.raw);
|
||||
@@ -247,8 +249,8 @@ class $BackupService {
|
||||
for (var key in (keychainJSON['_all'] as Map<String, dynamic>).keys) {
|
||||
try {
|
||||
if (!key.startsWith('MONERO_WALLET_')) continue;
|
||||
final decodedPassword = decodeWalletPassword(
|
||||
password: keychainJSON['_all'][key].toString());
|
||||
final decodedPassword =
|
||||
decodeWalletPassword(password: keychainJSON['_all'][key].toString());
|
||||
final walletName = key.split('_WALLET_')[1];
|
||||
final walletType = key.split('_WALLET_')[0].toLowerCase();
|
||||
await importWalletKeychainInfo({
|
||||
@@ -294,8 +296,11 @@ class $BackupService {
|
||||
}));
|
||||
final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
|
||||
final backupPassword = await _secureStorage.read(key: backupPasswordKey);
|
||||
final data = utf8.encode(
|
||||
json.encode({'wallets': wallets, backupPasswordKey: backupPassword, '_all': await _secureStorage.readAll()}));
|
||||
final data = utf8.encode(json.encode({
|
||||
'wallets': wallets,
|
||||
backupPasswordKey: backupPassword,
|
||||
'_all': await _secureStorage.readAll()
|
||||
}));
|
||||
final encrypted = await _encryptV2(Uint8List.fromList(data), '$keychainSalt$password');
|
||||
|
||||
return encrypted;
|
||||
|
||||
@@ -73,6 +73,7 @@ class ChunkLength {
|
||||
'plain': plain,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChunkLength(encrypted: $encrypted, plain: $plain)';
|
||||
@@ -124,7 +125,9 @@ class BackupMetadata {
|
||||
return BackupMetadata(
|
||||
version: BackupVersion.values[json['version'] as int],
|
||||
sha512sum: json['sha512sum'] as String,
|
||||
chunks: (json['chunks'] as List<dynamic>).map((chunk) => ChunkDetails.fromJson(chunk as Map<String, dynamic>)).toList(),
|
||||
chunks: (json['chunks'] as List<dynamic>)
|
||||
.map((chunk) => ChunkDetails.fromJson(chunk as Map<String, dynamic>))
|
||||
.toList(),
|
||||
cakeVersion: json['cakeVersion'] as String,
|
||||
);
|
||||
}
|
||||
@@ -160,7 +163,8 @@ class IncompatibleBackupAppException implements Exception {
|
||||
}
|
||||
|
||||
class BackupServiceV3 extends $BackupService {
|
||||
BackupServiceV3(super.secureStorage, super.transactionDescriptionBox, super.keyService, super.sharedPreferences);
|
||||
BackupServiceV3(super.secureStorage, super.transactionDescriptionBox, super.keyService,
|
||||
super.sharedPreferences);
|
||||
|
||||
static BackupVersion get currentVersion => BackupVersion.v3;
|
||||
|
||||
@@ -170,12 +174,12 @@ class BackupServiceV3 extends $BackupService {
|
||||
|
||||
BackupVersion getVersionFile(File data) {
|
||||
final raf = data.openSync(mode: FileMode.read);
|
||||
|
||||
|
||||
try {
|
||||
// Read first 4 bytes to check both version and zip signature
|
||||
final buffer = Uint8List(1);
|
||||
final bytesRead = raf.readIntoSync(buffer);
|
||||
|
||||
|
||||
if (bytesRead == 0) {
|
||||
throw Exception('Invalid backup file: empty file');
|
||||
}
|
||||
@@ -231,7 +235,7 @@ class BackupServiceV3 extends $BackupService {
|
||||
}
|
||||
|
||||
Future<void> importBackupFileV3(File file, String password,
|
||||
{String nonce = secrets.backupSalt,bool checkBackupApp = true}) async{
|
||||
{String nonce = secrets.backupSalt, bool checkBackupApp = true}) async {
|
||||
// Overall design of v3 backup is the following:
|
||||
// 1. backup.zip - plaintext zip file that user can open with any archive manager
|
||||
// 2. backup.zip/README.txt - text file to let user know what is inside of this file
|
||||
@@ -259,14 +263,14 @@ class BackupServiceV3 extends $BackupService {
|
||||
throw Exception('Invalid v3 backup: missing data.bin');
|
||||
}
|
||||
final dataStream = dataFile.rawContent!.getStream();
|
||||
|
||||
|
||||
final decryptedData = File('${file.path}_decrypted'); // decrypted zip file
|
||||
if (decryptedData.existsSync()) {
|
||||
decryptedData.deleteSync();
|
||||
}
|
||||
decryptedData.createSync(recursive: true);
|
||||
decryptedData.writeAsBytesSync(Uint8List(0), mode: FileMode.write, flush: true);
|
||||
|
||||
|
||||
int chunkIndex = 0;
|
||||
for (var chunk in metadata.chunks) {
|
||||
chunkIndex++;
|
||||
@@ -275,7 +279,8 @@ class BackupServiceV3 extends $BackupService {
|
||||
|
||||
// readBytes stores position internally, so we don't need to think about it.
|
||||
if (chunk.sha512sum.encrypted != chunkChecksum) {
|
||||
throw Exception('Invalid v3 backup: chunk (${chunk.length.encrypted} bytes) checksum mismatch at index $chunkIndex\n'
|
||||
throw Exception(
|
||||
'Invalid v3 backup: chunk (${chunk.length.encrypted} bytes) checksum mismatch at index $chunkIndex\n'
|
||||
'expected: ${chunk.sha512sum.encrypted}\n'
|
||||
'got: $chunkChecksum');
|
||||
}
|
||||
@@ -283,7 +288,6 @@ class BackupServiceV3 extends $BackupService {
|
||||
decryptedData.writeAsBytesSync(decryptedChunk, mode: FileMode.append, flush: true);
|
||||
}
|
||||
|
||||
|
||||
final sha512sum = (await sha512.bind(decryptedData.openRead()).first).toString();
|
||||
if (sha512sum.toString() != metadata.sha512sum) {
|
||||
throw Exception('Invalid v3 backup: SHA512 checksum mismatch\n'
|
||||
@@ -301,7 +305,6 @@ class BackupServiceV3 extends $BackupService {
|
||||
final decryptedDataStream = InputFileStream(decryptedData.path);
|
||||
final backupArchive = zip.decodeStream(decryptedDataStream);
|
||||
|
||||
|
||||
final appDir = await getAppDir();
|
||||
|
||||
outer:
|
||||
@@ -315,8 +318,7 @@ class BackupServiceV3 extends $BackupService {
|
||||
}
|
||||
printV("restoring: $filename");
|
||||
if (file.isFile) {
|
||||
final output = File('${appDir.path}/' + filename)
|
||||
..createSync(recursive: true);
|
||||
final output = File('${appDir.path}/' + filename)..createSync(recursive: true);
|
||||
final outputStream = OutputFileStream(output.path);
|
||||
file.writeContent(outputStream);
|
||||
outputStream.flush();
|
||||
@@ -326,7 +328,8 @@ class BackupServiceV3 extends $BackupService {
|
||||
dir.createSync(recursive: true);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
// Continue importing the backup the old way
|
||||
await super.verifyWallets();
|
||||
@@ -344,10 +347,10 @@ class BackupServiceV3 extends $BackupService {
|
||||
{String keychainSalt = secrets.backupKeychainSalt}) async {
|
||||
final appDir = await getAppDir();
|
||||
final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
|
||||
final decryptedKeychainDumpFileData = await decryptV2(
|
||||
keychainDumpFile.readAsBytesSync(), '$keychainSalt$password');
|
||||
final keychainJSON = json.decode(utf8.decode(decryptedKeychainDumpFileData))
|
||||
as Map<String, dynamic>;
|
||||
final decryptedKeychainDumpFileData =
|
||||
await decryptV2(keychainDumpFile.readAsBytesSync(), '$keychainSalt$password');
|
||||
final keychainJSON =
|
||||
json.decode(utf8.decode(decryptedKeychainDumpFileData)) as Map<String, dynamic>;
|
||||
final keychainWalletsInfo = keychainJSON['wallets'] as List;
|
||||
|
||||
final expectedHardwareWallets = keychainWalletsInfo
|
||||
@@ -358,10 +361,10 @@ class BackupServiceV3 extends $BackupService {
|
||||
|
||||
for (final expectedHardwareWallet in expectedHardwareWallets) {
|
||||
final info = expectedHardwareWallet as Map<String, dynamic>;
|
||||
final actualWalletInfo = await WalletInfo.get(info['name'] as String, WalletType.values.firstWhere((e) => e.toString() == info['type'] as String));
|
||||
final actualWalletInfo = await WalletInfo.get(info['name'] as String,
|
||||
WalletType.values.firstWhere((e) => e.toString() == info['type'] as String));
|
||||
if (actualWalletInfo != null &&
|
||||
info["hardwareWalletType"] !=
|
||||
actualWalletInfo.hardwareWalletType?.index) {
|
||||
info["hardwareWalletType"] != actualWalletInfo.hardwareWalletType?.index) {
|
||||
actualWalletInfo.hardwareWalletType =
|
||||
HardwareWalletType.values[info["hardwareWalletType"] as int];
|
||||
await actualWalletInfo.save();
|
||||
@@ -390,7 +393,8 @@ class BackupServiceV3 extends $BackupService {
|
||||
final transactionDescriptionDumpFile =
|
||||
File('${tmpDir.path}/~_transaction_descriptions_dump_TMP');
|
||||
|
||||
final transactionDescriptionData = super.transactionDescriptionBox
|
||||
final transactionDescriptionData = super
|
||||
.transactionDescriptionBox
|
||||
.toMap()
|
||||
.map((key, value) => MapEntry(key.toString(), value.toJson()));
|
||||
final transactionDescriptionDump = jsonEncode(transactionDescriptionData);
|
||||
@@ -443,15 +447,14 @@ class BackupServiceV3 extends $BackupService {
|
||||
int chunkIndex = 0;
|
||||
final stopwatch = Stopwatch()..start();
|
||||
printV("Starting backup encryption...");
|
||||
|
||||
|
||||
metadata.sha512sum = (await sha512.bind(dataBinUnencrypted.openRead()).first).toString();
|
||||
|
||||
final raf = await dataBinUnencrypted.open();
|
||||
|
||||
|
||||
while (true) {
|
||||
printV("Reading chunk ${chunkIndex++}");
|
||||
|
||||
|
||||
stopwatch.reset();
|
||||
final chunk = await raf.read(chunkSize);
|
||||
printV("Chunk read completed in ${stopwatch.elapsed}");
|
||||
@@ -459,13 +462,14 @@ class BackupServiceV3 extends $BackupService {
|
||||
if (chunk.length == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
stopwatch.reset();
|
||||
final encryptedChunk = await cake_backup.encrypt(password, chunk);
|
||||
printV("Encryption completed in ${stopwatch.elapsed}");
|
||||
|
||||
stopwatch.reset();
|
||||
final sha512sumEncryptedChunk = await sha512.bind(Stream.fromIterable([encryptedChunk])).first;
|
||||
final sha512sumEncryptedChunk =
|
||||
await sha512.bind(Stream.fromIterable([encryptedChunk])).first;
|
||||
final sha512sumUnencryptedChunk = await sha512.bind(Stream.fromIterable([chunk])).first;
|
||||
printV("Hashing completed in ${stopwatch.elapsed}");
|
||||
|
||||
@@ -481,7 +485,7 @@ class BackupServiceV3 extends $BackupService {
|
||||
plain: chunk.length,
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
await dataBinWriter.flush();
|
||||
printV("Writing completed in ${stopwatch.elapsed}");
|
||||
}
|
||||
@@ -495,7 +499,8 @@ class BackupServiceV3 extends $BackupService {
|
||||
|
||||
metadataFile.writeAsStringSync(JsonEncoder.withIndent(' ').convert(metadata.toJson()));
|
||||
final readmeFile = File('${tmpDir.path}/README.txt');
|
||||
readmeFile.writeAsStringSync('''This is a ${packageInfo.appName} backup. Do not modify this archive.
|
||||
readmeFile
|
||||
.writeAsStringSync('''This is a ${packageInfo.appName} backup. Do not modify this archive.
|
||||
|
||||
App version: ${packageInfo.version}
|
||||
|
||||
@@ -524,11 +529,9 @@ This backup was created on ${DateTime.now().toIso8601String()}
|
||||
final sourceAppName = _extractBackupAppName(readmeString);
|
||||
if (sourceAppName == null) return;
|
||||
|
||||
|
||||
final currentAppName = (await PackageInfo.fromPlatform()).appName;
|
||||
if (sourceAppName == currentAppName) return;
|
||||
|
||||
|
||||
throw IncompatibleBackupAppException(
|
||||
sourceAppName: sourceAppName,
|
||||
currentAppName: currentAppName,
|
||||
|
||||
@@ -73,12 +73,8 @@ class CsvExportService {
|
||||
|
||||
// Prefer tx.to/tx.from; fall back to address lists for chains that don't populate them.
|
||||
final address = tx.direction == TransactionDirection.incoming
|
||||
? (tx.from?.isNotEmpty == true
|
||||
? tx.from!
|
||||
: (tx.inputAddresses?.firstOrNull ?? ''))
|
||||
: (tx.to?.isNotEmpty == true
|
||||
? tx.to!
|
||||
: (tx.outputAddresses?.firstOrNull ?? ''));
|
||||
? (tx.from?.isNotEmpty == true ? tx.from! : (tx.inputAddresses?.firstOrNull ?? ''))
|
||||
: (tx.to?.isNotEmpty == true ? tx.to! : (tx.outputAddresses?.firstOrNull ?? ''));
|
||||
|
||||
final fee = tx.fee != null && !tx.fee!.isZero ? tx.fee.toString() : '';
|
||||
|
||||
@@ -213,7 +209,8 @@ class CsvExportService {
|
||||
String _isoDate(DateTime dt) => dt.toUtc().toIso8601String();
|
||||
|
||||
Future<void> exportToCsv(List<ActionListItem> items, BuildContext context) async {
|
||||
final dataItems = items.whereType<ActionListItem>().where((e) => e is! DateSectionItem).toList();
|
||||
final dataItems =
|
||||
items.whereType<ActionListItem>().where((e) => e is! DateSectionItem).toList();
|
||||
|
||||
if (dataItems.isEmpty) {
|
||||
await showBar<void>(context, S.current.csv_nothing_to_export);
|
||||
|
||||
@@ -5,7 +5,6 @@ class EmailValidator extends TextValidator {
|
||||
EmailValidator()
|
||||
: super(
|
||||
errorMessage: 'Invalid email address',
|
||||
pattern:
|
||||
'^[^@]+@[^@]+\.[^@]+',
|
||||
pattern: '^[^@]+@[^@]+\.[^@]+',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,4 +25,4 @@ class AwaitingConfirmationState extends ExecutionState {
|
||||
final String? message;
|
||||
final Function()? onConfirm;
|
||||
final Function()? onCancel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ const _fiatApiOnionAuthority = '46wisoe2uwipcj2j4og6smiq7hbmj34fkkrquwlzbbsqtgat
|
||||
const _fiatApiPath = '/v2/rates';
|
||||
|
||||
Future<double> _fetchPrice(String crypto, String fiat, bool torOnly) async {
|
||||
|
||||
final Map<String, String> queryParams = {
|
||||
'interval_count': '1',
|
||||
'base': crypto.split(".").first,
|
||||
@@ -23,14 +22,10 @@ Future<double> _fetchPrice(String crypto, String fiat, bool torOnly) async {
|
||||
final onionUri = Uri.http(_fiatApiOnionAuthority, _fiatApiPath, queryParams);
|
||||
final clearnetUri = Uri.https(_fiatApiClearNetAuthority, _fiatApiPath, queryParams);
|
||||
|
||||
final response = await ProxyWrapper().get(
|
||||
onionUri: onionUri,
|
||||
clearnetUri: torOnly ? onionUri : clearnetUri,
|
||||
headers: {
|
||||
"x-api-key": secrets.fiatApiKey,
|
||||
}
|
||||
);
|
||||
|
||||
final response = await ProxyWrapper()
|
||||
.get(onionUri: onionUri, clearnetUri: torOnly ? onionUri : clearnetUri, headers: {
|
||||
"x-api-key": secrets.fiatApiKey,
|
||||
});
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return 0.0;
|
||||
@@ -52,9 +47,8 @@ Future<double> _fetchPrice(String crypto, String fiat, bool torOnly) async {
|
||||
/// Override specific [CryptoCurrency] to fix its price to the price of another
|
||||
/// e.g. nDEPS should have the same price as DEPS, but only DEPS is tracked
|
||||
CryptoCurrency _overrideCryptoCurrency(CryptoCurrency crypto) {
|
||||
if (crypto.title == CryptoCurrency.ndeps.title)
|
||||
return CryptoCurrency.deps;
|
||||
return crypto;
|
||||
if (crypto.title == CryptoCurrency.ndeps.title) return CryptoCurrency.deps;
|
||||
return crypto;
|
||||
}
|
||||
|
||||
class FiatConversionService {
|
||||
|
||||
@@ -14,4 +14,4 @@ int mnemonicLength(WalletType type) {
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import 'package:cake_wallet/core/validator.dart';
|
||||
class MoneroLabelValidator extends TextValidator {
|
||||
MoneroLabelValidator()
|
||||
: super(
|
||||
errorMessage: S.current.error_text_account_name,
|
||||
pattern: '^[a-zA-Z0-9_ ]{1,15}\$',
|
||||
minLength: 1,
|
||||
maxLength: 15);
|
||||
errorMessage: S.current.error_text_account_name,
|
||||
pattern: '^[a-zA-Z0-9_ ]{1,15}\$',
|
||||
minLength: 1,
|
||||
maxLength: 15);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,7 @@ import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
|
||||
class OpenCryptoPayService {
|
||||
static bool isOpenCryptoPayQR(String value) =>
|
||||
value.toLowerCase().contains("lightning=lnurl") ||
|
||||
value.toLowerCase().startsWith("lnurl");
|
||||
value.toLowerCase().contains("lightning=lnurl") || value.toLowerCase().startsWith("lnurl");
|
||||
|
||||
static bool requiresClientCommit(CryptoCurrency currency) =>
|
||||
[CryptoCurrency.xmr, CryptoCurrency.zano, CryptoCurrency.sol].contains(currency) ||
|
||||
@@ -71,8 +70,8 @@ class OpenCryptoPayService {
|
||||
);
|
||||
}
|
||||
|
||||
Future<(_OpenCryptoPayQuote, Map<String, List<OpenCryptoPayQuoteAsset>>)>
|
||||
_getOpenCryptoPayParams(Uri uri) async {
|
||||
Future<(_OpenCryptoPayQuote, Map<String, List<OpenCryptoPayQuoteAsset>>)> _getOpenCryptoPayParams(
|
||||
Uri uri) async {
|
||||
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
@@ -90,18 +89,15 @@ class OpenCryptoPayService {
|
||||
final method = transferAmount['method'] as String;
|
||||
methods[method] = [];
|
||||
for (final assetJson in transferAmount['assets'] as List) {
|
||||
final asset = OpenCryptoPayQuoteAsset.fromJson(
|
||||
assetJson as Map<String, dynamic>);
|
||||
final asset = OpenCryptoPayQuoteAsset.fromJson(assetJson as Map<String, dynamic>);
|
||||
methods[method]?.add(asset);
|
||||
}
|
||||
}
|
||||
|
||||
log(responseBody.toString());
|
||||
|
||||
final quote = _OpenCryptoPayQuote.fromJson(
|
||||
responseBody['callback'] as String,
|
||||
responseBody['displayName'] as String?,
|
||||
responseBody['quote'] as Map<String, dynamic>);
|
||||
final quote = _OpenCryptoPayQuote.fromJson(responseBody['callback'] as String,
|
||||
responseBody['displayName'] as String?, responseBody['quote'] as Map<String, dynamic>);
|
||||
|
||||
return (quote, methods);
|
||||
} else {
|
||||
@@ -110,8 +106,7 @@ class OpenCryptoPayService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uri> getOpenCryptoPayAddress(
|
||||
OpenCryptoPayRequest request, CryptoCurrency asset) async {
|
||||
Future<Uri> getOpenCryptoPayAddress(OpenCryptoPayRequest request, CryptoCurrency asset) async {
|
||||
final uri = Uri.parse(request.callbackUrl);
|
||||
final queryParams = Map.of(uri.queryParameters);
|
||||
|
||||
@@ -135,8 +130,7 @@ class OpenCryptoPayService {
|
||||
|
||||
if (result.queryParameters['amount'] != null) return result;
|
||||
|
||||
final newQueryParameters =
|
||||
Map<String, dynamic>.from(result.queryParameters);
|
||||
final newQueryParameters = Map<String, dynamic>.from(result.queryParameters);
|
||||
|
||||
newQueryParameters['amount'] = _getAmountByAsset(request, asset);
|
||||
return Uri(
|
||||
|
||||
@@ -22,8 +22,7 @@ class SeedValidator extends Validator<MnemonicItem> {
|
||||
final String language;
|
||||
final List<String> _words;
|
||||
|
||||
static List<String> getWordList(
|
||||
{required WalletType type, required String language}) {
|
||||
static List<String> getWordList({required WalletType type, required String language}) {
|
||||
switch (type) {
|
||||
case WalletType.bitcoin:
|
||||
return getBitcoinWordList(language);
|
||||
@@ -49,7 +48,7 @@ class SeedValidator extends Validator<MnemonicItem> {
|
||||
case WalletType.tron:
|
||||
return tron!.getTronWordList(language);
|
||||
case WalletType.wownero:
|
||||
return wownero!.getWowneroWordList(language);
|
||||
return wownero!.getWowneroWordList(language);
|
||||
case WalletType.zano:
|
||||
return zano!.getWordList(language);
|
||||
case WalletType.decred:
|
||||
|
||||
@@ -5,7 +5,6 @@ abstract class SelectableItem {
|
||||
|
||||
class OptionTitle extends SelectableItem {
|
||||
OptionTitle({required String title}) : super(title: title);
|
||||
|
||||
}
|
||||
|
||||
abstract class SelectableOption extends SelectableItem {
|
||||
@@ -43,5 +42,3 @@ abstract class SelectableOption extends SelectableItem {
|
||||
|
||||
set isOptionSelected(bool isSelected) => false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
class SocksProxyNodeAddressValidator extends TextValidator {
|
||||
SocksProxyNodeAddressValidator()
|
||||
: super(
|
||||
errorMessage: S.current.error_text_node_proxy_address,
|
||||
pattern:
|
||||
'^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]+\$');
|
||||
errorMessage: S.current.error_text_node_proxy_address,
|
||||
pattern: '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]+\$');
|
||||
}
|
||||
|
||||
@@ -4,9 +4,8 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
class TemplateValidator extends TextValidator {
|
||||
TemplateValidator()
|
||||
: super(
|
||||
minLength: 0,
|
||||
maxLength: 0,
|
||||
pattern: '''^[^`,'"]{1,20}\$''',
|
||||
errorMessage: S.current.error_text_template
|
||||
);
|
||||
}
|
||||
minLength: 0,
|
||||
maxLength: 0,
|
||||
pattern: '''^[^`,'"]{1,20}\$''',
|
||||
errorMessage: S.current.error_text_template);
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ class UniversalAddressDetector {
|
||||
pattern: RegExp(r'^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'),
|
||||
currency: CryptoCurrency.btcln,
|
||||
),
|
||||
|
||||
|
||||
// Lightning Address (LNURL format)
|
||||
_DetectionPattern(
|
||||
pattern: RegExp(r'^(lightning:)?(lnurl)[a-z0-9]+$', caseSensitive: false),
|
||||
|
||||
@@ -8,8 +8,7 @@ extension EnhancedList<T> on Iterable<T> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String middleTruncate(String s, int head, int tail) {
|
||||
if (s.length <= head + tail + 3) return s;
|
||||
return s.substring(0, head) + '...' + s.substring(s.length - tail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ class TextValidator extends Validator<String> {
|
||||
String errorMessage = '',
|
||||
this.length,
|
||||
this.isAutovalidate = false,
|
||||
}) : super(
|
||||
errorMessage: errorMessage,
|
||||
useAdditionalValidation: useAdditionalValidation);
|
||||
}) : super(errorMessage: errorMessage, useAdditionalValidation: useAdditionalValidation);
|
||||
|
||||
final int? minLength;
|
||||
final int? maxLength;
|
||||
@@ -40,8 +38,7 @@ class TextValidator extends Validator<String> {
|
||||
final lengthMatched = length?.contains(value.length) ?? true;
|
||||
if (!lengthMatched) return false;
|
||||
|
||||
final lowerThanMaxLength =
|
||||
(maxLength ?? 0) > 0 ? (value.length <= maxLength!) : true;
|
||||
final lowerThanMaxLength = (maxLength ?? 0) > 0 ? (value.length <= maxLength!) : true;
|
||||
if (!lowerThanMaxLength) return false;
|
||||
|
||||
if (pattern == null) return true;
|
||||
@@ -54,6 +51,5 @@ class TextValidator extends Validator<String> {
|
||||
return valueValidated;
|
||||
}
|
||||
|
||||
bool match(String value) =>
|
||||
pattern != null ? RegExp(pattern!).hasMatch(value) : false;
|
||||
bool match(String value) => pattern != null ? RegExp(pattern!).hasMatch(value) : false;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@ class WalletCreationService {
|
||||
|
||||
Future<bool> exists(String name) async {
|
||||
final walletName = name.toLowerCase();
|
||||
return (await WalletInfo.getAll()).any((walletInfo) => walletInfo.name.toLowerCase() == walletName);
|
||||
return (await WalletInfo.getAll())
|
||||
.any((walletInfo) => walletInfo.name.toLowerCase() == walletName);
|
||||
}
|
||||
|
||||
Future<bool> typeExists(WalletType type) async {
|
||||
|
||||
@@ -10,4 +10,4 @@ class WalletCreationFailure extends WalletCreationState {
|
||||
WalletCreationFailure({required this.error});
|
||||
|
||||
final String error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,10 +58,12 @@ class WalletLoadingService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<WalletBase> load(WalletType type, String name, {String? password, bool isBackground = false}) async {
|
||||
Future<WalletBase> load(WalletType type, String name,
|
||||
{String? password, bool isBackground = false}) async {
|
||||
try {
|
||||
if (!isBackground) {
|
||||
await sharedPreferences.setString(PreferencesKey.backgroundSyncLastTrigger(name), DateTime.now().toIso8601String());
|
||||
await sharedPreferences.setString(
|
||||
PreferencesKey.backgroundSyncLastTrigger(name), DateTime.now().toIso8601String());
|
||||
}
|
||||
final walletService = walletServiceFactory.call(type);
|
||||
final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name));
|
||||
@@ -75,10 +77,14 @@ class WalletLoadingService {
|
||||
} catch (error, stack) {
|
||||
String corruptedWalletsSeeds = "Corrupted wallets seeds (if retrievable, empty otherwise):";
|
||||
|
||||
if(error is WalletDeprecationException) {
|
||||
if(navigatorKey.currentContext != null) {
|
||||
if (error is WalletDeprecationException) {
|
||||
if (navigatorKey.currentContext != null) {
|
||||
showModalBottomSheet(
|
||||
context: navigatorKey.currentContext!, builder: (context)=>WalletDeprecationPopup(type: type, seed: error.seed,));
|
||||
context: navigatorKey.currentContext!,
|
||||
builder: (context) => WalletDeprecationPopup(
|
||||
type: type,
|
||||
seed: error.seed,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
await ExceptionHandler.resetLastPopupDate();
|
||||
@@ -106,8 +112,7 @@ class WalletLoadingService {
|
||||
await updateMoneroWalletPassword(wallet);
|
||||
}
|
||||
|
||||
await sharedPreferences.setString(
|
||||
PreferencesKey.currentWalletName, wallet.name);
|
||||
await sharedPreferences.setString(PreferencesKey.currentWalletName, wallet.name);
|
||||
await sharedPreferences.setInt(
|
||||
PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
||||
|
||||
@@ -130,22 +135,22 @@ class WalletLoadingService {
|
||||
// if all user's wallets are corrupted throw exception
|
||||
final msg = error.toString() + "\n" + corruptedWalletsSeeds;
|
||||
if (navigatorKey.currentContext != null) {
|
||||
await showPopUp<void>(
|
||||
context: navigatorKey.currentContext!,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: "Corrupted seeds",
|
||||
alertContent: S.of(context).corrupted_seed_notice,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).show_seed,
|
||||
actionLeftButton: () {
|
||||
if (context.mounted && Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
actionRightButton: () => showSeedsPopup(context, msg),
|
||||
);
|
||||
});
|
||||
await showPopUp<void>(
|
||||
context: navigatorKey.currentContext!,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: "Corrupted seeds",
|
||||
alertContent: S.of(context).corrupted_seed_notice,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).show_seed,
|
||||
actionLeftButton: () {
|
||||
if (context.mounted && Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
actionRightButton: () => showSeedsPopup(context, msg),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
throw msg;
|
||||
}
|
||||
@@ -159,21 +164,21 @@ class WalletLoadingService {
|
||||
Future<void> showSeedsPopup(BuildContext context, String message) async {
|
||||
Navigator.of(context).pop();
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: "Corrupted seeds",
|
||||
alertContent: message,
|
||||
leftButtonText: S.of(context).copy,
|
||||
rightButtonText: S.of(context).ok,
|
||||
actionLeftButton: () async {
|
||||
await Clipboard.setData(ClipboardData(text: message));
|
||||
},
|
||||
actionRightButton: () async {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
});
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: "Corrupted seeds",
|
||||
alertContent: message,
|
||||
leftButtonText: S.of(context).copy,
|
||||
rightButtonText: S.of(context).ok,
|
||||
actionLeftButton: () async {
|
||||
await Clipboard.setData(ClipboardData(text: message));
|
||||
},
|
||||
actionRightButton: () async {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
|
||||
|
||||
Reference in New Issue
Block a user