mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-22 02:34:59 +00:00
Merge branch 'main' into CW-537-Integrate-ThorChain-swaps
This commit is contained in:
commit
e28b0bb895
75 changed files with 1659 additions and 711 deletions
|
@ -1,6 +1,6 @@
|
|||
Privacy Policy
|
||||
|
||||
Last modified: August 9, 2023
|
||||
Last modified: January 24, 2024
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
@ -112,12 +112,12 @@ Data Security
|
|||
|
||||
In any situation, Cake Labs takes no responsibility for interception of personal data by any outside individual, group, corporation, or institution. You should understand this and take any and all appropriate actions to secure your own data.
|
||||
|
||||
Links to Other Websites
|
||||
-----------------------
|
||||
Other Websites and Third-Party Services
|
||||
---------------------------------------
|
||||
|
||||
The App may contain links to other websites that are not operated by us. If you click on a Third-Party Service link, you will be directed to that third party's site. We strongly advise you to review the Privacy Policy of every site you visit. We have no control over and assume no responsibility for the content, privacy policies or practices of any third-party sites or services.
|
||||
|
||||
The App includes several optional Third-Party Services, which may not be available to all users. If you use Third-Party Services, you must agree to their respective Privacy Policies.
|
||||
The App includes several optional Third-Party Services, which may not be available to all users. If you use Third-Party Services, you must agree to their respective Privacy Policies. When using certain optional features in the app such as buying and selling, you may be asked to provide information to a Third-Party Service. You will need to read and accept the privacy policy for that third party. This Third-Party Service may ask for your name, your photo ID, your social security number or other similar number, mailing address, cryptocurrency address, or other information. They may ask you to take a selfie image. Information shared with a Third-Party Service is subject to their respective Privacy Policies.
|
||||
|
||||
Changes to Our Privacy Policy
|
||||
-----------------------------
|
||||
|
|
|
@ -1,40 +1,70 @@
|
|||
import 'dart:convert';
|
||||
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||
|
||||
class BitcoinAddressRecord {
|
||||
BitcoinAddressRecord(this.address,
|
||||
{required this.index, this.isHidden = false, bool isUsed = false})
|
||||
: _isUsed = isUsed;
|
||||
BitcoinAddressRecord(
|
||||
this.address, {
|
||||
required this.index,
|
||||
this.isHidden = false,
|
||||
int txCount = 0,
|
||||
int balance = 0,
|
||||
String name = '',
|
||||
bool isUsed = false,
|
||||
}) : _txCount = txCount,
|
||||
_balance = balance,
|
||||
_name = name,
|
||||
_isUsed = isUsed;
|
||||
|
||||
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
||||
return BitcoinAddressRecord(
|
||||
decoded['address'] as String,
|
||||
index: decoded['index'] as int,
|
||||
isHidden: decoded['isHidden'] as bool? ?? false,
|
||||
isUsed: decoded['isUsed'] as bool? ?? false);
|
||||
return BitcoinAddressRecord(decoded['address'] as String,
|
||||
index: decoded['index'] as int,
|
||||
isHidden: decoded['isHidden'] as bool? ?? false,
|
||||
isUsed: decoded['isUsed'] as bool? ?? false,
|
||||
txCount: decoded['txCount'] as int? ?? 0,
|
||||
name: decoded['name'] as String? ?? '',
|
||||
balance: decoded['balance'] as int? ?? 0);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) =>
|
||||
o is BitcoinAddressRecord && address == o.address;
|
||||
|
||||
final String address;
|
||||
final bool isHidden;
|
||||
final int index;
|
||||
int _txCount;
|
||||
int _balance;
|
||||
String _name;
|
||||
bool _isUsed;
|
||||
|
||||
int get txCount => _txCount;
|
||||
|
||||
String get name => _name;
|
||||
|
||||
int get balance => _balance;
|
||||
|
||||
set txCount(int value) => _txCount = value;
|
||||
|
||||
set balance(int value) => _balance = value;
|
||||
|
||||
bool get isUsed => _isUsed;
|
||||
|
||||
void setAsUsed() => _isUsed = true;
|
||||
void setNewName(String label) => _name = label;
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address;
|
||||
|
||||
@override
|
||||
int get hashCode => address.hashCode;
|
||||
|
||||
bool _isUsed;
|
||||
String get cashAddr => bitbox.Address.toCashAddress(address);
|
||||
|
||||
void setAsUsed() => _isUsed = true;
|
||||
|
||||
String toJSON() =>
|
||||
json.encode({
|
||||
String toJSON() => json.encode({
|
||||
'address': address,
|
||||
'index': index,
|
||||
'isHidden': isHidden,
|
||||
'isUsed': isUsed});
|
||||
'txCount': txCount,
|
||||
'name': name,
|
||||
'isUsed': isUsed,
|
||||
'balance': balance,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -47,6 +47,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
|
||||
.derivePath("m/0'/1"),
|
||||
networkType: networkType);
|
||||
autorun((_) {
|
||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||
});
|
||||
}
|
||||
|
||||
static Future<BitcoinWallet> create({
|
||||
|
|
|
@ -63,6 +63,7 @@ abstract class ElectrumWalletBase
|
|||
_password = password,
|
||||
_feeRates = <int>[],
|
||||
_isTransactionUpdating = false,
|
||||
isEnabledAutoGenerateSubaddress = true,
|
||||
unspentCoins = [],
|
||||
_scripthashesUpdateSubject = {},
|
||||
balance = ObservableMap<CryptoCurrency, ElectrumBalance>.of(currency != null
|
||||
|
@ -87,6 +88,10 @@ abstract class ElectrumWalletBase
|
|||
final bitcoin.HDWallet hd;
|
||||
final String mnemonic;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isEnabledAutoGenerateSubaddress;
|
||||
|
||||
late ElectrumClient electrumClient;
|
||||
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
||||
|
||||
|
@ -588,38 +593,66 @@ abstract class ElectrumWalletBase
|
|||
Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
|
||||
final addressHashes = <String, BitcoinAddressRecord>{};
|
||||
final normalizedHistories = <Map<String, dynamic>>[];
|
||||
final newTxCounts = <String, int>{};
|
||||
|
||||
walletAddresses.addresses.forEach((addressRecord) {
|
||||
final sh = scriptHash(addressRecord.address, networkType: networkType);
|
||||
addressHashes[sh] = addressRecord;
|
||||
newTxCounts[sh] = 0;
|
||||
});
|
||||
final histories = addressHashes.keys.map((scriptHash) =>
|
||||
electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
|
||||
final historyResults = await Future.wait(histories);
|
||||
historyResults.forEach((history) {
|
||||
history.entries.forEach((historyItem) {
|
||||
if (historyItem.value.isNotEmpty) {
|
||||
final address = addressHashes[historyItem.key];
|
||||
address?.setAsUsed();
|
||||
normalizedHistories.addAll(historyItem.value);
|
||||
}
|
||||
|
||||
try {
|
||||
final histories = addressHashes.keys.map((scriptHash) =>
|
||||
electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
|
||||
final historyResults = await Future.wait(histories);
|
||||
|
||||
|
||||
|
||||
historyResults.forEach((history) {
|
||||
history.entries.forEach((historyItem) {
|
||||
if (historyItem.value.isNotEmpty) {
|
||||
final address = addressHashes[historyItem.key];
|
||||
address?.setAsUsed();
|
||||
newTxCounts[historyItem.key] = historyItem.value.length;
|
||||
normalizedHistories.addAll(historyItem.value);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
final historiesWithDetails = await Future.wait(normalizedHistories.map((transaction) {
|
||||
try {
|
||||
return fetchTransactionInfo(
|
||||
hash: transaction['tx_hash'] as String, height: transaction['height'] as int);
|
||||
} catch (_) {
|
||||
return Future.value(null);
|
||||
|
||||
for (var sh in addressHashes.keys) {
|
||||
var balanceData = await electrumClient.getBalance(sh);
|
||||
var addressRecord = addressHashes[sh];
|
||||
if (addressRecord != null) {
|
||||
addressRecord.balance = balanceData['confirmed'] as int? ?? 0;
|
||||
}
|
||||
}
|
||||
}));
|
||||
return historiesWithDetails
|
||||
.fold<Map<String, ElectrumTransactionInfo>>(<String, ElectrumTransactionInfo>{}, (acc, tx) {
|
||||
if (tx == null) {
|
||||
|
||||
|
||||
addressHashes.forEach((sh, addressRecord) {
|
||||
addressRecord.txCount = newTxCounts[sh] ?? 0;
|
||||
});
|
||||
|
||||
final historiesWithDetails = await Future.wait(normalizedHistories.map((transaction) {
|
||||
try {
|
||||
return fetchTransactionInfo(
|
||||
hash: transaction['tx_hash'] as String, height: transaction['height'] as int);
|
||||
} catch (_) {
|
||||
return Future.value(null);
|
||||
}
|
||||
}));
|
||||
|
||||
return historiesWithDetails.fold<Map<String, ElectrumTransactionInfo>>(
|
||||
<String, ElectrumTransactionInfo>{}, (acc, tx) {
|
||||
if (tx == null) {
|
||||
return acc;
|
||||
}
|
||||
acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx;
|
||||
return acc;
|
||||
}
|
||||
acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx;
|
||||
return acc;
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateTransactions() async {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/electrum.dart';
|
||||
import 'package:cw_bitcoin/script_hash.dart';
|
||||
|
@ -10,8 +10,7 @@ import 'package:mobx/mobx.dart';
|
|||
|
||||
part 'electrum_wallet_addresses.g.dart';
|
||||
|
||||
class ElectrumWalletAddresses = ElectrumWalletAddressesBase
|
||||
with _$ElectrumWalletAddresses;
|
||||
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
|
||||
|
||||
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
ElectrumWalletAddressesBase(WalletInfo walletInfo,
|
||||
|
@ -22,19 +21,16 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
int initialRegularAddressIndex = 0,
|
||||
int initialChangeAddressIndex = 0})
|
||||
: addresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? []).toSet()),
|
||||
receiveAddresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? [])
|
||||
: addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
|
||||
receiveAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed)
|
||||
.toSet()),
|
||||
changeAddresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? [])
|
||||
.toSet()),
|
||||
changeAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed)
|
||||
.toSet()),
|
||||
.toSet()),
|
||||
currentReceiveAddressIndex = initialRegularAddressIndex,
|
||||
currentChangeAddressIndex = initialChangeAddressIndex,
|
||||
super(walletInfo);
|
||||
super(walletInfo);
|
||||
|
||||
static const defaultReceiveAddressesCount = 22;
|
||||
static const defaultChangeAddressesCount = 17;
|
||||
|
@ -42,6 +38,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
static String toCashAddr(String address) => bitbox.Address.toCashAddress(address);
|
||||
|
||||
static String toLegacy(String address) => bitbox.Address.toLegacyAddress(address);
|
||||
|
||||
final ObservableList<BitcoinAddressRecord> addresses;
|
||||
final ObservableList<BitcoinAddressRecord> receiveAddresses;
|
||||
final ObservableList<BitcoinAddressRecord> changeAddresses;
|
||||
|
@ -53,41 +51,67 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
@override
|
||||
@computed
|
||||
String get address {
|
||||
if (receiveAddresses.isEmpty) {
|
||||
final address = generateNewAddress().address;
|
||||
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(address) : address;
|
||||
}
|
||||
final receiveAddress = receiveAddresses.first.address;
|
||||
if (isEnabledAutoGenerateSubaddress) {
|
||||
if (receiveAddresses.isEmpty) {
|
||||
final newAddress = generateNewAddress().address;
|
||||
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(newAddress) : newAddress;
|
||||
}
|
||||
final receiveAddress = receiveAddresses.first.address;
|
||||
|
||||
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(receiveAddress) : receiveAddress;
|
||||
return walletInfo.type == WalletType.bitcoinCash
|
||||
? toCashAddr(receiveAddress)
|
||||
: receiveAddress;
|
||||
} else {
|
||||
final receiveAddress = (receiveAddresses.first.address != addresses.first.address &&
|
||||
previousAddressRecord != null)
|
||||
? previousAddressRecord!.address
|
||||
: addresses.first.address;
|
||||
|
||||
return walletInfo.type == WalletType.bitcoinCash
|
||||
? toCashAddr(receiveAddress)
|
||||
: receiveAddress;
|
||||
}
|
||||
}
|
||||
|
||||
@observable
|
||||
bool isEnabledAutoGenerateSubaddress = true;
|
||||
|
||||
@override
|
||||
set address(String addr) {
|
||||
if (addr.startsWith('bitcoincash:')) {
|
||||
addr = toLegacy(addr);
|
||||
}
|
||||
final addressRecord = addresses.firstWhere((addressRecord) => addressRecord.address == addr);
|
||||
|
||||
previousAddressRecord = addressRecord;
|
||||
receiveAddresses.remove(addressRecord);
|
||||
receiveAddresses.insert(0, addressRecord);
|
||||
}
|
||||
|
||||
@override
|
||||
String get primaryAddress => getAddress(index: 0, hd: mainHd);
|
||||
|
||||
@override
|
||||
set address(String addr) => null;
|
||||
|
||||
int currentReceiveAddressIndex;
|
||||
int currentChangeAddressIndex;
|
||||
|
||||
@computed
|
||||
int get totalCountOfReceiveAddresses =>
|
||||
addresses.fold(0, (acc, addressRecord) {
|
||||
if (!addressRecord.isHidden) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
@observable
|
||||
BitcoinAddressRecord? previousAddressRecord;
|
||||
|
||||
@computed
|
||||
int get totalCountOfChangeAddresses =>
|
||||
addresses.fold(0, (acc, addressRecord) {
|
||||
if (addressRecord.isHidden) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
int get totalCountOfReceiveAddresses => addresses.fold(0, (acc, addressRecord) {
|
||||
if (!addressRecord.isHidden) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
|
||||
@computed
|
||||
int get totalCountOfChangeAddresses => addresses.fold(0, (acc, addressRecord) {
|
||||
if (addressRecord.isHidden) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
|
||||
Future<void> discoverAddresses() async {
|
||||
await _discoverAddresses(mainHd, false);
|
||||
|
@ -117,11 +141,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
if (changeAddresses.isEmpty) {
|
||||
final newAddresses = await _createNewAddresses(gap,
|
||||
hd: sideHd,
|
||||
startIndex: totalCountOfChangeAddresses > 0
|
||||
? totalCountOfChangeAddresses - 1
|
||||
: 0,
|
||||
isHidden: true);
|
||||
hd: sideHd,
|
||||
startIndex: totalCountOfChangeAddresses > 0 ? totalCountOfChangeAddresses - 1 : 0,
|
||||
isHidden: true);
|
||||
_addAddresses(newAddresses);
|
||||
}
|
||||
|
||||
|
@ -135,14 +157,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
return address;
|
||||
}
|
||||
|
||||
BitcoinAddressRecord generateNewAddress(
|
||||
{bitcoin.HDWallet? hd, bool isHidden = false}) {
|
||||
currentReceiveAddressIndex += 1;
|
||||
// FIX-ME: Check logic for whichi HD should be used here ???
|
||||
final address = BitcoinAddressRecord(
|
||||
getAddress(index: currentReceiveAddressIndex, hd: hd ?? sideHd),
|
||||
index: currentReceiveAddressIndex,
|
||||
isHidden: isHidden);
|
||||
BitcoinAddressRecord generateNewAddress({bitcoin.HDWallet? hd, String? label}) {
|
||||
final isHidden = hd == sideHd;
|
||||
|
||||
final newAddressIndex = addresses.fold(
|
||||
0, (int acc, addressRecord) => isHidden == addressRecord.isHidden ? acc + 1 : acc);
|
||||
|
||||
final address = BitcoinAddressRecord(getAddress(index: newAddressIndex, hd: hd ?? sideHd),
|
||||
index: newAddressIndex, isHidden: isHidden, name: label ?? '');
|
||||
addresses.add(address);
|
||||
return address;
|
||||
}
|
||||
|
@ -160,20 +182,32 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void updateAddress(String address, String label) {
|
||||
if (address.startsWith('bitcoincash:')) {
|
||||
address = toLegacy(address);
|
||||
}
|
||||
final addressRecord = addresses.firstWhere((addressRecord) => addressRecord.address == address);
|
||||
addressRecord.setNewName(label);
|
||||
final index = addresses.indexOf(addressRecord);
|
||||
addresses.remove(addressRecord);
|
||||
addresses.insert(index, addressRecord);
|
||||
}
|
||||
|
||||
@action
|
||||
void updateReceiveAddresses() {
|
||||
receiveAddresses.removeRange(0, receiveAddresses.length);
|
||||
final newAdresses = addresses
|
||||
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
|
||||
receiveAddresses.addAll(newAdresses);
|
||||
final newAddresses =
|
||||
addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
|
||||
receiveAddresses.addAll(newAddresses);
|
||||
}
|
||||
|
||||
@action
|
||||
void updateChangeAddresses() {
|
||||
changeAddresses.removeRange(0, changeAddresses.length);
|
||||
final newAdresses = addresses
|
||||
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
|
||||
changeAddresses.addAll(newAdresses);
|
||||
final newAddresses =
|
||||
addresses.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
|
||||
changeAddresses.addAll(newAddresses);
|
||||
}
|
||||
|
||||
Future<void> _discoverAddresses(bitcoin.HDWallet hd, bool isHidden) async {
|
||||
|
@ -181,20 +215,16 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
List<BitcoinAddressRecord> addrs;
|
||||
|
||||
if (addresses.isNotEmpty) {
|
||||
addrs = addresses
|
||||
.where((addr) => addr.isHidden == isHidden)
|
||||
.toList();
|
||||
addrs = addresses.where((addr) => addr.isHidden == isHidden).toList();
|
||||
} else {
|
||||
addrs = await _createNewAddresses(
|
||||
isHidden
|
||||
? defaultChangeAddressesCount
|
||||
: defaultReceiveAddressesCount,
|
||||
isHidden ? defaultChangeAddressesCount : defaultReceiveAddressesCount,
|
||||
startIndex: 0,
|
||||
hd: hd,
|
||||
isHidden: isHidden);
|
||||
}
|
||||
|
||||
while(hasAddrUse) {
|
||||
while (hasAddrUse) {
|
||||
final addr = addrs.last.address;
|
||||
hasAddrUse = await _hasAddressUsed(addr);
|
||||
|
||||
|
@ -204,11 +234,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
final start = addrs.length;
|
||||
final count = start + gap;
|
||||
final batch = await _createNewAddresses(
|
||||
count,
|
||||
startIndex: start,
|
||||
hd: hd,
|
||||
isHidden: isHidden);
|
||||
final batch = await _createNewAddresses(count, startIndex: start, hd: hd, isHidden: isHidden);
|
||||
addrs.addAll(batch);
|
||||
}
|
||||
|
||||
|
@ -232,21 +258,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
||||
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
||||
final newAddresses = await _createNewAddresses(
|
||||
addressesCount,
|
||||
startIndex: countOfReceiveAddresses,
|
||||
hd: mainHd,
|
||||
isHidden: false);
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfReceiveAddresses, hd: mainHd, isHidden: false);
|
||||
addresses.addAll(newAddresses);
|
||||
}
|
||||
|
||||
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
||||
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
||||
final newAddresses = await _createNewAddresses(
|
||||
addressesCount,
|
||||
startIndex: countOfHiddenAddresses,
|
||||
hd: sideHd,
|
||||
isHidden: true);
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfHiddenAddresses, hd: sideHd, isHidden: true);
|
||||
addresses.addAll(newAddresses);
|
||||
}
|
||||
}
|
||||
|
@ -256,10 +276,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
final list = <BitcoinAddressRecord>[];
|
||||
|
||||
for (var i = startIndex; i < count + startIndex; i++) {
|
||||
final address = BitcoinAddressRecord(
|
||||
getAddress(index: i, hd: hd),
|
||||
index: i,
|
||||
isHidden: isHidden);
|
||||
final address =
|
||||
BitcoinAddressRecord(getAddress(index: i, hd: hd), index: i, isHidden: isHidden);
|
||||
list.add(address);
|
||||
}
|
||||
|
||||
|
@ -278,4 +296,4 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
final transactionHistory = await electrumClient.getHistory(sh);
|
||||
return transactionHistory.isNotEmpty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
.fromSeed(seedBytes, network: networkType)
|
||||
.derivePath("m/0'/1"),
|
||||
networkType: networkType,);
|
||||
autorun((_) {
|
||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||
});
|
||||
}
|
||||
|
||||
static Future<LitecoinWallet> create({
|
||||
|
|
|
@ -58,6 +58,9 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes)
|
||||
.derivePath("m/44'/145'/0'/1"),
|
||||
networkType: networkType);
|
||||
autorun((_) {
|
||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -202,11 +202,15 @@ I/flutter ( 4474): Gas Used: 53000
|
|||
Future<ERC20Balance> fetchERC20Balances(
|
||||
EthereumAddress userAddress, String contractAddress) async {
|
||||
final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
|
||||
final balance = await erc20.balanceOf(userAddress);
|
||||
try {
|
||||
final balance = await erc20.balanceOf(userAddress);
|
||||
|
||||
int exponent = (await erc20.decimals()).toInt();
|
||||
int exponent = (await erc20.decimals()).toInt();
|
||||
|
||||
return ERC20Balance(balance, exponent: exponent);
|
||||
return ERC20Balance(balance, exponent: exponent);
|
||||
} catch (_) {
|
||||
return ERC20Balance(BigInt.zero);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Erc20Token?> getErc20Token(String contractAddress) async {
|
||||
|
|
|
@ -63,9 +63,17 @@ class CWBitcoin extends Bitcoin {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> generateNewAddress(Object wallet) async {
|
||||
Future<void> generateNewAddress(Object wallet, String label) async {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
await bitcoinWallet.walletAddresses.generateNewAddress();
|
||||
await bitcoinWallet.walletAddresses.generateNewAddress(label: label);
|
||||
await wallet.save();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAddress(Object wallet,String address, String label) async {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
bitcoinWallet.walletAddresses.updateAddress(address, label);
|
||||
await wallet.save();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -100,6 +108,21 @@ class CWBitcoin extends Bitcoin {
|
|||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
@computed
|
||||
List<ElectrumSubAddress> getSubAddresses(Object wallet) {
|
||||
final electrumWallet = wallet as ElectrumWallet;
|
||||
return electrumWallet.walletAddresses.addresses
|
||||
.map((BitcoinAddressRecord addr) => ElectrumSubAddress(
|
||||
id: addr.index,
|
||||
name: addr.name,
|
||||
address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address,
|
||||
txCount: addr.txCount,
|
||||
balance: addr.balance,
|
||||
isChange: addr.isHidden))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
String getAddress(Object wallet) {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
|
|
|
@ -46,6 +46,8 @@ class DFXBuyProvider extends BuyProvider {
|
|||
return 'XMR';
|
||||
case WalletType.ethereum:
|
||||
return 'ETH';
|
||||
case WalletType.polygon:
|
||||
return 'MATIC';
|
||||
default:
|
||||
throw Exception("WalletType is not available for DFX ${wallet.type}");
|
||||
}
|
||||
|
@ -61,6 +63,8 @@ class DFXBuyProvider extends BuyProvider {
|
|||
return 'Monero';
|
||||
case WalletType.ethereum:
|
||||
return 'Ethereum';
|
||||
case WalletType.polygon:
|
||||
return 'Polygon';
|
||||
default:
|
||||
throw Exception("WalletType is not available for DFX ${wallet.type}");
|
||||
}
|
||||
|
@ -141,6 +145,7 @@ class DFXBuyProvider extends BuyProvider {
|
|||
String getSignature(String message) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return wallet.signMessage(message);
|
||||
case WalletType.monero:
|
||||
case WalletType.litecoin:
|
||||
|
|
|
@ -72,11 +72,11 @@ class AuthService with Store {
|
|||
|
||||
void saveLastAuthTime() {
|
||||
int timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
sharedPreferences.setInt(PreferencesKey.lastAuthTimeMilliseconds, timestamp);
|
||||
secureStorage.write(key: SecureKey.lastAuthTimeMilliseconds, value: timestamp.toString());
|
||||
}
|
||||
|
||||
bool requireAuth() {
|
||||
final timestamp = sharedPreferences.getInt(PreferencesKey.lastAuthTimeMilliseconds);
|
||||
Future<bool> requireAuth() async {
|
||||
final timestamp = int.tryParse(await secureStorage.read(key: SecureKey.lastAuthTimeMilliseconds) ?? '0');
|
||||
final duration = _durationToRequireAuth(timestamp ?? 0);
|
||||
final requiredPinInterval = settingsStore.pinTimeOutDuration;
|
||||
|
||||
|
@ -100,7 +100,7 @@ class AuthService with Store {
|
|||
'Either route or onAuthSuccess param must be passed.');
|
||||
|
||||
if (!conditionToDetermineIfToUse2FA) {
|
||||
if (!requireAuth() && !_alwaysAuthenticateRoutes.contains(route)) {
|
||||
if (!(await requireAuth()) && !_alwaysAuthenticateRoutes.contains(route)) {
|
||||
if (onAuthSuccess != null) {
|
||||
onAuthSuccess(true);
|
||||
} else {
|
||||
|
|
|
@ -213,8 +213,6 @@ class BackupService {
|
|||
final defaultBuyProvider = data[PreferencesKey.defaultBuyProvider] as int?;
|
||||
final currentTransactionPriorityKeyLegacy =
|
||||
data[PreferencesKey.currentTransactionPriorityKeyLegacy] as int?;
|
||||
final allowBiometricalAuthentication =
|
||||
data[PreferencesKey.allowBiometricalAuthenticationKey] as bool?;
|
||||
final currentBitcoinElectrumSererId =
|
||||
data[PreferencesKey.currentBitcoinElectrumSererIdKey] as int?;
|
||||
final currentLanguageCode = data[PreferencesKey.currentLanguageCode] as String?;
|
||||
|
@ -227,23 +225,6 @@ class BackupService {
|
|||
data[PreferencesKey.currentDefaultSettingsMigrationVersion] as int?;
|
||||
final moneroTransactionPriority = data[PreferencesKey.moneroTransactionPriority] as int?;
|
||||
final bitcoinTransactionPriority = data[PreferencesKey.bitcoinTransactionPriority] as int?;
|
||||
final selectedCake2FAPreset = data[PreferencesKey.selectedCake2FAPreset] as int?;
|
||||
final shouldRequireTOTP2FAForAccessingWallet =
|
||||
data[PreferencesKey.shouldRequireTOTP2FAForAccessingWallet] as bool?;
|
||||
final shouldRequireTOTP2FAForSendsToContact =
|
||||
data[PreferencesKey.shouldRequireTOTP2FAForSendsToContact] as bool?;
|
||||
final shouldRequireTOTP2FAForSendsToNonContact =
|
||||
data[PreferencesKey.shouldRequireTOTP2FAForSendsToNonContact] as bool?;
|
||||
final shouldRequireTOTP2FAForSendsToInternalWallets =
|
||||
data[PreferencesKey.shouldRequireTOTP2FAForSendsToInternalWallets] as bool?;
|
||||
final shouldRequireTOTP2FAForExchangesToInternalWallets =
|
||||
data[PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets] as bool?;
|
||||
final shouldRequireTOTP2FAForAddingContacts =
|
||||
data[PreferencesKey.shouldRequireTOTP2FAForAddingContacts] as bool?;
|
||||
final shouldRequireTOTP2FAForCreatingNewWallets =
|
||||
data[PreferencesKey.shouldRequireTOTP2FAForCreatingNewWallets] as bool?;
|
||||
final shouldRequireTOTP2FAForAllSecurityAndBackupSettings =
|
||||
data[PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings] as bool?;
|
||||
final sortBalanceTokensBy = data[PreferencesKey.sortBalanceBy] as int?;
|
||||
final pinNativeTokenAtTop = data[PreferencesKey.pinNativeTokenAtTop] as bool?;
|
||||
final useEtherscan = data[PreferencesKey.useEtherscan] as bool?;
|
||||
|
@ -294,14 +275,7 @@ class BackupService {
|
|||
if (currentTransactionPriorityKeyLegacy != null)
|
||||
await _sharedPreferences.setInt(
|
||||
PreferencesKey.currentTransactionPriorityKeyLegacy, currentTransactionPriorityKeyLegacy);
|
||||
|
||||
if (DeviceInfo.instance.isDesktop) {
|
||||
await _sharedPreferences.setBool(PreferencesKey.allowBiometricalAuthenticationKey, false);
|
||||
} else if (allowBiometricalAuthentication != null) {
|
||||
await _sharedPreferences.setBool(
|
||||
PreferencesKey.allowBiometricalAuthenticationKey, allowBiometricalAuthentication);
|
||||
}
|
||||
|
||||
|
||||
if (currentBitcoinElectrumSererId != null)
|
||||
await _sharedPreferences.setInt(
|
||||
PreferencesKey.currentBitcoinElectrumSererIdKey, currentBitcoinElectrumSererId);
|
||||
|
@ -344,43 +318,6 @@ class BackupService {
|
|||
await _sharedPreferences.setInt(
|
||||
PreferencesKey.bitcoinTransactionPriority, bitcoinTransactionPriority);
|
||||
|
||||
if (selectedCake2FAPreset != null)
|
||||
await _sharedPreferences.setInt(PreferencesKey.selectedCake2FAPreset, selectedCake2FAPreset);
|
||||
|
||||
if (shouldRequireTOTP2FAForAccessingWallet != null)
|
||||
await _sharedPreferences.setBool(PreferencesKey.shouldRequireTOTP2FAForAccessingWallet,
|
||||
shouldRequireTOTP2FAForAccessingWallet);
|
||||
|
||||
if (shouldRequireTOTP2FAForSendsToContact != null)
|
||||
await _sharedPreferences.setBool(PreferencesKey.shouldRequireTOTP2FAForSendsToContact,
|
||||
shouldRequireTOTP2FAForSendsToContact);
|
||||
|
||||
if (shouldRequireTOTP2FAForSendsToNonContact != null)
|
||||
await _sharedPreferences.setBool(PreferencesKey.shouldRequireTOTP2FAForSendsToNonContact,
|
||||
shouldRequireTOTP2FAForSendsToNonContact);
|
||||
|
||||
if (shouldRequireTOTP2FAForSendsToInternalWallets != null)
|
||||
await _sharedPreferences.setBool(PreferencesKey.shouldRequireTOTP2FAForSendsToInternalWallets,
|
||||
shouldRequireTOTP2FAForSendsToInternalWallets);
|
||||
|
||||
if (shouldRequireTOTP2FAForExchangesToInternalWallets != null)
|
||||
await _sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets,
|
||||
shouldRequireTOTP2FAForExchangesToInternalWallets);
|
||||
|
||||
if (shouldRequireTOTP2FAForAddingContacts != null)
|
||||
await _sharedPreferences.setBool(PreferencesKey.shouldRequireTOTP2FAForAddingContacts,
|
||||
shouldRequireTOTP2FAForAddingContacts);
|
||||
|
||||
if (shouldRequireTOTP2FAForCreatingNewWallets != null)
|
||||
await _sharedPreferences.setBool(PreferencesKey.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
shouldRequireTOTP2FAForCreatingNewWallets);
|
||||
|
||||
if (shouldRequireTOTP2FAForAllSecurityAndBackupSettings != null)
|
||||
await _sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
shouldRequireTOTP2FAForAllSecurityAndBackupSettings);
|
||||
|
||||
if (sortBalanceTokensBy != null)
|
||||
await _sharedPreferences.setInt(PreferencesKey.sortBalanceBy, sortBalanceTokensBy);
|
||||
|
||||
|
@ -532,8 +469,6 @@ class BackupService {
|
|||
PreferencesKey.currentPinLength: _sharedPreferences.getInt(PreferencesKey.currentPinLength),
|
||||
PreferencesKey.currentTransactionPriorityKeyLegacy:
|
||||
_sharedPreferences.getInt(PreferencesKey.currentTransactionPriorityKeyLegacy),
|
||||
PreferencesKey.allowBiometricalAuthenticationKey:
|
||||
_sharedPreferences.getBool(PreferencesKey.allowBiometricalAuthenticationKey),
|
||||
PreferencesKey.currentBitcoinElectrumSererIdKey:
|
||||
_sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey),
|
||||
PreferencesKey.currentLanguageCode:
|
||||
|
@ -550,24 +485,6 @@ class BackupService {
|
|||
_sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority),
|
||||
PreferencesKey.currentFiatApiModeKey:
|
||||
_sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey),
|
||||
PreferencesKey.selectedCake2FAPreset:
|
||||
_sharedPreferences.getInt(PreferencesKey.selectedCake2FAPreset),
|
||||
PreferencesKey.shouldRequireTOTP2FAForAccessingWallet:
|
||||
_sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForAccessingWallet),
|
||||
PreferencesKey.shouldRequireTOTP2FAForSendsToContact:
|
||||
_sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToContact),
|
||||
PreferencesKey.shouldRequireTOTP2FAForSendsToNonContact:
|
||||
_sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToNonContact),
|
||||
PreferencesKey.shouldRequireTOTP2FAForSendsToInternalWallets:
|
||||
_sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToInternalWallets),
|
||||
PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets: _sharedPreferences
|
||||
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets),
|
||||
PreferencesKey.shouldRequireTOTP2FAForAddingContacts:
|
||||
_sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForAddingContacts),
|
||||
PreferencesKey.shouldRequireTOTP2FAForCreatingNewWallets:
|
||||
_sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForCreatingNewWallets),
|
||||
PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings: _sharedPreferences
|
||||
.getBool(PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings),
|
||||
PreferencesKey.sortBalanceBy: _sharedPreferences.getInt(PreferencesKey.sortBalanceBy),
|
||||
PreferencesKey.pinNativeTokenAtTop:
|
||||
_sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop),
|
||||
|
|
28
lib/di.dart
28
lib/di.dart
|
@ -277,6 +277,7 @@ Future<void> setup({
|
|||
|
||||
if (!_isSetupFinished) {
|
||||
getIt.registerSingletonAsync<SharedPreferences>(() => SharedPreferences.getInstance());
|
||||
getIt.registerSingleton<FlutterSecureStorage>(secureStorage);
|
||||
}
|
||||
if (!_isSetupFinished) {
|
||||
getIt.registerFactory(() => BackgroundTasks());
|
||||
|
@ -303,7 +304,6 @@ Future<void> setup({
|
|||
getIt.registerFactory<Box<Node>>(() => _nodeSource);
|
||||
getIt.registerFactory<Box<Node>>(() => _powNodeSource, instanceName: Node.boxName + "pow");
|
||||
|
||||
getIt.registerSingleton<FlutterSecureStorage>(secureStorage);
|
||||
getIt.registerSingleton(AuthenticationStore());
|
||||
getIt.registerSingleton<WalletListStore>(WalletListStore());
|
||||
getIt.registerSingleton(NodeListStoreBase.instance);
|
||||
|
@ -345,17 +345,19 @@ Future<void> setup({
|
|||
walletInfoSource: _walletInfoSource));
|
||||
|
||||
getIt.registerFactoryParam<AdvancedPrivacySettingsViewModel, WalletType, void>(
|
||||
(type, _) => AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
|
||||
(type, _) => AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
|
||||
|
||||
getIt.registerFactory<WalletLoadingService>(() => WalletLoadingService(
|
||||
getIt.get<SharedPreferences>(),
|
||||
getIt.get<KeyService>(),
|
||||
(WalletType type) => getIt.get<WalletService>(param1: type)));
|
||||
|
||||
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) =>
|
||||
WalletNewVM(getIt.get<AppStore>(),
|
||||
getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
|
||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),type: type));
|
||||
|
||||
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) => WalletNewVM(
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletCreationService>(param1: type),
|
||||
_walletInfoSource,
|
||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
|
||||
type: type));
|
||||
|
||||
getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) {
|
||||
return WalletRestorationFromQRVM(getIt.get<AppStore>(),
|
||||
|
@ -611,7 +613,6 @@ Future<void> setup({
|
|||
_walletInfoSource,
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletLoadingService>(),
|
||||
getIt.get<AuthService>(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -622,7 +623,6 @@ Future<void> setup({
|
|||
_walletInfoSource,
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletLoadingService>(),
|
||||
getIt.get<AuthService>(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -724,7 +724,7 @@ Future<void> setup({
|
|||
});
|
||||
|
||||
getIt.registerFactory(() {
|
||||
return SecuritySettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AuthService>());
|
||||
return SecuritySettingsViewModel(getIt.get<SettingsStore>());
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => WalletSeedViewModel(getIt.get<AppStore>().wallet!));
|
||||
|
@ -806,8 +806,7 @@ Future<void> setup({
|
|||
.registerFactory<DFXBuyProvider>(() => DFXBuyProvider(wallet: getIt.get<AppStore>().wallet!));
|
||||
|
||||
getIt.registerFactory<MoonPaySellProvider>(() => MoonPaySellProvider(
|
||||
settingsStore: getIt.get<AppStore>().settingsStore,
|
||||
wallet: getIt.get<AppStore>().wallet!));
|
||||
settingsStore: getIt.get<AppStore>().settingsStore, wallet: getIt.get<AppStore>().wallet!));
|
||||
|
||||
getIt.registerFactory<OnRamperBuyProvider>(() => OnRamperBuyProvider(
|
||||
getIt.get<AppStore>().settingsStore,
|
||||
|
@ -920,8 +919,7 @@ Future<void> setup({
|
|||
(param1, isCreate) => NewWalletTypePage(onTypeSelected: param1, isCreate: isCreate ?? true));
|
||||
|
||||
getIt.registerFactoryParam<PreSeedPage, int, void>(
|
||||
(seedPhraseLength, _)
|
||||
=> PreSeedPage(seedPhraseLength));
|
||||
(seedPhraseLength, _) => PreSeedPage(seedPhraseLength));
|
||||
|
||||
getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
|
||||
TradeDetailsViewModel(
|
||||
|
@ -955,7 +953,7 @@ Future<void> setup({
|
|||
getIt.registerFactory(() => BuyAmountViewModel());
|
||||
|
||||
getIt.registerFactoryParam<BuySellOptionsPage, bool, void>(
|
||||
(isBuyOption, _) => BuySellOptionsPage(getIt.get<DashboardViewModel>(), isBuyOption));
|
||||
(isBuyOption, _) => BuySellOptionsPage(getIt.get<DashboardViewModel>(), isBuyOption));
|
||||
|
||||
getIt.registerFactory(() {
|
||||
final wallet = getIt.get<AppStore>().wallet;
|
||||
|
|
|
@ -185,6 +185,9 @@ Future<void> defaultSettingsMigration(
|
|||
case 25:
|
||||
await rewriteSecureStoragePin(secureStorage: secureStorage);
|
||||
break;
|
||||
case 26:
|
||||
await insecureStorageMigration(secureStorage: secureStorage, sharedPreferences: sharedPreferences);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -378,6 +381,82 @@ Node getMoneroDefaultNode({required Box<Node> nodes}) {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> insecureStorageMigration({
|
||||
required SharedPreferences sharedPreferences,
|
||||
required FlutterSecureStorage secureStorage,
|
||||
}) async {
|
||||
bool? allowBiometricalAuthentication =
|
||||
sharedPreferences.getBool(SecureKey.allowBiometricalAuthenticationKey);
|
||||
bool? useTOTP2FA = sharedPreferences.getBool(SecureKey.useTOTP2FA);
|
||||
bool? shouldRequireTOTP2FAForAccessingWallet =
|
||||
sharedPreferences.getBool(SecureKey.shouldRequireTOTP2FAForAccessingWallet);
|
||||
bool? shouldRequireTOTP2FAForSendsToContact =
|
||||
sharedPreferences.getBool(SecureKey.shouldRequireTOTP2FAForSendsToContact);
|
||||
bool? shouldRequireTOTP2FAForSendsToNonContact =
|
||||
sharedPreferences.getBool(SecureKey.shouldRequireTOTP2FAForSendsToNonContact);
|
||||
bool? shouldRequireTOTP2FAForSendsToInternalWallets =
|
||||
sharedPreferences.getBool(SecureKey.shouldRequireTOTP2FAForSendsToInternalWallets);
|
||||
bool? shouldRequireTOTP2FAForExchangesToInternalWallets =
|
||||
sharedPreferences.getBool(SecureKey.shouldRequireTOTP2FAForExchangesToInternalWallets);
|
||||
bool? shouldRequireTOTP2FAForExchangesToExternalWallets =
|
||||
sharedPreferences.getBool(SecureKey.shouldRequireTOTP2FAForExchangesToExternalWallets);
|
||||
bool? shouldRequireTOTP2FAForAddingContacts =
|
||||
sharedPreferences.getBool(SecureKey.shouldRequireTOTP2FAForAddingContacts);
|
||||
bool? shouldRequireTOTP2FAForCreatingNewWallets =
|
||||
sharedPreferences.getBool(SecureKey.shouldRequireTOTP2FAForCreatingNewWallets);
|
||||
bool? shouldRequireTOTP2FAForAllSecurityAndBackupSettings =
|
||||
sharedPreferences.getBool(SecureKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings);
|
||||
int? selectedCake2FAPreset = sharedPreferences.getInt(SecureKey.selectedCake2FAPreset);
|
||||
String? totpSecretKey = sharedPreferences.getString(SecureKey.totpSecretKey);
|
||||
int? pinTimeOutDuration = sharedPreferences.getInt(SecureKey.pinTimeOutDuration);
|
||||
int? lastAuthTimeMilliseconds = sharedPreferences.getInt(SecureKey.lastAuthTimeMilliseconds);
|
||||
|
||||
try {
|
||||
await secureStorage.write(
|
||||
key: SecureKey.allowBiometricalAuthenticationKey,
|
||||
value: allowBiometricalAuthentication.toString());
|
||||
await secureStorage.write(key: SecureKey.useTOTP2FA, value: useTOTP2FA.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForAccessingWallet,
|
||||
value: shouldRequireTOTP2FAForAccessingWallet.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToContact,
|
||||
value: shouldRequireTOTP2FAForSendsToContact.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToNonContact,
|
||||
value: shouldRequireTOTP2FAForSendsToNonContact.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToInternalWallets,
|
||||
value: shouldRequireTOTP2FAForSendsToInternalWallets.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForExchangesToInternalWallets,
|
||||
value: shouldRequireTOTP2FAForExchangesToInternalWallets.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForExchangesToExternalWallets,
|
||||
value: shouldRequireTOTP2FAForExchangesToExternalWallets.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForAddingContacts,
|
||||
value: shouldRequireTOTP2FAForAddingContacts.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
value: shouldRequireTOTP2FAForCreatingNewWallets.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
value: shouldRequireTOTP2FAForAllSecurityAndBackupSettings.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.selectedCake2FAPreset, value: selectedCake2FAPreset.toString());
|
||||
await secureStorage.write(key: SecureKey.totpSecretKey, value: totpSecretKey.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.pinTimeOutDuration, value: pinTimeOutDuration.toString());
|
||||
await secureStorage.write(
|
||||
key: SecureKey.lastAuthTimeMilliseconds, value: lastAuthTimeMilliseconds.toString());
|
||||
} catch (e) {
|
||||
print("Error migrating shared preferences to secure storage!: $e");
|
||||
// this actually shouldn't be that big of a problem since we don't delete the old keys in this update
|
||||
// and we read and write to the new locations when loading storage, the migration is just for extra safety
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> rewriteSecureStoragePin({required FlutterSecureStorage secureStorage}) async {
|
||||
// the bug only affects ios/mac:
|
||||
if (!Platform.isIOS && !Platform.isMacOS) {
|
||||
|
|
|
@ -6,12 +6,14 @@ import 'package:cake_wallet/entities/parsed_address.dart';
|
|||
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
|
||||
import 'package:cake_wallet/entities/emoji_string_extension.dart';
|
||||
import 'package:cake_wallet/mastodon/mastodon_api.dart';
|
||||
import 'package:cake_wallet/nostr/nostr_api.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/twitter/twitter_api.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/entities/fio_address_provider.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class AddressResolver {
|
||||
AddressResolver({required this.yatService, required this.wallet, required this.settingsStore})
|
||||
|
@ -58,7 +60,16 @@ class AddressResolver {
|
|||
});
|
||||
}
|
||||
|
||||
Future<ParsedAddress> resolve(String text, String ticker) async {
|
||||
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, String ticker) async {
|
||||
try {
|
||||
if (text.startsWith('@') && !text.substring(1).contains('@')) {
|
||||
if(settingsStore.lookupsTwitter) {
|
||||
|
@ -165,6 +176,21 @@ class AddressResolver {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (isEmailFormat(text)) {
|
||||
final nostrProfile = await NostrProfileHandler.queryProfile(context, text);
|
||||
if (nostrProfile?.relays != null) {
|
||||
final nostrUserData =
|
||||
await NostrProfileHandler.processRelays(context, nostrProfile!, text);
|
||||
|
||||
if (nostrUserData != null) {
|
||||
String? addressFromBio = extractAddressByType(
|
||||
raw: nostrUserData.about, type: CryptoCurrency.fromString(ticker));
|
||||
if (addressFromBio != null) {
|
||||
return ParsedAddress.nostrAddress(address: addressFromBio, name: text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
|
|
|
@ -2,7 +2,18 @@ import 'package:cake_wallet/entities/openalias_record.dart';
|
|||
import 'package:cake_wallet/entities/yat_record.dart';
|
||||
|
||||
|
||||
enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter, ens, contact, mastodon }
|
||||
enum ParseFrom {
|
||||
unstoppableDomains,
|
||||
openAlias,
|
||||
yatRecord,
|
||||
fio,
|
||||
notParsed,
|
||||
twitter,
|
||||
ens,
|
||||
contact,
|
||||
mastodon,
|
||||
nostr
|
||||
}
|
||||
|
||||
class ParsedAddress {
|
||||
ParsedAddress({
|
||||
|
@ -11,9 +22,9 @@ class ParsedAddress {
|
|||
this.description = '',
|
||||
this.parseFrom = ParseFrom.notParsed,
|
||||
});
|
||||
|
||||
|
||||
factory ParsedAddress.fetchEmojiAddress({
|
||||
List<YatRecord>? addresses,
|
||||
List<YatRecord>? addresses,
|
||||
required String name,
|
||||
}){
|
||||
if (addresses?.isEmpty ?? true) {
|
||||
|
@ -28,7 +39,7 @@ class ParsedAddress {
|
|||
}
|
||||
|
||||
factory ParsedAddress.fetchUnstoppableDomainAddress({
|
||||
String? address,
|
||||
String? address,
|
||||
required String name,
|
||||
}){
|
||||
if (address?.isEmpty ?? true) {
|
||||
|
@ -94,6 +105,14 @@ class ParsedAddress {
|
|||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.nostrAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.nostr,
|
||||
);
|
||||
}
|
||||
|
||||
final List<String> addresses;
|
||||
final String name;
|
||||
final String description;
|
||||
|
|
|
@ -23,8 +23,6 @@ class PreferencesKey {
|
|||
static const walletListOrder = 'wallet_list_order';
|
||||
static const walletListAscending = 'wallet_list_ascending';
|
||||
static const currentFiatApiModeKey = 'current_fiat_api_mode';
|
||||
static const allowBiometricalAuthenticationKey = 'allow_biometrical_authentication';
|
||||
static const useTOTP2FA = 'use_totp_2fa';
|
||||
static const failedTotpTokenTrials = 'failed_token_trials';
|
||||
static const disableExchangeKey = 'disable_exchange';
|
||||
static const exchangeStatusKey = 'exchange_status';
|
||||
|
@ -33,6 +31,7 @@ class PreferencesKey {
|
|||
static const displayActionListModeKey = 'display_list_mode';
|
||||
static const currentPinLength = 'current_pin_length';
|
||||
static const currentLanguageCode = 'language_code';
|
||||
static const currentSeedPhraseLength = 'current_seed_phrase_length';
|
||||
static const currentDefaultSettingsMigrationVersion =
|
||||
'current_default_settings_migration_version';
|
||||
static const moneroTransactionPriority = 'current_fee_priority_monero';
|
||||
|
@ -47,8 +46,6 @@ class PreferencesKey {
|
|||
static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
|
||||
static const syncModeKey = 'sync_mode';
|
||||
static const syncAllKey = 'sync_all';
|
||||
static const pinTimeOutDuration = 'pin_timeout_duration';
|
||||
static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds';
|
||||
static const lastPopupDate = 'last_popup_date';
|
||||
static const lastAppReviewDate = 'last_app_review_date';
|
||||
static const sortBalanceBy = 'sort_balance_by';
|
||||
|
@ -75,26 +72,5 @@ class PreferencesKey {
|
|||
static const lastSeenAppVersion = 'last_seen_app_version';
|
||||
static const shouldShowMarketPlaceInDashboard = 'should_show_marketplace_in_dashboard';
|
||||
static const isNewInstall = 'is_new_install';
|
||||
static const shouldRequireTOTP2FAForAccessingWallet =
|
||||
'should_require_totp_2fa_for_accessing_wallets';
|
||||
static const shouldRequireTOTP2FAForSendsToContact =
|
||||
'should_require_totp_2fa_for_sends_to_contact';
|
||||
static const shouldRequireTOTP2FAForSendsToNonContact =
|
||||
'should_require_totp_2fa_for_sends_to_non_contact';
|
||||
static const shouldRequireTOTP2FAForSendsToInternalWallets =
|
||||
'should_require_totp_2fa_for_sends_to_internal_wallets';
|
||||
static const shouldRequireTOTP2FAForExchangesToInternalWallets =
|
||||
'should_require_totp_2fa_for_exchanges_to_internal_wallets';
|
||||
static const shouldRequireTOTP2FAForExchangesToExternalWallets =
|
||||
'should_require_totp_2fa_for_exchanges_to_external_wallets';
|
||||
static const shouldRequireTOTP2FAForAddingContacts =
|
||||
'should_require_totp_2fa_for_adding_contacts';
|
||||
static const shouldRequireTOTP2FAForCreatingNewWallets =
|
||||
'should_require_totp_2fa_for_creating_new_wallets';
|
||||
static const shouldRequireTOTP2FAForAllSecurityAndBackupSettings =
|
||||
'should_require_totp_2fa_for_all_security_and_backup_settings';
|
||||
static const selectedCake2FAPreset = 'selected_cake_2fa_preset';
|
||||
static const totpSecretKey = 'totp_secret_key';
|
||||
static const currentSeedPhraseLength = 'current_seed_phrase_length';
|
||||
static const thorChainTradeCounter = 'thor_chain_trade_counter';
|
||||
}
|
||||
|
|
|
@ -65,9 +65,10 @@ class ProvidersHelper {
|
|||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood];
|
||||
case WalletType.polygon:
|
||||
return [ProviderType.askEachTime, ProviderType.dfx];
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.polygon:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -76,17 +77,22 @@ class ProvidersHelper {
|
|||
switch (walletType) {
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.ethereum:
|
||||
return [ProviderType.askEachTime, ProviderType.onramper,
|
||||
ProviderType.moonpaySell, ProviderType.dfx];
|
||||
return [
|
||||
ProviderType.askEachTime,
|
||||
ProviderType.onramper,
|
||||
ProviderType.moonpaySell,
|
||||
ProviderType.dfx,
|
||||
];
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
return [ProviderType.askEachTime, ProviderType.moonpaySell];
|
||||
case WalletType.polygon:
|
||||
return [ProviderType.askEachTime, ProviderType.dfx];
|
||||
case WalletType.monero:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.polygon:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
enum SecretStoreKey { moneroWalletPassword, pinCodePassword, backupPassword }
|
||||
|
||||
const moneroWalletPassword = "MONERO_WALLET_PASSWORD";
|
||||
|
@ -35,3 +38,65 @@ String generateStoreKeyFor({
|
|||
|
||||
return _key;
|
||||
}
|
||||
|
||||
class SecureKey {
|
||||
static const allowBiometricalAuthenticationKey = 'allow_biometrical_authentication';
|
||||
static const useTOTP2FA = 'use_totp_2fa';
|
||||
static const shouldRequireTOTP2FAForAccessingWallet =
|
||||
'should_require_totp_2fa_for_accessing_wallets';
|
||||
static const shouldRequireTOTP2FAForSendsToContact =
|
||||
'should_require_totp_2fa_for_sends_to_contact';
|
||||
static const shouldRequireTOTP2FAForSendsToNonContact =
|
||||
'should_require_totp_2fa_for_sends_to_non_contact';
|
||||
static const shouldRequireTOTP2FAForSendsToInternalWallets =
|
||||
'should_require_totp_2fa_for_sends_to_internal_wallets';
|
||||
static const shouldRequireTOTP2FAForExchangesToInternalWallets =
|
||||
'should_require_totp_2fa_for_exchanges_to_internal_wallets';
|
||||
static const shouldRequireTOTP2FAForExchangesToExternalWallets =
|
||||
'should_require_totp_2fa_for_exchanges_to_external_wallets';
|
||||
static const shouldRequireTOTP2FAForAddingContacts =
|
||||
'should_require_totp_2fa_for_adding_contacts';
|
||||
static const shouldRequireTOTP2FAForCreatingNewWallets =
|
||||
'should_require_totp_2fa_for_creating_new_wallets';
|
||||
static const shouldRequireTOTP2FAForAllSecurityAndBackupSettings =
|
||||
'should_require_totp_2fa_for_all_security_and_backup_settings';
|
||||
static const selectedCake2FAPreset = 'selected_cake_2fa_preset';
|
||||
static const totpSecretKey = 'totp_secret_key';
|
||||
static const pinTimeOutDuration = 'pin_timeout_duration';
|
||||
static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds';
|
||||
|
||||
static Future<int?> getInt({
|
||||
required FlutterSecureStorage secureStorage,
|
||||
required SharedPreferences sharedPreferences,
|
||||
required String key,
|
||||
}) async {
|
||||
int? value = int.tryParse((await secureStorage.read(key: key) ?? ''));
|
||||
value ??= sharedPreferences.getInt(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
static Future<bool?> getBool({
|
||||
required FlutterSecureStorage secureStorage,
|
||||
required SharedPreferences sharedPreferences,
|
||||
required String key,
|
||||
}) async {
|
||||
String? value = (await secureStorage.read(key: key) ?? '');
|
||||
if (value.toLowerCase() == "true") {
|
||||
return true;
|
||||
} else if (value.toLowerCase() == "false") {
|
||||
return false;
|
||||
} else {
|
||||
return sharedPreferences.getBool(key);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String?> getString({
|
||||
required FlutterSecureStorage secureStorage,
|
||||
required SharedPreferences sharedPreferences,
|
||||
required String key,
|
||||
}) async {
|
||||
String? value = await secureStorage.read(key: key);
|
||||
value ??= sharedPreferences.getString(key);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ Future<void> initializeAppConfigs() async {
|
|||
transactionDescriptions: transactionDescriptions,
|
||||
secureStorage: secureStorage,
|
||||
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
||||
initialMigrationVersion: 25);
|
||||
initialMigrationVersion: 26);
|
||||
}
|
||||
|
||||
Future<void> initialSetup(
|
||||
|
|
142
lib/nostr/nostr_api.dart
Normal file
142
lib/nostr/nostr_api.dart
Normal file
|
@ -0,0 +1,142 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/nostr/nostr_user.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nostr_tools/nostr_tools.dart';
|
||||
|
||||
class NostrProfileHandler {
|
||||
static final relayToDomainMap = {
|
||||
'relay.snort.social': 'snort.social',
|
||||
};
|
||||
|
||||
static Nip05 _nip05 = Nip05();
|
||||
|
||||
static Future<ProfilePointer?> queryProfile(BuildContext context, String nip05Address) async {
|
||||
var profile = await _nip05.queryProfile(nip05Address);
|
||||
if (profile?.pubkey != null) {
|
||||
if (profile?.relays?.isNotEmpty == true) {
|
||||
return profile;
|
||||
} else {
|
||||
await _showErrorDialog(context, S.of(context).no_relays, S.of(context).no_relays_message);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<UserMetadata?> processRelays(
|
||||
BuildContext context, ProfilePointer profile, String nip05Address) async {
|
||||
String userDomain = _extractDomain(nip05Address);
|
||||
const int metaData = 0;
|
||||
|
||||
for (String relayUrl in profile.relays ?? []) {
|
||||
final relayDomain = _getDomainFromRelayUrl(relayUrl);
|
||||
final formattedRelayDomain = relayToDomainMap[relayDomain] ?? relayDomain;
|
||||
if (formattedRelayDomain == userDomain) {
|
||||
final userDomainData = await _fetchInfoFromRelay(relayUrl, profile.pubkey, [metaData]);
|
||||
if (userDomainData != null) {
|
||||
return userDomainData;
|
||||
}
|
||||
}
|
||||
}
|
||||
await _showErrorDialog(context, S.of(context).no_relays, S.of(context).no_relay_on_domain);
|
||||
|
||||
String? chosenRelayUrl = await _showRelayChoiceDialog(context, profile.relays ?? []);
|
||||
if (chosenRelayUrl != null) {
|
||||
final userData = await _fetchInfoFromRelay(chosenRelayUrl, profile.pubkey, [metaData]);
|
||||
if (userData != null) {
|
||||
return userData;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<UserMetadata?> _fetchInfoFromRelay(
|
||||
String relayUrl, String userPubKey, List<int> kinds) async {
|
||||
try {
|
||||
final relay = RelayApi(relayUrl: relayUrl);
|
||||
final stream = await relay.connect();
|
||||
|
||||
relay.sub([
|
||||
Filter(
|
||||
kinds: kinds,
|
||||
authors: [userPubKey],
|
||||
)
|
||||
]);
|
||||
|
||||
await for (var message in stream) {
|
||||
if (message.type == 'EVENT') {
|
||||
final event = message.message as Event;
|
||||
|
||||
final eventContent = json.decode(event.content) as Map<String, dynamic>;
|
||||
|
||||
final userMetadata = UserMetadata.fromJson(eventContent);
|
||||
relay.close();
|
||||
return userMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
relay.close();
|
||||
return null;
|
||||
} catch (e) {
|
||||
print('[!] Error with relay $relayUrl: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _showErrorDialog(
|
||||
BuildContext context, String title, String errorMessage) async {
|
||||
if (context.mounted) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: title,
|
||||
alertContent: errorMessage,
|
||||
buttonText: S.of(dialogContext).ok,
|
||||
buttonAction: () => Navigator.of(dialogContext).pop(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static String _extractDomain(String nip05Address) {
|
||||
var parts = nip05Address.split('@');
|
||||
return parts.length == 2 ? parts[1] : '';
|
||||
}
|
||||
|
||||
static String _getDomainFromRelayUrl(String relayUrl) {
|
||||
try {
|
||||
var uri = Uri.parse(relayUrl);
|
||||
return uri.host;
|
||||
} catch (e) {
|
||||
print('Error parsing URL: $e');
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String?> _showRelayChoiceDialog(BuildContext context, List<String> relays) async {
|
||||
String? selectedRelay;
|
||||
|
||||
if (context.mounted) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return Picker<String>(
|
||||
selectedAtIndex: 0,
|
||||
title: S.of(dialogContext).choose_relay,
|
||||
items: relays,
|
||||
onItemSelected: (String relay) => selectedRelay = relay,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return selectedRelay;
|
||||
}
|
||||
}
|
35
lib/nostr/nostr_user.dart
Normal file
35
lib/nostr/nostr_user.dart
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
class UserMetadata {
|
||||
final String name;
|
||||
final String lnurl;
|
||||
final String email;
|
||||
final String picture;
|
||||
final String about;
|
||||
final String nip05;
|
||||
final String banner;
|
||||
final String website;
|
||||
|
||||
UserMetadata({
|
||||
required this.name,
|
||||
required this.lnurl,
|
||||
required this.email,
|
||||
required this.picture,
|
||||
required this.about,
|
||||
required this.nip05,
|
||||
required this.banner,
|
||||
required this.website,
|
||||
});
|
||||
|
||||
factory UserMetadata.fromJson(Map<String, dynamic> json) {
|
||||
return UserMetadata(
|
||||
name: json['name'] as String? ?? '',
|
||||
lnurl: json['lud06'] as String? ?? '',
|
||||
email: json['lud16'] as String? ?? '',
|
||||
picture: json['picture'] as String? ?? '',
|
||||
about: json['about'] as String? ?? '',
|
||||
nip05: json['nip05'] as String? ?? '',
|
||||
banner: json['banner'] as String? ?? '',
|
||||
website: json['website'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
}
|
|
@ -68,7 +68,8 @@ void startCurrentWalletChangeReaction(
|
|||
.get<SharedPreferences>()
|
||||
.setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
||||
|
||||
if (wallet.type == WalletType.monero) {
|
||||
if (wallet.type == WalletType.monero || wallet.type == WalletType.bitcoin ||
|
||||
wallet.type == WalletType.litecoin || wallet.type == WalletType.bitcoinCash ) {
|
||||
_setAutoGenerateSubaddressStatus(wallet, settingsStore);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
@ -14,13 +17,14 @@ class WebViewPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return WebViewPageBody(_url);
|
||||
return WebViewPageBody(_title, _url);
|
||||
}
|
||||
}
|
||||
|
||||
class WebViewPageBody extends StatefulWidget {
|
||||
WebViewPageBody(this.uri);
|
||||
WebViewPageBody(this.title, this.uri);
|
||||
|
||||
final String title;
|
||||
final Uri uri;
|
||||
|
||||
@override
|
||||
|
@ -40,6 +44,27 @@ class WebViewPageBodyState extends State<WebViewPageBody> {
|
|||
onPermissionRequest: (controller, request) async {
|
||||
bool permissionGranted = await Permission.camera.status == PermissionStatus.granted;
|
||||
if (!permissionGranted) {
|
||||
final bool userConsent = await showPopUp<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).privacy,
|
||||
alertContent: S.of(context).camera_consent(widget.title),
|
||||
rightButtonText: S.of(context).agree,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () => Navigator.of(context).pop(true),
|
||||
actionLeftButton: () => Navigator.of(context).pop(false));
|
||||
}) ??
|
||||
false;
|
||||
|
||||
/// if user did NOT give the consent then return permission denied
|
||||
if (!userConsent) {
|
||||
return PermissionResponse(
|
||||
resources: request.resources,
|
||||
action: PermissionResponseAction.DENY,
|
||||
);
|
||||
}
|
||||
|
||||
permissionGranted = await Permission.camera.request().isGranted;
|
||||
}
|
||||
|
||||
|
|
|
@ -166,12 +166,16 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
|||
}
|
||||
|
||||
try {
|
||||
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
|
||||
if (context.mounted) {
|
||||
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
|
||||
}
|
||||
await widget.walletListViewModel.loadWallet(wallet);
|
||||
hideProgressText();
|
||||
setState(() {});
|
||||
} catch (e) {
|
||||
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||
if (context.mounted) {
|
||||
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||
}
|
||||
}
|
||||
},
|
||||
conditionToDetermineIfToUse2FA:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
|
||||
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
|
@ -15,6 +16,7 @@ import 'package:cake_wallet/utils/share_util.dart';
|
|||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
||||
|
@ -155,63 +157,27 @@ class AddressPage extends BasePage {
|
|||
amountController: _amountController,
|
||||
isLight: dashboardViewModel.settingsStore.currentTheme.type ==
|
||||
ThemeType.light))),
|
||||
SizedBox(height: 16),
|
||||
Observer(builder: (_) {
|
||||
if (addressListViewModel.hasAddressList) {
|
||||
return GestureDetector(
|
||||
onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled
|
||||
return SelectButton(
|
||||
text: addressListViewModel.buttonTitle,
|
||||
onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled &&
|
||||
(WalletType.monero == addressListViewModel.wallet.type ||
|
||||
WalletType.haven == addressListViewModel.wallet.type)
|
||||
? await showPopUp<void>(
|
||||
context: context, builder: (_) => getIt.get<MoneroAccountListPage>())
|
||||
context: context,
|
||||
builder: (_) => getIt.get<MoneroAccountListPage>())
|
||||
: Navigator.of(context).pushNamed(Routes.receive),
|
||||
child: Container(
|
||||
height: 50,
|
||||
padding: EdgeInsets.only(left: 24, right: 12),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
border: Border.all(
|
||||
color:
|
||||
Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
||||
width: 1),
|
||||
color: Theme.of(context)
|
||||
.extension<SyncIndicatorTheme>()!
|
||||
.syncedBackgroundColor),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Observer(
|
||||
builder: (_) {
|
||||
String label = addressListViewModel.hasAccounts
|
||||
? S.of(context).accounts_subaddresses
|
||||
: S.of(context).addresses;
|
||||
|
||||
if (dashboardViewModel.isAutoGenerateSubaddressesEnabled) {
|
||||
label = addressListViewModel.hasAccounts
|
||||
? S.of(context).accounts
|
||||
: S.of(context).account;
|
||||
}
|
||||
return Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.extension<SyncIndicatorTheme>()!
|
||||
.textColor),
|
||||
);
|
||||
},
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14,
|
||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
textColor: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||
borderColor: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
||||
arrowColor: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
||||
textSize: 14,
|
||||
height: 50,
|
||||
);
|
||||
} else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled ||
|
||||
addressListViewModel.showElectrumAddressDisclaimer) {
|
||||
addressListViewModel.isElectrumWallet) {
|
||||
return Text(S.of(context).electrum_address_disclaimer,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
|
|
|
@ -560,7 +560,7 @@ class ExchangePage extends BasePage {
|
|||
}
|
||||
|
||||
Future<String> fetchParsedAddress(BuildContext context, String domain, String ticker) async {
|
||||
final parsedAddress = await getIt.get<AddressResolver>().resolve(domain, ticker);
|
||||
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, ticker);
|
||||
final address = await extractAddressFromParsed(context, parsedAddress);
|
||||
return address;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'dart:ui';
|
||||
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
|
||||
|
@ -344,7 +345,11 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
bottom: 24,
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
Navigator.of(popupContext).pop();
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
popupContext,
|
||||
Routes.dashboard,
|
||||
(route) => false,
|
||||
);
|
||||
RequestReviewHandler.requestReview();
|
||||
},
|
||||
text: S.of(popupContext).got_it,
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/widgets/popup_cancellable_alert.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/search_bar_widget.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/wallet_types.g.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -26,15 +28,18 @@ class NewWalletTypePage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletTypeForm(
|
||||
onTypeSelected: onTypeSelected,
|
||||
walletImage: currentTheme.type == ThemeType.dark ? walletTypeImage : walletTypeLightImage);
|
||||
onTypeSelected: onTypeSelected,
|
||||
walletImage: currentTheme.type == ThemeType.dark ? walletTypeImage : walletTypeLightImage,
|
||||
isCreate: isCreate,
|
||||
);
|
||||
}
|
||||
|
||||
class WalletTypeForm extends StatefulWidget {
|
||||
WalletTypeForm({required this.onTypeSelected, required this.walletImage});
|
||||
WalletTypeForm({required this.onTypeSelected, required this.walletImage, required this.isCreate});
|
||||
|
||||
final void Function(BuildContext, WalletType) onTypeSelected;
|
||||
final Image walletImage;
|
||||
final bool isCreate;
|
||||
|
||||
@override
|
||||
WalletTypeFormState createState() => WalletTypeFormState();
|
||||
|
@ -70,7 +75,8 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
|||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
||||
constraints:
|
||||
BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
|
@ -128,6 +134,19 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
|||
throw Exception('Wallet Type is not selected yet.');
|
||||
}
|
||||
|
||||
if (selected == WalletType.haven && widget.isCreate) {
|
||||
return await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return PopUpCancellableAlertDialog(
|
||||
contentText: S.of(context).pause_wallet_creation,
|
||||
actionButtonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
widget.onTypeSelected(context, selected!);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,29 +11,37 @@ class SelectButton extends StatelessWidget {
|
|||
this.isSelected = false,
|
||||
this.showTrailingIcon = true,
|
||||
this.height = 60,
|
||||
this.textSize = 18,
|
||||
this.color,
|
||||
this.textColor,
|
||||
this.arrowColor,
|
||||
this.borderColor,
|
||||
});
|
||||
|
||||
final Image? image;
|
||||
final String text;
|
||||
final double textSize;
|
||||
final bool isSelected;
|
||||
final VoidCallback onTap;
|
||||
final bool showTrailingIcon;
|
||||
final double height;
|
||||
final Color? color;
|
||||
final Color? textColor;
|
||||
final Color? arrowColor;
|
||||
final Color? borderColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = isSelected
|
||||
? Colors.green
|
||||
: Theme.of(context).cardColor;
|
||||
final textColor = isSelected
|
||||
final backgroundColor = color ?? (isSelected ? Colors.green : Theme.of(context).cardColor);
|
||||
final effectiveTextColor = textColor ?? (isSelected
|
||||
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
||||
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
|
||||
final arrowColor = isSelected
|
||||
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor);
|
||||
final effectiveArrowColor = arrowColor ?? (isSelected
|
||||
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
||||
: Theme.of(context).extension<FilterTheme>()!.titlesColor;
|
||||
: Theme.of(context).extension<FilterTheme>()!.titlesColor);
|
||||
|
||||
final selectArrowImage = Image.asset('assets/images/select_arrow.png',
|
||||
color: arrowColor);
|
||||
color: effectiveArrowColor);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
|
@ -44,7 +52,9 @@ class SelectButton extends StatelessWidget {
|
|||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
color: color
|
||||
color: backgroundColor,
|
||||
border: borderColor != null ? Border.all(color: borderColor!) : null,
|
||||
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
|
@ -63,9 +73,9 @@ class SelectButton extends StatelessWidget {
|
|||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontSize: textSize,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: textColor
|
||||
color: effectiveTextColor,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -49,7 +49,7 @@ class ReceivePage extends BasePage {
|
|||
bool get gradientBackground => true;
|
||||
|
||||
@override
|
||||
bool get resizeToAvoidBottomInset => false;
|
||||
bool get resizeToAvoidBottomInset => true;
|
||||
|
||||
final FocusNode _cryptoAmountFocus;
|
||||
|
||||
|
@ -99,10 +99,11 @@ class ReceivePage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final isElectrumWallet = addressListViewModel.isElectrumWallet;
|
||||
return (addressListViewModel.type == WalletType.monero ||
|
||||
addressListViewModel.type == WalletType.haven ||
|
||||
addressListViewModel.type == WalletType.nano ||
|
||||
addressListViewModel.type == WalletType.banano)
|
||||
isElectrumWallet)
|
||||
? KeyboardActions(
|
||||
config: KeyboardActionsConfig(
|
||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||
|
@ -140,7 +141,9 @@ class ReceivePage extends BasePage {
|
|||
|
||||
if (item is WalletAccountListHeader) {
|
||||
cell = HeaderTile(
|
||||
onTap: () async {
|
||||
showTrailingButton: true,
|
||||
walletAddressListViewModel: addressListViewModel,
|
||||
trailingButtonTap: () async {
|
||||
if (addressListViewModel.type == WalletType.monero ||
|
||||
addressListViewModel.type == WalletType.haven) {
|
||||
await showPopUp<void>(
|
||||
|
@ -153,7 +156,7 @@ class ReceivePage extends BasePage {
|
|||
}
|
||||
},
|
||||
title: S.of(context).accounts,
|
||||
icon: Icon(
|
||||
trailingIcon: Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
|
@ -161,16 +164,21 @@ class ReceivePage extends BasePage {
|
|||
}
|
||||
|
||||
if (item is WalletAddressListHeader) {
|
||||
cell = HeaderTile(
|
||||
onTap: () =>
|
||||
Navigator.of(context).pushNamed(Routes.newSubaddress),
|
||||
title: S.of(context).addresses,
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
size: 20,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
));
|
||||
}
|
||||
cell = HeaderTile(
|
||||
title: S.of(context).addresses,
|
||||
walletAddressListViewModel: addressListViewModel,
|
||||
showTrailingButton: !addressListViewModel.isAutoGenerateSubaddressEnabled,
|
||||
showSearchButton: true,
|
||||
trailingButtonTap: () =>
|
||||
Navigator.of(context).pushNamed(Routes.newSubaddress),
|
||||
trailingIcon: Icon(
|
||||
Icons.add,
|
||||
size: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.iconsColor,
|
||||
));
|
||||
}
|
||||
|
||||
if (item is WalletAddressListItem) {
|
||||
cell = Observer(builder: (_) {
|
||||
|
@ -185,6 +193,7 @@ class ReceivePage extends BasePage {
|
|||
|
||||
return AddressCell.fromItem(item,
|
||||
isCurrent: isCurrent,
|
||||
hasBalance: addressListViewModel.isElectrumWallet,
|
||||
backgroundColor: backgroundColor,
|
||||
textColor: textColor,
|
||||
onTap: (_) => addressListViewModel.setAddress(item),
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
|
||||
class AddressCell extends StatelessWidget {
|
||||
AddressCell(
|
||||
|
@ -12,13 +13,18 @@ class AddressCell extends StatelessWidget {
|
|||
required this.backgroundColor,
|
||||
required this.textColor,
|
||||
this.onTap,
|
||||
this.onEdit});
|
||||
this.onEdit,
|
||||
this.txCount,
|
||||
this.balance,
|
||||
this.isChange = false,
|
||||
this.hasBalance = false});
|
||||
|
||||
factory AddressCell.fromItem(WalletAddressListItem item,
|
||||
{required bool isCurrent,
|
||||
required Color backgroundColor,
|
||||
required Color textColor,
|
||||
Function(String)? onTap,
|
||||
bool hasBalance = false,
|
||||
Function()? onEdit}) =>
|
||||
AddressCell(
|
||||
address: item.address,
|
||||
|
@ -28,7 +34,11 @@ class AddressCell extends StatelessWidget {
|
|||
backgroundColor: backgroundColor,
|
||||
textColor: textColor,
|
||||
onTap: onTap,
|
||||
onEdit: onEdit);
|
||||
onEdit: onEdit,
|
||||
txCount: item.txCount,
|
||||
balance: item.balance,
|
||||
isChange: item.isChange,
|
||||
hasBalance: hasBalance);
|
||||
|
||||
final String address;
|
||||
final String name;
|
||||
|
@ -38,17 +48,22 @@ class AddressCell extends StatelessWidget {
|
|||
final Color textColor;
|
||||
final Function(String)? onTap;
|
||||
final Function()? onEdit;
|
||||
final int? txCount;
|
||||
final String? balance;
|
||||
final bool isChange;
|
||||
final bool hasBalance;
|
||||
|
||||
String get label {
|
||||
if (name.isEmpty){
|
||||
if(address.length<=16){
|
||||
return address;
|
||||
}else{
|
||||
return address.substring(0,8)+'...'+
|
||||
address.substring(address.length-8,address.length);
|
||||
}
|
||||
}else{
|
||||
return name;
|
||||
static const int addressPreviewLength = 8;
|
||||
|
||||
String get formattedAddress {
|
||||
final formatIfCashAddr = address.replaceAll('bitcoincash:', '');
|
||||
|
||||
if (formatIfCashAddr.length <= (name.isNotEmpty ? 16 : 43)) {
|
||||
return formatIfCashAddr;
|
||||
} else {
|
||||
return formatIfCashAddr.substring(0, addressPreviewLength) +
|
||||
'...' +
|
||||
formatIfCashAddr.substring(formatIfCashAddr.length - addressPreviewLength, formatIfCashAddr.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,41 +74,114 @@ class AddressCell extends StatelessWidget {
|
|||
child: Container(
|
||||
width: double.infinity,
|
||||
color: backgroundColor,
|
||||
padding: EdgeInsets.only(left: 24, right: 24, top: 28, bottom: 28),
|
||||
child: Text(
|
||||
label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: textColor,
|
||||
),
|
||||
padding: EdgeInsets.only(left: 24, right: 24, top: 20, bottom: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
if (isChange)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Container(
|
||||
height: 20,
|
||||
padding: EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.5)),
|
||||
color: textColor),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
S.of(context).unspent_change,
|
||||
style: TextStyle(
|
||||
color: backgroundColor,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (name.isNotEmpty)
|
||||
Text(
|
||||
'$name - ',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: textColor,
|
||||
),
|
||||
),
|
||||
AutoSizeText(
|
||||
formattedAddress,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: isChange ? 10 : 14,
|
||||
color: textColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (hasBalance)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text(
|
||||
'Balance: $balance',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: textColor,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${S.of(context).transactions.toLowerCase()}: $txCount',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: textColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
return Semantics(
|
||||
label: S.of(context).slidable,
|
||||
selected: isCurrent,
|
||||
enabled: !isCurrent,
|
||||
child: Slidable(
|
||||
key: Key(address),
|
||||
startActionPane: _actionPane(context),
|
||||
endActionPane: _actionPane(context),
|
||||
child: cell,
|
||||
),
|
||||
);
|
||||
return onEdit == null
|
||||
? cell
|
||||
: Semantics(
|
||||
label: S.of(context).slidable,
|
||||
selected: isCurrent,
|
||||
enabled: !isCurrent,
|
||||
child: Slidable(
|
||||
key: Key(address),
|
||||
startActionPane: _actionPane(context),
|
||||
endActionPane: _actionPane(context),
|
||||
child: cell,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ActionPane _actionPane(BuildContext context) => ActionPane(
|
||||
motion: const ScrollMotion(),
|
||||
extentRatio: 0.3,
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (_) => onEdit?.call(),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
icon: Icons.edit,
|
||||
label: S.of(context).edit,
|
||||
),
|
||||
],
|
||||
);
|
||||
motion: const ScrollMotion(),
|
||||
extentRatio: 0.3,
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (_) => onEdit?.call(),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
icon: Icons.edit,
|
||||
label: S.of(context).edit,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,50 +1,114 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HeaderTile extends StatelessWidget {
|
||||
class HeaderTile extends StatefulWidget {
|
||||
HeaderTile({
|
||||
required this.onTap,
|
||||
required this.title,
|
||||
required this.icon
|
||||
required this.walletAddressListViewModel,
|
||||
this.showSearchButton = false,
|
||||
this.showTrailingButton = false,
|
||||
this.trailingButtonTap,
|
||||
this.trailingIcon,
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
final String title;
|
||||
final Icon icon;
|
||||
final WalletAddressListViewModel walletAddressListViewModel;
|
||||
final bool showSearchButton;
|
||||
final bool showTrailingButton;
|
||||
final VoidCallback? trailingButtonTap;
|
||||
final Icon? trailingIcon;
|
||||
|
||||
@override
|
||||
_HeaderTileState createState() => _HeaderTileState();
|
||||
}
|
||||
|
||||
class _HeaderTileState extends State<HeaderTile> {
|
||||
bool _isSearchActive = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: 24,
|
||||
bottom: 24
|
||||
),
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
width: 32,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsBackgroundColor),
|
||||
child: icon,
|
||||
)
|
||||
],
|
||||
),
|
||||
final searchIcon = Image.asset("assets/images/search_icon.png",
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor);
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
_isSearchActive
|
||||
? Expanded(
|
||||
child: TextField(
|
||||
onChanged: (value) => widget.walletAddressListViewModel.updateSearchText(value),
|
||||
cursorColor: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
|
||||
cursorWidth: 0.5,
|
||||
decoration: InputDecoration(
|
||||
hintText: '${S.of(context).search}...',
|
||||
isDense: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
|
||||
border: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Theme.of(context).dividerColor),
|
||||
),
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Theme.of(context).dividerColor),
|
||||
),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Theme.of(context).dividerColor),
|
||||
),
|
||||
),
|
||||
autofocus: true,
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
if (widget.showSearchButton)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isSearchActive = !_isSearchActive;
|
||||
widget.walletAddressListViewModel.updateSearchText('');
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
height: 32,
|
||||
width: 32,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.iconsBackgroundColor),
|
||||
child: searchIcon,
|
||||
)),
|
||||
const SizedBox(width: 8),
|
||||
if (widget.showTrailingButton)
|
||||
GestureDetector(
|
||||
onTap: widget.trailingButtonTap,
|
||||
child: Container(
|
||||
height: 32,
|
||||
width: 32,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color:
|
||||
Theme.of(context).extension<ReceivePageTheme>()!.iconsBackgroundColor),
|
||||
child: widget.trailingIcon,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
|
@ -6,6 +7,7 @@ import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dar
|
|||
import 'package:cake_wallet/utils/brightness_util.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
@ -144,9 +146,10 @@ class QRWidget extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
child: AutoSizeText(
|
||||
addressListViewModel.address.address,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: addressListViewModel.wallet.type == WalletType.monero ? 2 : 1,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
|
|
|
@ -53,13 +53,17 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
_requestAuth = widget.authService.requireAuth();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
bool value = await widget.authService.requireAuth();
|
||||
setState(() {
|
||||
_requestAuth = value;
|
||||
});
|
||||
});
|
||||
_isInactiveController = StreamController<bool>.broadcast();
|
||||
_isInactive = false;
|
||||
_postFrameCallback = false;
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
super.initState();
|
||||
|
||||
if (DeviceInfo.instance.isMobile) {
|
||||
initUniLinks();
|
||||
}
|
||||
|
@ -105,8 +109,10 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
|
||||
break;
|
||||
case AppLifecycleState.resumed:
|
||||
setState(() {
|
||||
_requestAuth = widget.authService.requireAuth();
|
||||
widget.authService.requireAuth().then((value) {
|
||||
setState(() {
|
||||
_requestAuth = value;
|
||||
});
|
||||
});
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -43,6 +43,11 @@ Future<String> extractAddressFromParsed(
|
|||
content = S.of(context).extracted_address_content('${parsedAddress.name} (Mastodon)');
|
||||
address = parsedAddress.addresses.first;
|
||||
break;
|
||||
case ParseFrom.nostr:
|
||||
title = S.of(context).address_detected;
|
||||
content = S.of(context).extracted_address_content('${parsedAddress.name} (Nostr NIP-05)');
|
||||
address = parsedAddress.addresses.first;
|
||||
break;
|
||||
case ParseFrom.yatRecord:
|
||||
if (parsedAddress.name.isEmpty) {
|
||||
title = S.of(context).yat_error;
|
||||
|
|
|
@ -478,7 +478,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
|||
Text(
|
||||
output.estimatedFee.toString() +
|
||||
' ' +
|
||||
sendViewModel.selectedCryptoCurrency.toString(),
|
||||
sendViewModel.currency.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:io';
|
|||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/provider_types.dart';
|
||||
import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
|
||||
|
@ -10,6 +9,7 @@ import 'package:cake_wallet/entities/background_tasks.dart';
|
|||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/pin_code_required_duration.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/secret_store_key.dart';
|
||||
import 'package:cake_wallet/entities/seed_phrase_length.dart';
|
||||
import 'package:cake_wallet/entities/seed_type.dart';
|
||||
import 'package:cake_wallet/entities/sort_balance_types.dart';
|
||||
|
@ -24,6 +24,7 @@ import 'package:cake_wallet/themes/theme_base.dart';
|
|||
import 'package:cake_wallet/themes/theme_list.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:package_info/package_info.dart';
|
||||
|
@ -38,13 +39,15 @@ import 'package:cake_wallet/monero/monero.dart';
|
|||
import 'package:cake_wallet/entities/action_list_display_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cw_core/set_app_secure_native.dart';
|
||||
|
||||
part 'settings_store.g.dart';
|
||||
|
||||
class SettingsStore = SettingsStoreBase with _$SettingsStore;
|
||||
|
||||
abstract class SettingsStoreBase with Store {
|
||||
SettingsStoreBase(
|
||||
{required BackgroundTasks backgroundTasks,
|
||||
{required FlutterSecureStorage secureStorage,
|
||||
required BackgroundTasks backgroundTasks,
|
||||
required SharedPreferences sharedPreferences,
|
||||
required bool initialShouldShowMarketPlaceInDashboard,
|
||||
required FiatCurrency initialFiatCurrency,
|
||||
|
@ -109,6 +112,7 @@ abstract class SettingsStoreBase with Store {
|
|||
TransactionPriority? initialBitcoinCashTransactionPriority})
|
||||
: nodes = ObservableMap<WalletType, Node>.of(nodes),
|
||||
powNodes = ObservableMap<WalletType, Node>.of(powNodes),
|
||||
_secureStorage = secureStorage,
|
||||
_sharedPreferences = sharedPreferences,
|
||||
_backgroundTasks = backgroundTasks,
|
||||
fiatCurrency = initialFiatCurrency,
|
||||
|
@ -187,8 +191,9 @@ abstract class SettingsStoreBase with Store {
|
|||
final key = 'buyProvider_${walletType.toString()}';
|
||||
final providerId = sharedPreferences.getString(key);
|
||||
if (providerId != null) {
|
||||
defaultBuyProviders[walletType] = ProviderType.values
|
||||
.firstWhere((provider) => provider.id == providerId, orElse: () => ProviderType.askEachTime);
|
||||
defaultBuyProviders[walletType] = ProviderType.values.firstWhere(
|
||||
(provider) => provider.id == providerId,
|
||||
orElse: () => ProviderType.askEachTime);
|
||||
} else {
|
||||
defaultBuyProviders[walletType] = ProviderType.askEachTime;
|
||||
}
|
||||
|
@ -198,8 +203,9 @@ abstract class SettingsStoreBase with Store {
|
|||
final key = 'sellProvider_${walletType.toString()}';
|
||||
final providerId = sharedPreferences.getString(key);
|
||||
if (providerId != null) {
|
||||
defaultSellProviders[walletType] = ProviderType.values
|
||||
.firstWhere((provider) => provider.id == providerId, orElse: () => ProviderType.askEachTime);
|
||||
defaultSellProviders[walletType] = ProviderType.values.firstWhere(
|
||||
(provider) => provider.id == providerId,
|
||||
orElse: () => ProviderType.askEachTime);
|
||||
} else {
|
||||
defaultSellProviders[walletType] = ProviderType.askEachTime;
|
||||
}
|
||||
|
@ -312,74 +318,6 @@ abstract class SettingsStoreBase with Store {
|
|||
reaction((_) => currentTheme,
|
||||
(ThemeBase theme) => sharedPreferences.setInt(PreferencesKey.currentTheme, theme.raw));
|
||||
|
||||
reaction(
|
||||
(_) => allowBiometricalAuthentication,
|
||||
(bool biometricalAuthentication) => sharedPreferences.setBool(
|
||||
PreferencesKey.allowBiometricalAuthenticationKey, biometricalAuthentication));
|
||||
|
||||
reaction(
|
||||
(_) => selectedCake2FAPreset,
|
||||
(Cake2FAPresetsOptions selectedCake2FAPreset) => sharedPreferences.setInt(
|
||||
PreferencesKey.selectedCake2FAPreset, selectedCake2FAPreset.serialize()));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForAccessingWallet,
|
||||
(bool requireTOTP2FAForAccessingWallet) => sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForAccessingWallet,
|
||||
requireTOTP2FAForAccessingWallet));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForSendsToContact,
|
||||
(bool requireTOTP2FAForSendsToContact) => sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForSendsToContact, requireTOTP2FAForSendsToContact));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForSendsToNonContact,
|
||||
(bool requireTOTP2FAForSendsToNonContact) => sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForSendsToNonContact,
|
||||
requireTOTP2FAForSendsToNonContact));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForSendsToInternalWallets,
|
||||
(bool requireTOTP2FAForSendsToInternalWallets) => sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForSendsToInternalWallets,
|
||||
requireTOTP2FAForSendsToInternalWallets));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForExchangesToInternalWallets,
|
||||
(bool requireTOTP2FAForExchangesToInternalWallets) => sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets,
|
||||
requireTOTP2FAForExchangesToInternalWallets));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForExchangesToExternalWallets,
|
||||
(bool requireTOTP2FAForExchangesToExternalWallets) => sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForExchangesToExternalWallets,
|
||||
requireTOTP2FAForExchangesToExternalWallets));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForAddingContacts,
|
||||
(bool requireTOTP2FAForAddingContacts) => sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForAddingContacts, requireTOTP2FAForAddingContacts));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
(bool requireTOTP2FAForCreatingNewWallets) => sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
requireTOTP2FAForCreatingNewWallets));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
(bool requireTOTP2FAForAllSecurityAndBackupSettings) => sharedPreferences.setBool(
|
||||
PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
requireTOTP2FAForAllSecurityAndBackupSettings));
|
||||
|
||||
reaction(
|
||||
(_) => useTOTP2FA, (bool use) => sharedPreferences.setBool(PreferencesKey.useTOTP2FA, use));
|
||||
|
||||
reaction((_) => totpSecretKey,
|
||||
(String totpKey) => sharedPreferences.setString(PreferencesKey.totpSecretKey, totpKey));
|
||||
|
||||
reaction(
|
||||
(_) => numberOfFailedTokenTrials,
|
||||
(int failedTokenTrail) =>
|
||||
|
@ -403,11 +341,6 @@ abstract class SettingsStoreBase with Store {
|
|||
(SeedPhraseLength seedPhraseWordCount) => sharedPreferences.setInt(
|
||||
PreferencesKey.currentSeedPhraseLength, seedPhraseWordCount.value));
|
||||
|
||||
reaction(
|
||||
(_) => pinTimeOutDuration,
|
||||
(PinCodeRequiredDuration pinCodeInterval) =>
|
||||
sharedPreferences.setInt(PreferencesKey.pinTimeOutDuration, pinCodeInterval.value));
|
||||
|
||||
reaction(
|
||||
(_) => balanceDisplayMode,
|
||||
(BalanceDisplayMode mode) => sharedPreferences.setInt(
|
||||
|
@ -485,6 +418,84 @@ abstract class SettingsStoreBase with Store {
|
|||
reaction((_) => lookupsENS,
|
||||
(bool looksUpENS) => _sharedPreferences.setBool(PreferencesKey.lookupsENS, looksUpENS));
|
||||
|
||||
// secure storage keys:
|
||||
reaction(
|
||||
(_) => allowBiometricalAuthentication,
|
||||
(bool biometricalAuthentication) => secureStorage.write(
|
||||
key: SecureKey.allowBiometricalAuthenticationKey,
|
||||
value: biometricalAuthentication.toString()));
|
||||
|
||||
reaction(
|
||||
(_) => selectedCake2FAPreset,
|
||||
(Cake2FAPresetsOptions selectedCake2FAPreset) => secureStorage.write(
|
||||
key: SecureKey.selectedCake2FAPreset,
|
||||
value: selectedCake2FAPreset.serialize().toString()));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForAccessingWallet,
|
||||
(bool requireTOTP2FAForAccessingWallet) => secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForAccessingWallet,
|
||||
value: requireTOTP2FAForAccessingWallet.toString()));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForSendsToContact,
|
||||
(bool requireTOTP2FAForSendsToContact) => secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToContact,
|
||||
value: requireTOTP2FAForSendsToContact.toString()));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForSendsToNonContact,
|
||||
(bool requireTOTP2FAForSendsToNonContact) => secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToNonContact,
|
||||
value: requireTOTP2FAForSendsToNonContact.toString()));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForSendsToInternalWallets,
|
||||
(bool requireTOTP2FAForSendsToInternalWallets) => secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToInternalWallets,
|
||||
value: requireTOTP2FAForSendsToInternalWallets.toString()));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForExchangesToInternalWallets,
|
||||
(bool requireTOTP2FAForExchangesToInternalWallets) => secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForExchangesToInternalWallets,
|
||||
value: requireTOTP2FAForExchangesToInternalWallets.toString()));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForExchangesToExternalWallets,
|
||||
(bool requireTOTP2FAForExchangesToExternalWallets) => secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForExchangesToExternalWallets,
|
||||
value: requireTOTP2FAForExchangesToExternalWallets.toString()));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForAddingContacts,
|
||||
(bool requireTOTP2FAForAddingContacts) => secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForAddingContacts,
|
||||
value: requireTOTP2FAForAddingContacts.toString()));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
(bool requireTOTP2FAForCreatingNewWallets) => secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
value: requireTOTP2FAForCreatingNewWallets.toString()));
|
||||
|
||||
reaction(
|
||||
(_) => shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
(bool requireTOTP2FAForAllSecurityAndBackupSettings) => secureStorage.write(
|
||||
key: SecureKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
value: requireTOTP2FAForAllSecurityAndBackupSettings.toString()));
|
||||
|
||||
reaction((_) => useTOTP2FA,
|
||||
(bool use) => secureStorage.write(key: SecureKey.useTOTP2FA, value: use.toString()));
|
||||
|
||||
reaction((_) => totpSecretKey,
|
||||
(String totpKey) => secureStorage.write(key: SecureKey.totpSecretKey, value: totpKey));
|
||||
|
||||
reaction(
|
||||
(_) => pinTimeOutDuration,
|
||||
(PinCodeRequiredDuration pinCodeInterval) => secureStorage.write(
|
||||
key: SecureKey.pinTimeOutDuration, value: pinCodeInterval.value.toString()));
|
||||
|
||||
this.nodes.observe((change) {
|
||||
if (change.newValue != null && change.key != null) {
|
||||
_saveCurrentNode(change.newValue!, change.key!);
|
||||
|
@ -668,6 +679,7 @@ abstract class SettingsStoreBase with Store {
|
|||
|
||||
String deviceName;
|
||||
|
||||
final FlutterSecureStorage _secureStorage;
|
||||
final SharedPreferences _sharedPreferences;
|
||||
final BackgroundTasks _backgroundTasks;
|
||||
|
||||
|
@ -710,6 +722,7 @@ abstract class SettingsStoreBase with Store {
|
|||
BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance,
|
||||
ThemeBase? initialTheme}) async {
|
||||
final sharedPreferences = await getIt.getAsync<SharedPreferences>();
|
||||
final secureStorage = await getIt.get<FlutterSecureStorage>();
|
||||
final backgroundTasks = getIt.get<BackgroundTasks>();
|
||||
final currentFiatCurrency = FiatCurrency.deserialize(
|
||||
raw: sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!);
|
||||
|
@ -770,36 +783,6 @@ abstract class SettingsStoreBase with Store {
|
|||
final currentFiatApiMode = FiatApiMode.deserialize(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey) ??
|
||||
FiatApiMode.enabled.raw);
|
||||
final allowBiometricalAuthentication =
|
||||
sharedPreferences.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? false;
|
||||
final selectedCake2FAPreset = Cake2FAPresetsOptions.deserialize(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.selectedCake2FAPreset) ??
|
||||
Cake2FAPresetsOptions.normal.raw);
|
||||
final shouldRequireTOTP2FAForAccessingWallet =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForAccessingWallet) ?? false;
|
||||
final shouldRequireTOTP2FAForSendsToContact =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToContact) ?? false;
|
||||
final shouldRequireTOTP2FAForSendsToNonContact =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToNonContact) ?? false;
|
||||
final shouldRequireTOTP2FAForSendsToInternalWallets =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToInternalWallets) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForExchangesToInternalWallets = sharedPreferences
|
||||
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForExchangesToExternalWallets = sharedPreferences
|
||||
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToExternalWallets) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForAddingContacts =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForAddingContacts) ?? false;
|
||||
final shouldRequireTOTP2FAForCreatingNewWallets =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForCreatingNewWallets) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForAllSecurityAndBackupSettings = sharedPreferences
|
||||
.getBool(PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings) ??
|
||||
false;
|
||||
final useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? false;
|
||||
final totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? '';
|
||||
final tokenTrialNumber = sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? 0;
|
||||
final shouldShowMarketPlaceInDashboard =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ?? true;
|
||||
|
@ -816,18 +799,14 @@ abstract class SettingsStoreBase with Store {
|
|||
actionListDisplayMode.addAll(deserializeActionlistDisplayModes(
|
||||
sharedPreferences.getInt(PreferencesKey.displayActionListModeKey) ?? defaultActionsMode));
|
||||
var pinLength = sharedPreferences.getInt(PreferencesKey.currentPinLength);
|
||||
final timeOutDuration = sharedPreferences.getInt(PreferencesKey.pinTimeOutDuration);
|
||||
final seedPhraseCount = sharedPreferences.getInt(PreferencesKey.currentSeedPhraseLength);
|
||||
final pinCodeTimeOutDuration = timeOutDuration != null
|
||||
? PinCodeRequiredDuration.deserialize(raw: timeOutDuration)
|
||||
: defaultPinCodeTimeOutDuration;
|
||||
final seedPhraseWordCount = seedPhraseCount != null
|
||||
? SeedPhraseLength.deserialize(raw: seedPhraseCount)
|
||||
: defaultSeedPhraseLength;
|
||||
final sortBalanceBy =
|
||||
SortBalanceBy.values[sharedPreferences.getInt(PreferencesKey.sortBalanceBy) ?? 0];
|
||||
final pinNativeTokenAtTop =
|
||||
sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true;
|
||||
final seedPhraseCount = sharedPreferences.getInt(PreferencesKey.currentSeedPhraseLength);
|
||||
final seedPhraseWordCount = seedPhraseCount != null
|
||||
? SeedPhraseLength.deserialize(raw: seedPhraseCount)
|
||||
: defaultSeedPhraseLength;
|
||||
final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
|
||||
final usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
|
||||
final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
|
||||
|
@ -929,7 +908,101 @@ abstract class SettingsStoreBase with Store {
|
|||
});
|
||||
final savedSyncAll = sharedPreferences.getBool(PreferencesKey.syncAllKey) ?? true;
|
||||
|
||||
// migrated to secure:
|
||||
final timeOutDuration = await SecureKey.getInt(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.pinTimeOutDuration,
|
||||
);
|
||||
|
||||
final pinCodeTimeOutDuration = timeOutDuration != null
|
||||
? PinCodeRequiredDuration.deserialize(raw: timeOutDuration)
|
||||
: defaultPinCodeTimeOutDuration;
|
||||
|
||||
final allowBiometricalAuthentication = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.pinTimeOutDuration,
|
||||
) ??
|
||||
false;
|
||||
|
||||
final selectedCake2FAPreset = Cake2FAPresetsOptions.deserialize(
|
||||
raw: await SecureKey.getInt(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.selectedCake2FAPreset,
|
||||
) ??
|
||||
Cake2FAPresetsOptions.normal.raw);
|
||||
|
||||
final shouldRequireTOTP2FAForAccessingWallet = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForAccessingWallet,
|
||||
) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForSendsToContact = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToContact,
|
||||
) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForSendsToNonContact = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToNonContact,
|
||||
) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForSendsToInternalWallets = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToInternalWallets,
|
||||
) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForExchangesToInternalWallets = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForExchangesToInternalWallets,
|
||||
) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForExchangesToExternalWallets = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForExchangesToExternalWallets,
|
||||
) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForAddingContacts = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForAddingContacts,
|
||||
) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForCreatingNewWallets = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
) ??
|
||||
false;
|
||||
final shouldRequireTOTP2FAForAllSecurityAndBackupSettings = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
) ??
|
||||
false;
|
||||
final useTOTP2FA = await SecureKey.getBool(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.useTOTP2FA,
|
||||
) ??
|
||||
false;
|
||||
final totpSecretKey = await SecureKey.getString(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.totpSecretKey,
|
||||
) ??
|
||||
'';
|
||||
|
||||
return SettingsStore(
|
||||
secureStorage: secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard,
|
||||
nodes: nodes,
|
||||
|
@ -1007,34 +1080,37 @@ abstract class SettingsStoreBase with Store {
|
|||
priority[WalletType.monero] = monero?.deserializeMoneroTransactionPriority(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ??
|
||||
priority[WalletType.monero]!;
|
||||
priority[WalletType.bitcoin] = bitcoin?.deserializeBitcoinTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ??
|
||||
priority[WalletType.bitcoin]!;
|
||||
|
||||
if (sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) {
|
||||
priority[WalletType.haven] = monero?.deserializeMoneroTransactionPriority(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!) ??
|
||||
priority[WalletType.haven]!;
|
||||
if (bitcoin != null &&
|
||||
sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority) != null) {
|
||||
priority[WalletType.bitcoin] = bitcoin!.deserializeBitcoinTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority)!);
|
||||
}
|
||||
if (sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority) != null) {
|
||||
priority[WalletType.litecoin] = bitcoin?.deserializeLitecoinTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!) ??
|
||||
priority[WalletType.litecoin]!;
|
||||
|
||||
if (monero != null &&
|
||||
sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) {
|
||||
priority[WalletType.haven] = monero!.deserializeMoneroTransactionPriority(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!);
|
||||
}
|
||||
if (sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority) != null) {
|
||||
priority[WalletType.ethereum] = ethereum?.deserializeEthereumTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority)!) ??
|
||||
priority[WalletType.ethereum]!;
|
||||
if (bitcoin != null &&
|
||||
sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority) != null) {
|
||||
priority[WalletType.litecoin] = bitcoin!.deserializeLitecoinTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!);
|
||||
}
|
||||
if (sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority) != null) {
|
||||
priority[WalletType.polygon] = polygon?.deserializePolygonTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority)!) ??
|
||||
priority[WalletType.polygon]!;
|
||||
if (ethereum != null &&
|
||||
sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority) != null) {
|
||||
priority[WalletType.ethereum] = ethereum!.deserializeEthereumTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority)!);
|
||||
}
|
||||
if (sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
|
||||
priority[WalletType.bitcoinCash] = bitcoinCash?.deserializeBitcoinCashTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!) ??
|
||||
priority[WalletType.bitcoinCash]!;
|
||||
if (polygon != null &&
|
||||
sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority) != null) {
|
||||
priority[WalletType.polygon] = polygon!.deserializePolygonTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.polygonTransactionPriority)!);
|
||||
}
|
||||
if (bitcoinCash != null &&
|
||||
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
|
||||
priority[WalletType.bitcoinCash] = bitcoinCash!.deserializeBitcoinCashTransactionPriority(
|
||||
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!);
|
||||
}
|
||||
|
||||
final generateSubaddresses =
|
||||
|
@ -1055,8 +1131,6 @@ abstract class SettingsStoreBase with Store {
|
|||
shouldSaveRecipientAddress =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ??
|
||||
shouldSaveRecipientAddress;
|
||||
useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? useTOTP2FA;
|
||||
totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? totpSecretKey;
|
||||
numberOfFailedTokenTrials =
|
||||
sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials;
|
||||
isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure;
|
||||
|
@ -1065,41 +1139,10 @@ abstract class SettingsStoreBase with Store {
|
|||
walletListOrder =
|
||||
WalletListOrderType.values[sharedPreferences.getInt(PreferencesKey.walletListOrder) ?? 0];
|
||||
walletListAscending = sharedPreferences.getBool(PreferencesKey.walletListAscending) ?? true;
|
||||
allowBiometricalAuthentication =
|
||||
sharedPreferences.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
|
||||
allowBiometricalAuthentication;
|
||||
selectedCake2FAPreset = Cake2FAPresetsOptions.deserialize(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.selectedCake2FAPreset) ??
|
||||
Cake2FAPresetsOptions.normal.raw);
|
||||
shouldRequireTOTP2FAForAccessingWallet =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForAccessingWallet) ?? false;
|
||||
shouldRequireTOTP2FAForSendsToContact =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToContact) ?? false;
|
||||
shouldRequireTOTP2FAForSendsToNonContact =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToNonContact) ?? false;
|
||||
shouldRequireTOTP2FAForSendsToInternalWallets =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToInternalWallets) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForExchangesToInternalWallets = sharedPreferences
|
||||
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForExchangesToExternalWallets = sharedPreferences
|
||||
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToExternalWallets) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForAddingContacts =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForAddingContacts) ?? false;
|
||||
shouldRequireTOTP2FAForCreatingNewWallets =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForCreatingNewWallets) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForAllSecurityAndBackupSettings = sharedPreferences
|
||||
.getBool(PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings) ??
|
||||
false;
|
||||
|
||||
shouldShowMarketPlaceInDashboard =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ??
|
||||
shouldShowMarketPlaceInDashboard;
|
||||
selectedCake2FAPreset = Cake2FAPresetsOptions.deserialize(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.selectedCake2FAPreset) ??
|
||||
Cake2FAPresetsOptions.narrow.raw);
|
||||
exchangeStatus = ExchangeApiMode.deserialize(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.exchangeStatusKey) ??
|
||||
ExchangeApiMode.enabled.raw);
|
||||
|
@ -1147,7 +1190,6 @@ abstract class SettingsStoreBase with Store {
|
|||
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||
final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
|
||||
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||
final moneroNode = nodeSource.get(nodeId);
|
||||
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
|
||||
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
|
||||
|
@ -1188,6 +1230,93 @@ abstract class SettingsStoreBase with Store {
|
|||
if (nanoNode != null) {
|
||||
nodes[WalletType.nano] = nanoNode;
|
||||
}
|
||||
|
||||
// MIGRATED:
|
||||
|
||||
useTOTP2FA = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.useTOTP2FA,
|
||||
) ??
|
||||
useTOTP2FA;
|
||||
|
||||
totpSecretKey = await SecureKey.getString(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.totpSecretKey,
|
||||
) ??
|
||||
totpSecretKey;
|
||||
|
||||
allowBiometricalAuthentication = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.allowBiometricalAuthenticationKey,
|
||||
) ??
|
||||
allowBiometricalAuthentication;
|
||||
|
||||
selectedCake2FAPreset = Cake2FAPresetsOptions.deserialize(
|
||||
raw: await SecureKey.getInt(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.selectedCake2FAPreset,
|
||||
) ??
|
||||
Cake2FAPresetsOptions.normal.raw);
|
||||
|
||||
shouldRequireTOTP2FAForAccessingWallet = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForAccessingWallet,
|
||||
) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForSendsToContact = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToContact,
|
||||
) ??
|
||||
false;
|
||||
|
||||
shouldRequireTOTP2FAForSendsToNonContact = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToNonContact,
|
||||
) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForSendsToInternalWallets = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForSendsToInternalWallets,
|
||||
) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForExchangesToInternalWallets = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForExchangesToInternalWallets,
|
||||
) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForExchangesToExternalWallets = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForExchangesToExternalWallets,
|
||||
) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForAddingContacts = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForAddingContacts,
|
||||
) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForCreatingNewWallets = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
) ??
|
||||
false;
|
||||
shouldRequireTOTP2FAForAllSecurityAndBackupSettings = await SecureKey.getBool(
|
||||
secureStorage: _secureStorage,
|
||||
sharedPreferences: sharedPreferences,
|
||||
key: SecureKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
) ??
|
||||
false;
|
||||
}
|
||||
|
||||
Future<void> _saveCurrentNode(Node node, WalletType walletType) async {
|
||||
|
|
|
@ -265,7 +265,7 @@ abstract class OutputBase with Store {
|
|||
Future<void> fetchParsedAddress(BuildContext context) async {
|
||||
final domain = address;
|
||||
final ticker = cryptoCurrencyHandler().title.toLowerCase();
|
||||
parsedAddress = await getIt.get<AddressResolver>().resolve(domain, ticker);
|
||||
parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, ticker);
|
||||
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
|
||||
note = parsedAddress.description;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@ abstract class Setup2FAViewModelBase with Store {
|
|||
unhighlightTabs = false,
|
||||
selected2FASettings = ObservableList<VerboseControlSettings>(),
|
||||
state = InitialExecutionState() {
|
||||
selectCakePreset(selectedCake2FAPreset);
|
||||
if (selectedCake2FAPreset != Cake2FAPresetsOptions.none) {
|
||||
selectCakePreset(selectedCake2FAPreset);
|
||||
}
|
||||
reaction((_) => state, _saveLastAuthTime);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,11 @@ abstract class PrivacySettingsViewModelBase with Store {
|
|||
}
|
||||
}
|
||||
|
||||
bool get isAutoGenerateSubaddressesVisible => _wallet.type == WalletType.monero;
|
||||
bool get isAutoGenerateSubaddressesVisible =>
|
||||
_wallet.type == WalletType.monero ||
|
||||
_wallet.type == WalletType.bitcoin ||
|
||||
_wallet.type == WalletType.litecoin ||
|
||||
_wallet.type == WalletType.bitcoinCash;
|
||||
|
||||
@computed
|
||||
bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/entities/biometric_auth.dart';
|
||||
import 'package:cake_wallet/entities/pin_code_required_duration.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
|
@ -9,14 +8,10 @@ part 'security_settings_view_model.g.dart';
|
|||
class SecuritySettingsViewModel = SecuritySettingsViewModelBase with _$SecuritySettingsViewModel;
|
||||
|
||||
abstract class SecuritySettingsViewModelBase with Store {
|
||||
SecuritySettingsViewModelBase(
|
||||
this._settingsStore,
|
||||
this._authService,
|
||||
) : _biometricAuth = BiometricAuth();
|
||||
SecuritySettingsViewModelBase(this._settingsStore) : _biometricAuth = BiometricAuth();
|
||||
|
||||
final BiometricAuth _biometricAuth;
|
||||
final SettingsStore _settingsStore;
|
||||
final AuthService _authService;
|
||||
|
||||
@computed
|
||||
bool get allowBiometricalAuthentication => _settingsStore.allowBiometricalAuthentication;
|
||||
|
@ -41,8 +36,6 @@ abstract class SecuritySettingsViewModelBase with Store {
|
|||
_settingsStore.allowBiometricalAuthentication = value;
|
||||
|
||||
@action
|
||||
setPinCodeRequiredDuration(PinCodeRequiredDuration duration) =>
|
||||
void setPinCodeRequiredDuration(PinCodeRequiredDuration duration) =>
|
||||
_settingsStore.pinTimeOutDuration = duration;
|
||||
|
||||
bool checkPinCodeRiquired() => _authService.requireAuth();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
|
@ -33,7 +32,7 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
|||
state = AddressEditOrCreateStateInitial(),
|
||||
label = item?.name ?? '',
|
||||
_item = item,
|
||||
_wallet = wallet;
|
||||
_wallet = wallet;
|
||||
|
||||
@observable
|
||||
AddressEditOrCreateState state;
|
||||
|
@ -46,6 +45,10 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
|||
final WalletAddressListItem? _item;
|
||||
final WalletBase _wallet;
|
||||
|
||||
bool get isElectrum => _wallet.type == WalletType.bitcoin ||
|
||||
_wallet.type == WalletType.bitcoinCash ||
|
||||
_wallet.type == WalletType.litecoin;
|
||||
|
||||
Future<void> save() async {
|
||||
try {
|
||||
state = AddressIsSaving();
|
||||
|
@ -65,12 +68,7 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
|||
Future<void> _createNew() async {
|
||||
final wallet = _wallet;
|
||||
|
||||
if (wallet.type == WalletType.bitcoin
|
||||
|| wallet.type == WalletType.litecoin
|
||||
|| wallet.type == WalletType.bitcoinCash) {
|
||||
await bitcoin!.generateNewAddress(wallet);
|
||||
await wallet.save();
|
||||
}
|
||||
if (isElectrum) await bitcoin!.generateNewAddress(wallet, label);
|
||||
|
||||
if (wallet.type == WalletType.monero) {
|
||||
await monero
|
||||
|
@ -96,10 +94,8 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
|||
Future<void> _update() async {
|
||||
final wallet = _wallet;
|
||||
|
||||
/*if (wallet is BitcoinWallet) {
|
||||
await wallet.walletAddresses.updateAddress(_item.address as String);
|
||||
await wallet.save();
|
||||
}*/
|
||||
if (isElectrum) await bitcoin!.updateAddress(wallet, _item!.address, label);
|
||||
|
||||
final index = _item?.id;
|
||||
if (index != null) {
|
||||
if (wallet.type == WalletType.monero) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/utils/list_item.dart';
|
||||
|
||||
class WalletAddressListItem extends ListItem {
|
||||
|
@ -6,13 +5,19 @@ class WalletAddressListItem extends ListItem {
|
|||
required this.address,
|
||||
required this.isPrimary,
|
||||
this.id,
|
||||
this.name})
|
||||
this.name,
|
||||
this.txCount,
|
||||
this.balance,
|
||||
this.isChange = false})
|
||||
: super();
|
||||
|
||||
final int? id;
|
||||
final bool isPrimary;
|
||||
final String address;
|
||||
final String? name;
|
||||
final int? txCount;
|
||||
final String? balance;
|
||||
final bool isChange;
|
||||
|
||||
@override
|
||||
String toString() => name ?? address;
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/haven/haven.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/utils/list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:cw_core/amount_converter.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/haven/haven.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'wallet_address_list_view_model.g.dart';
|
||||
|
||||
|
@ -110,7 +114,8 @@ class EthereumURI extends PaymentURI {
|
|||
|
||||
class BitcoinCashURI extends PaymentURI {
|
||||
BitcoinCashURI({required String amount, required String address})
|
||||
: super(amount: amount, address: address);
|
||||
: super(amount: amount, address: address);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var base = address;
|
||||
|
@ -121,9 +126,7 @@ class BitcoinCashURI extends PaymentURI {
|
|||
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class NanoURI extends PaymentURI {
|
||||
NanoURI({required String amount, required String address})
|
||||
|
@ -167,6 +170,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
hasAccounts =
|
||||
appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven,
|
||||
amount = '',
|
||||
_settingsStore = appStore.settingsStore,
|
||||
super(appStore: appStore) {
|
||||
_init();
|
||||
}
|
||||
|
@ -184,12 +188,28 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
final NumberFormat _cryptoNumberFormat;
|
||||
|
||||
final FiatConversionStore fiatConversionStore;
|
||||
final SettingsStore _settingsStore;
|
||||
|
||||
List<Currency> get currencies => [walletTypeToCryptoCurrency(wallet.type), ...FiatCurrency.all];
|
||||
|
||||
String get buttonTitle {
|
||||
if (isElectrumWallet) {
|
||||
return S.current.addresses;
|
||||
}
|
||||
|
||||
if (isAutoGenerateSubaddressEnabled) {
|
||||
return hasAccounts ? S.current.accounts : S.current.account;
|
||||
}
|
||||
|
||||
return hasAccounts ? S.current.accounts_subaddresses : S.current.addresses;
|
||||
}
|
||||
|
||||
@observable
|
||||
Currency selectedCurrency;
|
||||
|
||||
@observable
|
||||
String searchText = '';
|
||||
|
||||
@computed
|
||||
int get selectedCurrencyIndex => currencies.indexOf(selectedCurrency);
|
||||
|
||||
|
@ -277,14 +297,21 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
addressList.addAll(addressItems);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.bitcoin) {
|
||||
final primaryAddress = bitcoin!.getAddress(wallet);
|
||||
final bitcoinAddresses = bitcoin!.getAddresses(wallet).map((addr) {
|
||||
final isPrimary = addr == primaryAddress;
|
||||
if (isElectrumWallet) {
|
||||
final addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) {
|
||||
final isPrimary = subaddress.id == 0;
|
||||
|
||||
return WalletAddressListItem(isPrimary: isPrimary, name: null, address: addr);
|
||||
return WalletAddressListItem(
|
||||
id: subaddress.id,
|
||||
isPrimary: isPrimary,
|
||||
name: subaddress.name,
|
||||
address: subaddress.address,
|
||||
txCount: subaddress.txCount,
|
||||
balance: AmountConverter.amountIntToString(
|
||||
walletTypeToCryptoCurrency(type), subaddress.balance),
|
||||
isChange: subaddress.isChange);
|
||||
});
|
||||
addressList.addAll(bitcoinAddresses);
|
||||
addressList.addAll(addressItems);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.ethereum) {
|
||||
|
@ -299,6 +326,15 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
||||
}
|
||||
|
||||
if (searchText.isNotEmpty) {
|
||||
return ObservableList.of(addressList.where((item) {
|
||||
if (item is WalletAddressListItem) {
|
||||
return item.address.toLowerCase().contains(searchText.toLowerCase());
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
|
||||
return addressList;
|
||||
}
|
||||
|
||||
|
@ -321,15 +357,23 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
@computed
|
||||
bool get hasAddressList =>
|
||||
wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.haven;/* ||
|
||||
wallet.type == WalletType.nano ||
|
||||
wallet.type == WalletType.banano;*/// TODO: nano accounts are disabled for now
|
||||
wallet.type == WalletType.haven ||
|
||||
wallet.type == WalletType.bitcoinCash ||
|
||||
wallet.type == WalletType.bitcoin ||
|
||||
wallet.type == WalletType.litecoin;
|
||||
|
||||
// wallet.type == WalletType.nano ||
|
||||
// wallet.type == WalletType.banano; TODO: nano accounts are disabled for now
|
||||
|
||||
@computed
|
||||
bool get showElectrumAddressDisclaimer =>
|
||||
bool get isElectrumWallet =>
|
||||
wallet.type == WalletType.bitcoin ||
|
||||
wallet.type == WalletType.litecoin ||
|
||||
wallet.type == WalletType.bitcoinCash;
|
||||
wallet.type == WalletType.litecoin ||
|
||||
wallet.type == WalletType.bitcoinCash;
|
||||
|
||||
@computed
|
||||
bool get isAutoGenerateSubaddressEnabled =>
|
||||
_settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled;
|
||||
|
||||
List<ListItem> _baseItems;
|
||||
|
||||
|
@ -343,9 +387,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
_baseItems = [];
|
||||
|
||||
if (wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.haven /*||
|
||||
wallet.type ==
|
||||
WalletType
|
||||
.haven /*||
|
||||
wallet.type == WalletType.nano ||
|
||||
wallet.type == WalletType.banano*/) {
|
||||
wallet.type == WalletType.banano*/
|
||||
) {
|
||||
_baseItems.add(WalletAccountListHeader());
|
||||
}
|
||||
|
||||
|
@ -367,6 +414,11 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void updateSearchText(String text) {
|
||||
searchText = text;
|
||||
}
|
||||
|
||||
void _convertAmountToCrypto() {
|
||||
final cryptoCurrency = walletTypeToCryptoCurrency(wallet.type);
|
||||
try {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||
import 'package:cake_wallet/entities/wallet_list_order_types.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
@ -18,7 +17,6 @@ abstract class WalletListViewModelBase with Store {
|
|||
this._walletInfoSource,
|
||||
this._appStore,
|
||||
this._walletLoadingService,
|
||||
this._authService,
|
||||
) : wallets = ObservableList<WalletListItem>() {
|
||||
setOrderType(_appStore.settingsStore.walletListOrder);
|
||||
reaction((_) => _appStore.wallet, (_) => updateList());
|
||||
|
@ -39,7 +37,6 @@ abstract class WalletListViewModelBase with Store {
|
|||
final AppStore _appStore;
|
||||
final Box<WalletInfo> _walletInfoSource;
|
||||
final WalletLoadingService _walletLoadingService;
|
||||
final AuthService _authService;
|
||||
|
||||
WalletType get currentWalletType => _appStore.wallet!.type;
|
||||
|
||||
|
@ -160,8 +157,4 @@ abstract class WalletListViewModelBase with Store {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool checkIfAuthRequired() {
|
||||
return _authService.requireAuth();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ EXTERNAL SOURCES:
|
|||
|
||||
SPEC CHECKSUMS:
|
||||
connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308
|
||||
cw_monero: ec03de55a19c4a2b174ea687e0f4202edc716fa4
|
||||
cw_monero: f8b7f104508efba2591548e76b5c058d05cba3f0
|
||||
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
|
||||
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
|
||||
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d
|
||||
|
|
|
@ -105,6 +105,7 @@ dependencies:
|
|||
socks5_proxy: ^1.0.4
|
||||
flutter_svg: ^2.0.9
|
||||
polyseed: ^0.0.2
|
||||
nostr_tools: ^1.0.9
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -123,6 +124,11 @@ dev_dependencies:
|
|||
url: https://github.com/cake-tech/google-translator.git
|
||||
version: 1.0.0
|
||||
|
||||
dependency_overrides:
|
||||
bech32:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bech32.git
|
||||
|
||||
flutter_icons:
|
||||
image_path: "assets/images/app_logo.png"
|
||||
android: true
|
||||
|
|
|
@ -756,12 +756,18 @@
|
|||
"dfx_option_description": "ﺎﺑﻭﺭﻭﺃ ﻲﻓ ﺕﺎﻛﺮﺸﻟﺍﻭ ﺔﺋﺰﺠﺘﻟﺍ ءﻼﻤﻌﻟ .ﻲﻓﺎﺿﺇ KYC ﻥﻭﺪﺑ ﻭﺭﻮﻳ 990 ﻰﻟﺇ ﻞﺼﻳ ﺎﻣ .ﻱﺮﺴﻳﻮﺴﻟﺍ",
|
||||
"polygonscan_history": "ﻥﺎﻜﺴﻧﻮﺠﻴﻟﻮﺑ ﺦﻳﺭﺎﺗ",
|
||||
"wallet_seed_legacy": "بذرة محفظة قديمة",
|
||||
"default_sell_provider": " ﻲﺿﺍﺮﺘﻓﻻﺍ ﻊﻴﺒﻟﺍ ﺩﻭﺰﻣ",
|
||||
"default_sell_provider": "ﻲﺿﺍﺮﺘﻓﻻﺍ ﻊﻴﺒﻟﺍ ﺩﻭﺰﻣ",
|
||||
"select_sell_provider_notice": ".ﻖﻴﺒﻄﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻚﺑ ﺹﺎﺨﻟﺍ ﻲﺿﺍﺮﺘﻓﻻﺍ ﻊﻴﺒﻟﺍ ﺩﻭﺰﻣ ﻦﻴﻴﻌﺗ ﻖﻳﺮﻃ ﻦﻋ ﺔﺷﺎﺸﻟﺍ ﻩﺬﻫ ﻲﻄﺨﺗ",
|
||||
"custom_drag": "مخصص (عقد وسحب)",
|
||||
"switchToEVMCompatibleWallet": " (Ethereum، Polygon) ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ EVM ﻊﻣ ﺔﻘﻓﺍﻮﺘﻣ ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ",
|
||||
"receivable_balance": "التوازن القادم",
|
||||
"confirmed_tx": "مؤكد",
|
||||
"transaction_details_source_address": "عنوان المصدر",
|
||||
"track": " ﺭﺎﺴﻣ"
|
||||
"track": " ﺭﺎﺴﻣ",
|
||||
"pause_wallet_creation": ".ﺎﻴًﻟﺎﺣ ﺎﺘًﻗﺆﻣ ﺔﻔﻗﻮﺘﻣ Haven Wallet ءﺎﺸﻧﺇ ﻰﻠﻋ ﺓﺭﺪﻘﻟﺍ",
|
||||
"camera_consent": ".ﻞﻴﺻﺎﻔﺘﻟﺍ ﻰﻠﻋ ﻝﻮﺼﺤﻠﻟ ﻢﻬﺑ ﺔﺻﺎﺨﻟﺍ ﺔﻴﺻﻮﺼﺨﻟﺍ ﺔﺳﺎﻴﺳ ﻦﻣ ﻖﻘﺤﺘﻟﺍ ﻰﺟﺮﻳ .${provider} ﻝﻮﻠ",
|
||||
"no_relays": " ﺕﻼﺣﺮﻤﻟﺍ ﻻ",
|
||||
"choose_relay": " ﻡﺍﺪﺨﺘﺳﻼﻟ ﻊﺑﺎﺘﺘﻟﺍ ﺭﺎﻴﺘﺧﺍ ءﺎﺟﺮﻟﺍ",
|
||||
"no_relays_message": ".ﻪﺑ ﺹﺎﺨﻟﺍ Nostr ﻞﺠﺳ ﻰﻟﺇ ﺕﻼﺣﺮﻤﻟﺍ ﺔﻓﺎﺿﻹ ﻢﻠﺘﺴﻤﻟﺍ ﺩﺎﺷﺭﺇ ﻰﺟﺮﻳ .ﺕﻼﺣﺮﻣ ﻱﺃ ﻰﻠﻋ ﻱﻮﺘﺤﻳ ﻻ",
|
||||
"no_relay_on_domain": ".ﻡﺍﺪﺨﺘﺳﻼﻟ ﻊﺑﺎﺘﺘﻟﺍ ﺭﺎﻴﺘﺧﺍ ءﺎﺟﺮﻟﺍ .ﺡﺎﺘﻣ ﺮﻴﻏ ﻞﻴﺣﺮﺘﻟﺍ ﻥﺃ ﻭﺃ ﻡﺪﺨﺘﺴﻤﻟﺍ ﻝﺎﺠﻤﻟ ﻞﻴﺣﺮﺗ ﺪ"
|
||||
}
|
||||
|
|
|
@ -759,5 +759,11 @@
|
|||
"receivable_balance": "Баланс за вземания",
|
||||
"confirmed_tx": "Потвърдено",
|
||||
"transaction_details_source_address": "Адрес на източника",
|
||||
"track": "Писта"
|
||||
"track": "Писта",
|
||||
"pause_wallet_creation": "Възможността за създаване на Haven Wallet в момента е на пауза.",
|
||||
"camera_consent": "Вашият фотоапарат ще бъде използван за заснемане на изображение с цел идентификация от ${provider}. Моля, проверете тяхната политика за поверителност за подробности.",
|
||||
"no_relays": "Без релета",
|
||||
"choose_relay": "Моля, изберете реле, което да използвате",
|
||||
"no_relays_message": "Намерихме запис Nostr NIP-05 за този потребител, но той не съдържа релета. Моля, инструктирайте получателя да добави релета към своя Nostr запис.",
|
||||
"no_relay_on_domain": "Няма реле за домейна на потребителя или релето не е налично. Моля, изберете реле, което да използвате."
|
||||
}
|
||||
|
|
|
@ -759,5 +759,11 @@
|
|||
"receivable_balance": "Zůstatek pohledávek",
|
||||
"confirmed_tx": "Potvrzeno",
|
||||
"transaction_details_source_address": "Zdrojová adresa",
|
||||
"track": "Dráha"
|
||||
"track": "Dráha",
|
||||
"pause_wallet_creation": "Možnost vytvářet Haven Wallet je momentálně pozastavena.",
|
||||
"camera_consent": "Váš fotoaparát použije k pořízení snímku pro účely identifikace ${provider}. Podrobnosti najdete v jejich Zásadách ochrany osobních údajů.",
|
||||
"no_relays": "Žádná relé",
|
||||
"choose_relay": "Vyberte relé, které chcete použít",
|
||||
"no_relays_message": "Pro tohoto uživatele jsme našli záznam Nostr NIP-05, který však neobsahuje žádná relé. Požádejte příjemce, aby přidal přenosy do svého záznamu Nostr.",
|
||||
"no_relay_on_domain": "Pro doménu uživatele neexistuje přenos nebo je přenos nedostupný. Vyberte relé, které chcete použít."
|
||||
}
|
||||
|
|
|
@ -767,5 +767,11 @@
|
|||
"receivable_balance": "Forderungsbilanz",
|
||||
"confirmed_tx": "Bestätigt",
|
||||
"transaction_details_source_address": "Quelladresse",
|
||||
"track": "Schiene"
|
||||
"track": "Schiene",
|
||||
"pause_wallet_creation": "Die Möglichkeit, Haven Wallet zu erstellen, ist derzeit pausiert.",
|
||||
"camera_consent": "Mit Ihrer Kamera wird bis zum ${provider} ein Bild zur Identifizierung aufgenommen. Weitere Informationen finden Sie in deren Datenschutzbestimmungen.",
|
||||
"no_relays": "Keine Relais",
|
||||
"choose_relay": "Bitte wählen Sie ein zu verwendendes Relais aus",
|
||||
"no_relays_message": "Wir haben einen Nostr NIP-05-Eintrag für diesen Benutzer gefunden, der jedoch keine Relays enthält. Bitte weisen Sie den Empfänger an, Relays zu seinem Nostr-Datensatz hinzuzufügen.",
|
||||
"no_relay_on_domain": "Es gibt kein Relay für die Domäne des Benutzers oder das Relay ist nicht verfügbar. Bitte wählen Sie ein zu verwendendes Relais aus."
|
||||
}
|
||||
|
|
|
@ -768,5 +768,11 @@
|
|||
"receivable_balance": "Receivable Balance",
|
||||
"confirmed_tx": "Confirmed",
|
||||
"transaction_details_source_address": "Source address",
|
||||
"track": "Track"
|
||||
"track": "Track",
|
||||
"pause_wallet_creation": "Ability to create Haven Wallet is currently paused.",
|
||||
"camera_consent": "Your camera will be used to capture an image for identification purposes by ${provider}. Please check their Privacy Policy for details.",
|
||||
"no_relays": "No relays",
|
||||
"choose_relay": "Please choose a relay to use",
|
||||
"no_relays_message": "We found a Nostr NIP-05 record for this user, but it does not contain any relays. Please instruct the recipient to add relays to their Nostr record.",
|
||||
"no_relay_on_domain": "There isn't a relay for user's domain or the relay is unavailable. Please choose a relay to use."
|
||||
}
|
||||
|
|
|
@ -767,5 +767,11 @@
|
|||
"receivable_balance": "Saldo de cuentas por cobrar",
|
||||
"confirmed_tx": "Confirmado",
|
||||
"transaction_details_source_address": "Dirección de la fuente",
|
||||
"track": "Pista"
|
||||
"track": "Pista",
|
||||
"pause_wallet_creation": "La capacidad para crear Haven Wallet está actualmente pausada.",
|
||||
"camera_consent": "Su cámara será utilizada para capturar una imagen con fines de identificación por ${provider}. Consulte su Política de privacidad para obtener más detalles.",
|
||||
"no_relays": "Sin relevos",
|
||||
"choose_relay": "Por favor elija un relé para usar",
|
||||
"no_relays_message": "Encontramos un registro Nostr NIP-05 para este usuario, pero no contiene ningún relé. Indique al destinatario que agregue retransmisiones a su registro Nostr.",
|
||||
"no_relay_on_domain": "No hay una retransmisión para el dominio del usuario o la retransmisión no está disponible. Elija un relé para usar."
|
||||
}
|
||||
|
|
|
@ -767,5 +767,11 @@
|
|||
"receivable_balance": "Solde de créances",
|
||||
"confirmed_tx": "Confirmé",
|
||||
"transaction_details_source_address": "Adresse source",
|
||||
"track": "Piste"
|
||||
"track": "Piste",
|
||||
"pause_wallet_creation": "La possibilité de créer Haven Wallet est actuellement suspendue.",
|
||||
"camera_consent": "Votre appareil photo sera utilisé pour capturer une image à des fins d'identification par ${provider}. Veuillez consulter leur politique de confidentialité pour plus de détails.",
|
||||
"no_relays": "Pas de relais",
|
||||
"choose_relay": "Veuillez choisir un relais à utiliser",
|
||||
"no_relays_message": "Nous avons trouvé un enregistrement Nostr NIP-05 pour cet utilisateur, mais il ne contient aucun relais. Veuillez demander au destinataire d'ajouter des relais à son enregistrement Nostr.",
|
||||
"no_relay_on_domain": "Il n'existe pas de relais pour le domaine de l'utilisateur ou le relais n'est pas disponible. Veuillez choisir un relais à utiliser."
|
||||
}
|
||||
|
|
|
@ -749,5 +749,11 @@
|
|||
"receivable_balance": "Daidaituwa da daidaituwa",
|
||||
"confirmed_tx": "Tabbatar",
|
||||
"transaction_details_source_address": "Adireshin Incord",
|
||||
"track": "Waƙa"
|
||||
"track": "Waƙa",
|
||||
"pause_wallet_creation": "A halin yanzu an dakatar da ikon ƙirƙirar Haven Wallet.",
|
||||
"camera_consent": "Za a yi amfani da kyamarar ku don ɗaukar hoto don dalilai na tantancewa ta ${provider}. Da fatan za a duba Manufar Sirri don cikakkun bayanai.",
|
||||
"no_relays": "Babu relays",
|
||||
"choose_relay": "Da fatan za a zaɓi gudun ba da sanda don amfani",
|
||||
"no_relays_message": "Mun sami rikodin Nostr NIP-05 don wannan mai amfani, amma ba ya ƙunshe da kowane relays. Da fatan za a umurci mai karɓa ya ƙara relays zuwa rikodin su na Nostr.",
|
||||
"no_relay_on_domain": "Babu gudun ba da sanda ga yankin mai amfani ko kuma ba a samu ba. Da fatan za a zaɓi gudun ba da sanda don amfani."
|
||||
}
|
||||
|
|
|
@ -767,5 +767,11 @@
|
|||
"receivable_balance": "प्राप्य शेष",
|
||||
"confirmed_tx": "की पुष्टि",
|
||||
"transaction_details_source_address": "स्रोत पता",
|
||||
"track": "रास्ता"
|
||||
"track": "रास्ता",
|
||||
"pause_wallet_creation": "हेवन वॉलेट बनाने की क्षमता फिलहाल रुकी हुई है।",
|
||||
"camera_consent": "आपके कैमरे का उपयोग ${provider} द्वारा पहचान उद्देश्यों के लिए एक छवि कैप्चर करने के लिए किया जाएगा। विवरण के लिए कृपया उनकी गोपनीयता नीति जांचें।",
|
||||
"no_relays": "कोई रिले नहीं",
|
||||
"choose_relay": "कृपया उपयोग करने के लिए एक रिले चुनें",
|
||||
"no_relays_message": "हमें इस उपयोगकर्ता के लिए एक Nostr NIP-05 रिकॉर्ड मिला, लेकिन इसमें कोई रिले नहीं है। कृपया प्राप्तकर्ता को अपने नॉस्ट्र रिकॉर्ड में रिले जोड़ने का निर्देश दें।",
|
||||
"no_relay_on_domain": "उपयोगकर्ता के डोमेन के लिए कोई रिले नहीं है या रिले अनुपलब्ध है। कृपया उपयोग करने के लिए एक रिले चुनें।"
|
||||
}
|
||||
|
|
|
@ -765,5 +765,11 @@
|
|||
"receivable_balance": "Stanje potraživanja",
|
||||
"confirmed_tx": "Potvrđen",
|
||||
"transaction_details_source_address": "Adresa izvora",
|
||||
"track": "Staza"
|
||||
"track": "Staza",
|
||||
"pause_wallet_creation": "Mogućnost stvaranja novčanika Haven trenutno je pauzirana.",
|
||||
"camera_consent": "Vaš će fotoaparat koristiti za snimanje slike u svrhu identifikacije od strane ${provider}. Pojedinosti potražite u njihovoj politici privatnosti.",
|
||||
"no_relays": "Nema releja",
|
||||
"choose_relay": "Odaberite relej za korištenje",
|
||||
"no_relays_message": "Pronašli smo zapis Nostr NIP-05 za ovog korisnika, ali on ne sadrži nikakve releje. Uputite primatelja da doda releje u svoj Nostr zapis.",
|
||||
"no_relay_on_domain": "Ne postoji relej za korisničku domenu ili je relej nedostupan. Odaberite relej za korištenje."
|
||||
}
|
||||
|
|
|
@ -755,5 +755,11 @@
|
|||
"receivable_balance": "Saldo piutang",
|
||||
"confirmed_tx": "Dikonfirmasi",
|
||||
"transaction_details_source_address": "Alamat sumber",
|
||||
"track": "Melacak"
|
||||
"track": "Melacak",
|
||||
"pause_wallet_creation": "Kemampuan untuk membuat Haven Wallet saat ini dijeda.",
|
||||
"camera_consent": "Kamera Anda akan digunakan untuk mengambil gambar untuk tujuan identifikasi oleh ${provider}. Silakan periksa Kebijakan Privasi mereka untuk detailnya.",
|
||||
"no_relays": "Tidak ada relay",
|
||||
"choose_relay": "Silakan pilih relai yang akan digunakan",
|
||||
"no_relays_message": "Kami menemukan catatan Nostr NIP-05 untuk pengguna ini, tetapi tidak berisi relay apa pun. Harap instruksikan penerima untuk menambahkan relay ke catatan Nostr mereka.",
|
||||
"no_relay_on_domain": "Tidak ada relai untuk domain pengguna atau relai tidak tersedia. Silakan pilih relai yang akan digunakan."
|
||||
}
|
||||
|
|
|
@ -767,5 +767,11 @@
|
|||
"receivable_balance": "Bilanciamento creditizio",
|
||||
"confirmed_tx": "Confermato",
|
||||
"transaction_details_source_address": "Indirizzo di partenza",
|
||||
"track": "Traccia"
|
||||
"track": "Traccia",
|
||||
"pause_wallet_creation": "La possibilità di creare Haven Wallet è attualmente sospesa.",
|
||||
"camera_consent": "La tua fotocamera verrà utilizzata per acquisire un'immagine a scopo identificativo da ${provider}. Si prega di controllare la loro Informativa sulla privacy per i dettagli.",
|
||||
"no_relays": "Nessun relè",
|
||||
"choose_relay": "Scegli un relè da utilizzare",
|
||||
"no_relays_message": "Abbiamo trovato un record Nostr NIP-05 per questo utente, ma non contiene alcun relè. Si prega di indicare al destinatario di aggiungere inoltri al proprio record Nostr.",
|
||||
"no_relay_on_domain": "Non esiste un inoltro per il dominio dell'utente oppure l'inoltro non è disponibile. Scegli un relè da utilizzare."
|
||||
}
|
||||
|
|
|
@ -767,5 +767,11 @@
|
|||
"receivable_balance": "売掛金残高",
|
||||
"confirmed_tx": "確認済み",
|
||||
"transaction_details_source_address": "ソースアドレス",
|
||||
"track": "追跡"
|
||||
"track": "追跡",
|
||||
"pause_wallet_creation": "Haven Wallet を作成する機能は現在一時停止されています。",
|
||||
"camera_consent": "あなたのカメラは、${provider}_ までに識別目的で画像を撮影するために使用されます。詳細については、プライバシー ポリシーをご確認ください。",
|
||||
"no_relays": "リレーなし",
|
||||
"choose_relay": "使用するリレーを選択してください",
|
||||
"no_relays_message": "このユーザーの Nostr NIP-05 レコードが見つかりましたが、リレーは含まれていません。受信者に Nostr レコードにリレーを追加するよう指示してください。",
|
||||
"no_relay_on_domain": "ユーザーのドメインのリレーが存在しないか、リレーが使用できません。使用するリレーを選択してください。"
|
||||
}
|
||||
|
|
|
@ -765,5 +765,11 @@
|
|||
"receivable_balance": "채권 잔액",
|
||||
"confirmed_tx": "확인",
|
||||
"transaction_details_source_address": "소스 주소",
|
||||
"track": "길"
|
||||
"track": "길",
|
||||
"pause_wallet_creation": "Haven Wallet 생성 기능이 현재 일시 중지되었습니다.",
|
||||
"camera_consent": "귀하의 카메라는 ${provider}의 식별 목적으로 이미지를 캡처하는 데 사용됩니다. 자세한 내용은 해당 개인정보 보호정책을 확인하세요.",
|
||||
"no_relays": "릴레이 없음",
|
||||
"choose_relay": "사용할 릴레이를 선택해주세요",
|
||||
"no_relays_message": "이 사용자에 대한 Nostr NIP-05 레코드를 찾았지만 릴레이가 포함되어 있지 않습니다. 수신자에게 Nostr 기록에 릴레이를 추가하도록 지시하십시오.",
|
||||
"no_relay_on_domain": "사용자 도메인에 릴레이가 없거나 릴레이를 사용할 수 없습니다. 사용할 릴레이를 선택해주세요."
|
||||
}
|
||||
|
|
|
@ -765,5 +765,11 @@
|
|||
"receivable_balance": "လက်ကျန်ငွေ",
|
||||
"confirmed_tx": "အတည်ပြုသည်",
|
||||
"transaction_details_source_address": "အရင်းအမြစ်လိပ်စာ",
|
||||
"track": "တစ်ပုဒ်"
|
||||
"track": "တစ်ပုဒ်",
|
||||
"pause_wallet_creation": "Haven Wallet ဖန်တီးနိုင်မှုကို လောလောဆယ် ခေတ္တရပ်ထားသည်။",
|
||||
"camera_consent": "မှတ်ပုံတင်ခြင်းရည်ရွယ်ချက်များအတွက် ${provider} တွင် သင့်ကင်မရာကို အသုံးပြုပါမည်။ အသေးစိတ်အတွက် ၎င်းတို့၏ ကိုယ်ရေးကိုယ်တာမူဝါဒကို စစ်ဆေးပါ။",
|
||||
"no_relays": "Relay မရှိပါ။",
|
||||
"choose_relay": "အသုံးပြုရန် relay ကိုရွေးချယ်ပါ။",
|
||||
"no_relays_message": "ဤအသုံးပြုသူအတွက် Nostr NIP-05 မှတ်တမ်းကို ကျွန်ုပ်တို့တွေ့ရှိသော်လည်း ၎င်းတွင် မည်သည့် relays မှ မပါဝင်ပါ။ ကျေးဇူးပြု၍ လက်ခံသူကို ၎င်းတို့၏ Nostr မှတ်တမ်းတွင် ထပ်လောင်းထည့်ရန် ညွှန်ကြားပါ။",
|
||||
"no_relay_on_domain": "အသုံးပြုသူ၏ဒိုမိန်းအတွက် ထပ်ဆင့်လွှင့်ခြင်း မရှိပါ သို့မဟုတ် ထပ်ဆင့်လွှင့်ခြင်း မရနိုင်ပါ။ အသုံးပြုရန် relay ကိုရွေးချယ်ပါ။"
|
||||
}
|
||||
|
|
|
@ -767,5 +767,11 @@
|
|||
"receivable_balance": "Het saldo",
|
||||
"confirmed_tx": "Bevestigd",
|
||||
"transaction_details_source_address": "Bron adres",
|
||||
"track": "Spoor"
|
||||
"track": "Spoor",
|
||||
"pause_wallet_creation": "De mogelijkheid om Haven Wallet te maken is momenteel onderbroken.",
|
||||
"camera_consent": "Uw camera wordt gebruikt om vóór ${provider} een beeld vast te leggen voor identificatiedoeleinden. Raadpleeg hun privacybeleid voor meer informatie.",
|
||||
"no_relays": "Geen relais",
|
||||
"choose_relay": "Kies een relais dat u wilt gebruiken",
|
||||
"no_relays_message": "We hebben een Nostr NIP-05-record voor deze gebruiker gevonden, maar deze bevat geen relays. Instrueer de ontvanger om relays toe te voegen aan zijn Nostr-record.",
|
||||
"no_relay_on_domain": "Er is geen relay voor het domein van de gebruiker of de relay is niet beschikbaar. Kies een relais dat u wilt gebruiken."
|
||||
}
|
||||
|
|
|
@ -767,5 +767,11 @@
|
|||
"receivable_balance": "Saldo należności",
|
||||
"confirmed_tx": "Potwierdzony",
|
||||
"transaction_details_source_address": "Adres źródłowy",
|
||||
"track": "Ścieżka"
|
||||
"track": "Ścieżka",
|
||||
"pause_wallet_creation": "Możliwość utworzenia Portfela Haven jest obecnie wstrzymana.",
|
||||
"camera_consent": "Twój aparat zostanie użyty do przechwycenia obrazu w celach identyfikacyjnych przez ${provider}. Aby uzyskać szczegółowe informacje, sprawdź ich Politykę prywatności.",
|
||||
"no_relays": "Żadnych przekaźników",
|
||||
"choose_relay": "Wybierz przekaźnik, którego chcesz użyć",
|
||||
"no_relays_message": "Znaleźliśmy rekord Nostr NIP-05 dla tego użytkownika, ale nie zawiera on żadnych przekaźników. Poinstruuj odbiorcę, aby dodał przekaźniki do swojego rekordu Nostr.",
|
||||
"no_relay_on_domain": "Brak przekaźnika dla domeny użytkownika lub przekaźnik jest niedostępny. Wybierz przekaźnik, którego chcesz użyć."
|
||||
}
|
||||
|
|
|
@ -766,5 +766,11 @@
|
|||
"receivable_balance": "Saldo a receber",
|
||||
"confirmed_tx": "Confirmado",
|
||||
"transaction_details_source_address": "Endereço de Origem",
|
||||
"track": "Acompanhar"
|
||||
"track": "Acompanhar",
|
||||
"pause_wallet_creation": "A capacidade de criar a Haven Wallet está atualmente pausada.",
|
||||
"camera_consent": "Sua câmera será usada para capturar uma imagem para fins de identificação por ${provider}. Por favor, verifique a Política de Privacidade para obter detalhes.",
|
||||
"no_relays": "Sem relés",
|
||||
"choose_relay": "Escolha um relé para usar",
|
||||
"no_relays_message": "Encontramos um registro Nostr NIP-05 para este usuário, mas ele não contém nenhum relé. Instrua o destinatário a adicionar retransmissões ao seu registro Nostr.",
|
||||
"no_relay_on_domain": "Não há uma retransmissão para o domínio do usuário ou a retransmissão está indisponível. Escolha um relé para usar."
|
||||
}
|
||||
|
|
|
@ -767,5 +767,11 @@
|
|||
"receivable_balance": "Баланс дебиторской задолженности",
|
||||
"confirmed_tx": "Подтвержденный",
|
||||
"transaction_details_source_address": "Адрес источника",
|
||||
"track": "Отслеживать"
|
||||
"track": "Отслеживать",
|
||||
"pause_wallet_creation": "Возможность создания Haven Wallet в настоящее время приостановлена.",
|
||||
"camera_consent": "Ваша камера будет использоваться для захвата изображения в целях идентификации ${provider}. Пожалуйста, ознакомьтесь с их Политикой конфиденциальности для получения подробной информации.",
|
||||
"no_relays": "Нет реле",
|
||||
"choose_relay": "Пожалуйста, выберите реле для использования",
|
||||
"no_relays_message": "Мы нашли запись Nostr NIP-05 для этого пользователя, но она не содержит никаких реле. Попросите получателя добавить реле в свою запись Nostr.",
|
||||
"no_relay_on_domain": "Для домена пользователя реле не существует или реле недоступно. Пожалуйста, выберите реле для использования."
|
||||
}
|
||||
|
|
|
@ -765,5 +765,11 @@
|
|||
"receivable_balance": "ยอดลูกหนี้",
|
||||
"confirmed_tx": "ซึ่งยืนยันแล้ว",
|
||||
"transaction_details_source_address": "ที่อยู่แหล่งกำเนิด",
|
||||
"track": "ติดตาม"
|
||||
"track": "ติดตาม",
|
||||
"pause_wallet_creation": "ขณะนี้ความสามารถในการสร้าง Haven Wallet ถูกหยุดชั่วคราว",
|
||||
"camera_consent": "กล้องของคุณจะถูกนำมาใช้เพื่อจับภาพเพื่อวัตถุประสงค์ในการระบุตัวตนภายใน ${provider} โปรดตรวจสอบนโยบายความเป็นส่วนตัวเพื่อดูรายละเอียด",
|
||||
"no_relays": "ไม่มีรีเลย์",
|
||||
"choose_relay": "กรุณาเลือกรีเลย์ที่จะใช้",
|
||||
"no_relays_message": "เราพบบันทึก Nostr NIP-05 สำหรับผู้ใช้รายนี้ แต่ไม่มีรีเลย์ใดๆ โปรดแนะนำให้ผู้รับเพิ่มรีเลย์ลงในบันทึก Nostr ของตน",
|
||||
"no_relay_on_domain": "ไม่มีการส่งต่อสำหรับโดเมนของผู้ใช้ หรือการส่งต่อไม่พร้อมใช้งาน กรุณาเลือกรีเลย์ที่จะใช้"
|
||||
}
|
||||
|
|
|
@ -761,5 +761,11 @@
|
|||
"receivable_balance": "Natatanggap na balanse",
|
||||
"confirmed_tx": "Nakumpirma",
|
||||
"transaction_details_source_address": "SOURCE ADDRESS",
|
||||
"track": "Subaybayan"
|
||||
"track": "Subaybayan",
|
||||
"pause_wallet_creation": "Kasalukuyang naka-pause ang kakayahang gumawa ng Haven Wallet.",
|
||||
"camera_consent": "Gagamitin ang iyong camera upang kumuha ng larawan para sa mga layunin ng pagkakakilanlan sa pamamagitan ng ${provider}. Pakisuri ang kanilang Patakaran sa Privacy para sa mga detalye.",
|
||||
"no_relays": "Walang mga relay",
|
||||
"choose_relay": "Mangyaring pumili ng relay na gagamitin",
|
||||
"no_relays_message": "Nakakita kami ng Nostr NIP-05 record para sa user na ito, ngunit hindi ito naglalaman ng anumang mga relay. Mangyaring atasan ang tatanggap na magdagdag ng mga relay sa kanilang Nostr record.",
|
||||
"no_relay_on_domain": "Walang relay para sa domain ng user o hindi available ang relay. Mangyaring pumili ng relay na gagamitin."
|
||||
}
|
||||
|
|
|
@ -765,5 +765,11 @@
|
|||
"receivable_balance": "Alacak bakiyesi",
|
||||
"confirmed_tx": "Onaylanmış",
|
||||
"transaction_details_source_address": "Kaynak adresi",
|
||||
"track": "İzlemek"
|
||||
"track": "İzlemek",
|
||||
"pause_wallet_creation": "Haven Cüzdanı oluşturma yeteneği şu anda duraklatıldı.",
|
||||
"camera_consent": "Kameranız ${provider} tarihine kadar tanımlama amacıyla bir görüntü yakalamak için kullanılacaktır. Ayrıntılar için lütfen Gizlilik Politikalarını kontrol edin.",
|
||||
"no_relays": "Röle yok",
|
||||
"choose_relay": "Lütfen kullanmak için bir röle seçin",
|
||||
"no_relays_message": "Bu kullanıcı için bir Nostr NIP-05 kaydı bulduk ancak bu kayıt herhangi bir aktarma içermiyor. Lütfen alıcıya Nostr kayıtlarına aktarma eklemesi talimatını verin.",
|
||||
"no_relay_on_domain": "Kullanıcının alanı için bir geçiş yok veya geçiş kullanılamıyor. Lütfen kullanmak için bir röle seçin."
|
||||
}
|
||||
|
|
|
@ -767,5 +767,11 @@
|
|||
"receivable_balance": "Баланс дебіторської заборгованості",
|
||||
"confirmed_tx": "Підтверджений",
|
||||
"transaction_details_source_address": "Адреса джерела",
|
||||
"track": "Відслідковувати"
|
||||
"track": "Відслідковувати",
|
||||
"pause_wallet_creation": "Можливість створення гаманця Haven зараз призупинено.",
|
||||
"camera_consent": "Ваша камера використовуватиметься для зйомки зображення з метою ідентифікації ${provider}. Будь ласка, ознайомтеся з їхньою політикою конфіденційності, щоб дізнатися більше.",
|
||||
"no_relays": "Без реле",
|
||||
"choose_relay": "Будь ласка, виберіть реле для використання",
|
||||
"no_relays_message": "Ми знайшли запис Nostr NIP-05 для цього користувача, але він не містить жодних реле. Будь ласка, попросіть одержувача додати реле до свого запису Nostr.",
|
||||
"no_relay_on_domain": "Немає ретранслятора для домену користувача або ретранслятор недоступний. Будь ласка, виберіть реле для використання."
|
||||
}
|
||||
|
|
|
@ -759,5 +759,11 @@
|
|||
"receivable_balance": "قابل وصول توازن",
|
||||
"confirmed_tx": "تصدیق",
|
||||
"transaction_details_source_address": "ماخذ ایڈریس",
|
||||
"track": " ﮏﯾﺮﭨ"
|
||||
"track": " ﮏﯾﺮﭨ",
|
||||
"pause_wallet_creation": "Haven Wallet ۔ﮯﮨ ﻑﻮﻗﻮﻣ ﻝﺎﺤﻟﺍ ﯽﻓ ﺖﯿﻠﮨﺍ ﯽﮐ ﮯﻧﺎﻨﺑ",
|
||||
"camera_consent": "۔ﮟﯿﮭﮑﯾﺩ ﯽﺴﯿﻟﺎﭘ ﯽﺴﯾﻮﯿﺋﺍﺮﭘ ﯽﮐ ﻥﺍ ﻡﺮﮐ ﮦﺍﺮﺑ ﮯﯿﻟ ﮯﮐ ﺕﻼ${provider}ﯿﺼﻔﺗ ۔ﺎﮔ ﮯﺋﺎﺟ ﺎﯿﮐ ﻝﺎﻤﻌﺘﺳﺍ ﮯﯿﻟ",
|
||||
"no_relays": " ۔ﮟﯿﮩﻧ ﮯﻠﯾﺭ ﯽﺋﻮﮐ",
|
||||
"choose_relay": " ۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﮯﻠﯾﺭ ﮯﯿﻟ ﮯﮐ ﮯﻧﺮﮐ ﻝﺎﻤﻌﺘﺳﺍ ﻡﺮﮐ ﮦﺍﺮﺑ",
|
||||
"no_relays_message": "۔ﮟﯾﺮﮐ ﻞﻣﺎﺷ ﮯﻠﯾﺭ ﮟﯿﻣ ﮈﺭﺎﮑﯾﺭ ﺮﭩﺳﻮﻧ ﮯﻨﭘﺍ ﮦﻭ ﮧﮐ ﮟﯾﺩ ﺖﯾﺍﺪﮨ ﻮﮐ ﮦﺪﻨﻨﮐ ﻝﻮﺻﻭ ﻡﺮﮐ ﮦﺍﺮﺑ ۔",
|
||||
"no_relay_on_domain": "۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﮯﻠﯾﺭ ﮯﯿﻟ ﮯﮐ ﮯﻧﺮﮐ ﻝﺎﻤﻌﺘﺳﺍ ﻡﺮﮐ ﮦﺍﺮﺑ ۔ﮯﮨ ﮟﯿﮩﻧ ﺏﺎﯿﺘﺳﺩ ﮯﻠﯾﺭ ﺎﯾ ﮯﮨ ﮟ"
|
||||
}
|
||||
|
|
|
@ -761,5 +761,11 @@
|
|||
"receivable_balance": "Iwontunws.funfun ti o gba",
|
||||
"confirmed_tx": "Jẹrisi",
|
||||
"transaction_details_source_address": "Adirẹsi orisun",
|
||||
"track": "Orin"
|
||||
"track": "Orin",
|
||||
"pause_wallet_creation": "Agbara lati ṣẹda Haven Wallet ti wa ni idaduro lọwọlọwọ.",
|
||||
"camera_consent": "Kamẹra rẹ yoo ṣee lo lati ya aworan kan fun awọn idi idanimọ nipasẹ ${provider}. Jọwọ ṣayẹwo Ilana Aṣiri wọn fun awọn alaye.",
|
||||
"no_relays": "Ko si relays",
|
||||
"choose_relay": "Jọwọ yan yii lati lo",
|
||||
"no_relays_message": "A ri igbasilẹ Nostr NIP-05 fun olumulo yii, ṣugbọn ko ni eyikeyi awọn iṣipopada ninu. Jọwọ sọ fun olugba lati ṣafikun awọn isunmọ si igbasilẹ Nostr wọn.",
|
||||
"no_relay_on_domain": "Ko si iṣipopada fun agbegbe olumulo tabi yiyi ko si. Jọwọ yan yii lati lo."
|
||||
}
|
||||
|
|
|
@ -766,5 +766,11 @@
|
|||
"receivable_balance": "应收余额",
|
||||
"confirmed_tx": "确认的",
|
||||
"transaction_details_source_address": "源地址",
|
||||
"track": "追踪"
|
||||
"track": "追踪",
|
||||
"pause_wallet_creation": "创建 Haven 钱包的功能当前已暂停。",
|
||||
"camera_consent": "${provider} 将使用您的相机拍摄图像以供识别之用。请查看他们的隐私政策了解详情。",
|
||||
"no_relays": "无继电器",
|
||||
"choose_relay": "请选择要使用的继电器",
|
||||
"no_relays_message": "我们找到了该用户的 Nostr NIP-05 记录,但它不包含任何中继。请指示收件人将中继添加到他们的 Nostr 记录中。",
|
||||
"no_relay_on_domain": "用户域没有中继或中继不可用。请选择要使用的继电器。"
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ import 'package:cw_core/output_info.dart';
|
|||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:hive/hive.dart';""";
|
||||
const bitcoinCWHeaders = """
|
||||
import 'package:cw_bitcoin/electrum_wallet.dart';
|
||||
|
@ -76,9 +77,27 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
|||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||
import 'package:cw_bitcoin/litecoin_wallet_service.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
""";
|
||||
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
|
||||
const bitcoinContent = """
|
||||
|
||||
class ElectrumSubAddress {
|
||||
ElectrumSubAddress({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.address,
|
||||
required this.txCount,
|
||||
required this.balance,
|
||||
required this.isChange});
|
||||
final int id;
|
||||
final String name;
|
||||
final String address;
|
||||
final int txCount;
|
||||
final int balance;
|
||||
final bool isChange;
|
||||
}
|
||||
|
||||
abstract class Bitcoin {
|
||||
TransactionPriority getMediumTransactionPriority();
|
||||
|
||||
|
@ -92,13 +111,16 @@ abstract class Bitcoin {
|
|||
TransactionPriority deserializeBitcoinTransactionPriority(int raw);
|
||||
TransactionPriority deserializeLitecoinTransactionPriority(int raw);
|
||||
int getFeeRate(Object wallet, TransactionPriority priority);
|
||||
Future<void> generateNewAddress(Object wallet);
|
||||
Future<void> generateNewAddress(Object wallet, String label);
|
||||
Future<void> updateAddress(Object wallet,String address, String label);
|
||||
Object createBitcoinTransactionCredentials(List<Output> outputs, {required TransactionPriority priority, int? feeRate});
|
||||
Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs, {TransactionPriority? priority, required int feeRate});
|
||||
|
||||
List<String> getAddresses(Object wallet);
|
||||
String getAddress(Object wallet);
|
||||
|
||||
List<ElectrumSubAddress> getSubAddresses(Object wallet);
|
||||
|
||||
String formatterBitcoinAmountToString({required int amount});
|
||||
double formatterBitcoinAmountToDouble({required int amount});
|
||||
int formatterStringDoubleToBitcoinAmount(String amount);
|
||||
|
|
Loading…
Reference in a new issue