Merge remote-tracking branch 'origin/cw_linux_direct_input_password' into silent-payments-linux-2

This commit is contained in:
Rafael Saes 2023-11-18 16:50:00 -03:00
commit bc24ed5fab
115 changed files with 1846 additions and 934 deletions

View file

@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: '8.x'
java-version: '11.x'
- name: Flutter action
uses: subosito/flutter-action@v1

View file

@ -22,7 +22,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: '11.x'
java-version: "11.x"
- name: Flutter action
uses: subosito/flutter-action@v1
@ -38,6 +38,8 @@ jobs:
sudo mkdir -p /opt/android
sudo chown $USER /opt/android
cd /opt/android
-y curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install cargo-ndk
git clone https://github.com/cake-tech/cake_wallet.git --branch $GITHUB_HEAD_REF
cd cake_wallet/scripts/android/
./install_ndk.sh

View file

@ -3,6 +3,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

View file

@ -1,4 +1,7 @@
UI enhancements
Coin control fixes and enhancements
In-app Tor connection
Accessibility enhancements
Privacy settings enhancements
Tablet/iPad fixes
UI enhancements
Backup flow fixes
Bug fixes

View file

@ -1,5 +1,7 @@
WalletConnect enhancements
UI enhancements
Coin control fixes and enhancements
In-app Tor connection
Accessibility enhancements
Privacy settings enhancements
Tablet/iPad fixes
UI enhancements
Backup flow fixes
Bug fixes

View file

@ -3,7 +3,7 @@ import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:math';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:hive/hive.dart';
@ -12,30 +12,39 @@ import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
import 'package:flutter/foundation.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cw_bitcoin/electrum_transaction_info.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:collection/collection.dart';
import 'package:cw_bitcoin/address_to_output_script.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
import 'package:cw_bitcoin/electrum_transaction_history.dart';
import 'package:cw_bitcoin/bitcoin_transaction_no_inputs_exception.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_bitcoin/bitcoin_transaction_wrong_balance_exception.dart';
import 'package:cw_bitcoin/bitcoin_unspent.dart';
import 'package:cw_bitcoin/bitcoin_wallet_keys.dart';
import 'package:cw_bitcoin/electrum.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/electrum_transaction_history.dart';
import 'package:cw_bitcoin/electrum_transaction_info.dart';
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
import 'package:cw_bitcoin/script_hash.dart';
import 'package:cw_bitcoin/utils.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_bitcoin/electrum.dart';
import 'package:hex/hex.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:collection/collection.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:flutter/foundation.dart';
import 'package:hex/hex.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
part 'electrum_wallet.g.dart';
@ -678,6 +687,7 @@ abstract class ElectrumWalletBase
address: coin.bitcoinAddressRecord.address,
value: coin.value,
vout: coin.vout,
isChange: coin.isChange,
);
await unspentCoinsInfo.add(newInfo);

View file

@ -229,7 +229,7 @@ packages:
dependency: "direct main"
description:
name: cryptography
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
sha256: e0e37f79665cd5c86e8897f9abe1accfe813c0cc5299dab22256e22fddc1fef8
url: "https://pub.dev"
source: hosted
version: "2.5.0"
@ -640,6 +640,14 @@ packages:
description: flutter
source: sdk
version: "0.0.99"
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
source_gen:
dependency: transitive
description:
@ -720,7 +728,7 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
tuple:
tor:
dependency: transitive
description:
name: tuple

View file

@ -2,4 +2,5 @@ import 'package:bitbox/bitbox.dart' as bitbox;
class AddressUtils {
static String getCashAddrFormat(String address) => bitbox.Address.toCashAddress(address);
static String toLegacyAddress(String address) => bitbox.Address.toLegacyAddress(address);
}

View file

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:bitbox/bitbox.dart' as bitbox;
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cw_core/encryption_file_utils.dart';
@ -301,4 +303,16 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
return 0;
}
@override
String signMessage(String message, {String? address = null}) {
final index = address != null
? walletAddresses.addresses
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
.index
: null;
return index == null
? base64Encode(hd.sign(message))
: base64Encode(hd.derive(index).sign(message));
}
}

View file

@ -13,8 +13,7 @@ import 'package:collection/collection.dart';
import 'package:hive/hive.dart';
class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredentials,
BitcoinCashRestoreWalletFromSeedCredentials,
BitcoinCashRestoreWalletFromWIFCredentials> {
BitcoinCashRestoreWalletFromSeedCredentials, BitcoinCashRestoreWalletFromWIFCredentials> {
BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
@ -30,8 +29,13 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
@override
Future<BitcoinCashWallet> create(credentials, {bool? isTestnet}) async {
final strength = (credentials.seedPhraseLength == 12)
? 128
: (credentials.seedPhraseLength == 24)
? 256
: 128;
final wallet = await BitcoinCashWalletBase.create(
mnemonic: await Mnemonic.generate(),
mnemonic: await Mnemonic.generate(strength: strength),
password: credentials.password!,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
@ -43,10 +47,12 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
@override
Future<BitcoinCashWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(name, getType()))!;
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final wallet = await BitcoinCashWalletBase.open(
password: password, name: name, walletInfo: walletInfo,
password: password,
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect));
await wallet.init();
@ -55,17 +61,16 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType()))
.delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWalletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await BitcoinCashWalletBase.open(
password: password,
name: currentName,
@ -83,8 +88,7 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
}
@override
Future<BitcoinCashWallet>
restoreFromKeys(credentials) {
Future<BitcoinCashWallet> restoreFromKeys(credentials) {
// TODO: implement restoreFromKeys
throw UnimplementedError('restoreFromKeys() is not implemented');
}

View file

@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
tor
)
set(PLUGIN_BUNDLED_LIBRARIES)

View file

@ -9,5 +9,5 @@ abstract class Balance {
String get formattedAdditionalBalance;
String get formattedFrozenBalance => '';
String get formattedUnAvailableBalance => '';
}

View file

@ -4,17 +4,18 @@ import 'package:cw_core/monero_amount_format.dart';
class MoneroBalance extends Balance {
MoneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0})
: formattedFullBalance = moneroAmountToString(amount: fullBalance),
formattedUnlockedBalance = moneroAmountToString(amount: unlockedBalance),
frozenFormatted = moneroAmountToString(amount: frozenBalance),
formattedUnlockedBalance = moneroAmountToString(amount: unlockedBalance - frozenBalance),
formattedLockedBalance =
moneroAmountToString(amount: frozenBalance + fullBalance - unlockedBalance),
super(unlockedBalance, fullBalance);
MoneroBalance.fromString(
{required this.formattedFullBalance,
required this.formattedUnlockedBalance,
this.frozenFormatted = '0.0'})
this.formattedLockedBalance = '0.0'})
: fullBalance = moneroParseAmount(amount: formattedFullBalance),
unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance),
frozenBalance = moneroParseAmount(amount: frozenFormatted),
frozenBalance = moneroParseAmount(amount: formattedLockedBalance),
super(moneroParseAmount(amount: formattedUnlockedBalance),
moneroParseAmount(amount: formattedFullBalance));
@ -23,10 +24,11 @@ class MoneroBalance extends Balance {
final int frozenBalance;
final String formattedFullBalance;
final String formattedUnlockedBalance;
final String frozenFormatted;
final String formattedLockedBalance;
@override
String get formattedFrozenBalance => frozenFormatted == '0.0' ? '' : frozenFormatted;
String get formattedUnAvailableBalance =>
formattedLockedBalance == '0.0' ? '' : formattedLockedBalance;
@override
String get formattedAvailableBalance => formattedUnlockedBalance;

View file

@ -6,6 +6,7 @@ import 'package:hive/hive.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:http/io_client.dart' as ioc;
import 'package:tor/tor.dart';
part 'node.g.dart';
@ -129,9 +130,7 @@ class Node extends HiveObject with Keyable {
try {
switch (type) {
case WalletType.monero:
return useSocksProxy
? requestNodeWithProxy(socksProxyAddress ?? '')
: requestMoneroNode();
return requestMoneroNode();
case WalletType.bitcoin:
return requestElectrumServer();
case WalletType.litecoin:
@ -154,6 +153,9 @@ class Node extends HiveObject with Keyable {
}
Future<bool> requestMoneroNode() async {
if (uri.toString().contains(".onion") || useSocksProxy) {
return await requestNodeWithProxy();
}
final path = '/json_rpc';
final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path);
final realm = 'monero-rpc';
@ -205,11 +207,20 @@ class Node extends HiveObject with Keyable {
}
}
Future<bool> requestNodeWithProxy(String proxy) async {
if (proxy.isEmpty || !proxy.contains(':')) {
Future<bool> requestNodeWithProxy() async {
if ((socksProxyAddress == null ||
socksProxyAddress!.isEmpty ||
!socksProxyAddress!.contains(':')) &&
!Tor.instance.enabled) {
return false;
}
final proxyAddress = proxy.split(':')[0];
String? proxy = socksProxyAddress;
if ((proxy?.isEmpty ?? true) && Tor.instance.enabled) {
proxy = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}";
}
final proxyAddress = proxy!.split(':')[0];
final proxyPort = int.parse(proxy.split(':')[1]);
try {
final socket = await Socket.connect(proxyAddress, proxyPort, timeout: Duration(seconds: 5));

View file

@ -14,7 +14,9 @@ class UnspentCoinsInfo extends HiveObject {
required this.address,
required this.vout,
required this.value,
this.keyImage = null
this.keyImage = null,
this.isChange = false,
this.accountIndex = 0
});
static const typeId = UNSPENT_COINS_INFO_TYPE_ID;
@ -47,6 +49,12 @@ class UnspentCoinsInfo extends HiveObject {
@HiveField(8, defaultValue: null)
String? keyImage;
@HiveField(9, defaultValue: false)
bool isChange;
@HiveField(10, defaultValue: 0)
int accountIndex;
String get note => noteRaw ?? '';

View file

@ -2,6 +2,7 @@ class Unspent {
Unspent(this.address, this.hash, this.value, this.vout, this.keyImage)
: isSending = true,
isFrozen = false,
isChange = false,
note = '';
final String address;
@ -10,6 +11,7 @@ class Unspent {
final int vout;
final String? keyImage;
bool isChange;
bool isSending;
bool isFrozen;
String note;

View file

@ -4,6 +4,7 @@ abstract class WalletCredentials {
WalletCredentials({
required this.name,
this.height,
this.seedPhraseLength,
this.walletInfo,
this.password,
this.derivationType,
@ -12,6 +13,7 @@ abstract class WalletCredentials {
final String name;
final int? height;
int? seedPhraseLength;
String? password;
DerivationType? derivationType;
String? derivationPath;

View file

@ -113,15 +113,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.4.3"
cake_backup:
dependency: "direct main"
description:
path: "."
ref: main
resolved-ref: "3aba867dcab6737f6707782f5db15d71f303db38"
url: "https://github.com/cake-tech/cake_backup.git"
source: git
version: "1.0.0+1"
characters:
dependency: transitive
description:
@ -178,22 +169,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.2"
cryptography:
dependency: transitive
description:
name: cryptography
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
url: "https://pub.dev"
source: hosted
version: "2.5.0"
cupertino_icons:
dependency: transitive
description:
name: cupertino_icons
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
url: "https://pub.dev"
source: hosted
version: "1.0.6"
dart_style:
dependency: transitive
description:
@ -432,50 +407,50 @@ packages:
dependency: "direct main"
description:
name: path_provider
sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
url: "https://pub.dev"
source: hosted
version: "2.0.12"
version: "2.1.1"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
url: "https://pub.dev"
source: hosted
version: "2.0.22"
version: "2.2.1"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74"
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.3.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.1.7"
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
url: "https://pub.dev"
source: hosted
version: "2.0.5"
version: "2.1.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
version: "2.2.1"
platform:
dependency: transitive
description:
@ -553,6 +528,14 @@ packages:
description: flutter
source: sdk
version: "0.0.99"
socks5_proxy:
dependency: "direct main"
description:
name: socks5_proxy
sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
source_gen:
dependency: transitive
description:
@ -633,14 +616,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
tuple:
dependency: transitive
tor:
dependency: "direct main"
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev"
source: hosted
version: "2.0.2"
path: "."
ref: main
resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5"
url: "https://github.com/cake-tech/tor.git"
source: git
version: "0.0.1"
typed_data:
dependency: transitive
description:
@ -699,4 +683,4 @@ packages:
version: "3.1.1"
sdks:
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.0.0"
flutter: ">=3.7.0"

View file

@ -24,6 +24,11 @@ dependencies:
url: https://github.com/cake-tech/cake_backup.git
ref: main
version: 1.0.0
socks5_proxy: ^1.0.4
tor:
git:
url: https://github.com/cake-tech/tor.git
ref: main
dev_dependencies:
flutter_test:

View file

@ -38,8 +38,13 @@ class EthereumClient {
// });
}
Future<EtherAmount> getBalance(EthereumAddress address) async =>
await _client!.getBalance(address);
Future<EtherAmount> getBalance(EthereumAddress address) async {
try {
return await _client!.getBalance(address);
} catch (_) {
return EtherAmount.zero();
}
}
Future<int> getGasUnitPrice() async {
try {

View file

@ -22,7 +22,13 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
@override
Future<EthereumWallet> create(EthereumNewWalletCredentials credentials, {bool? isTestnet}) async {
final mnemonic = bip39.generateMnemonic();
final strength = (credentials.seedPhraseLength == 12)
? 128
: (credentials.seedPhraseLength == 24)
? 256
: 128;
final mnemonic = bip39.generateMnemonic(strength: strength);
final wallet = EthereumWallet(
walletInfo: credentials.walletInfo!,
mnemonic: mnemonic,

View file

@ -113,15 +113,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.4.3"
cake_backup:
dependency: transitive
description:
path: "."
ref: main
resolved-ref: "3aba867dcab6737f6707782f5db15d71f303db38"
url: "https://github.com/cake-tech/cake_backup.git"
source: git
version: "1.0.0+1"
characters:
dependency: transitive
description:
@ -178,22 +169,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.2"
cryptography:
dependency: transitive
description:
name: cryptography
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
url: "https://pub.dev"
source: hosted
version: "2.5.0"
cupertino_icons:
dependency: transitive
description:
name: cupertino_icons
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
url: "https://pub.dev"
source: hosted
version: "1.0.6"
cw_core:
dependency: "direct main"
description:
@ -439,50 +414,50 @@ packages:
dependency: "direct main"
description:
name: path_provider
sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
url: "https://pub.dev"
source: hosted
version: "2.0.12"
version: "2.1.1"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
url: "https://pub.dev"
source: hosted
version: "2.0.22"
version: "2.2.1"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74"
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.3.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.1.7"
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
url: "https://pub.dev"
source: hosted
version: "2.0.5"
version: "2.1.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.2.1"
platform:
dependency: transitive
description:
@ -560,6 +535,14 @@ packages:
description: flutter
source: sdk
version: "0.0.99"
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
source_gen:
dependency: transitive
description:
@ -640,14 +623,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
tuple:
tor:
dependency: transitive
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev"
source: hosted
version: "2.0.2"
path: "."
ref: main
resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5"
url: "https://github.com/cake-tech/tor.git"
source: git
version: "0.0.1"
typed_data:
dependency: transitive
description:
@ -706,4 +690,4 @@ packages:
version: "3.1.1"
sdks:
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.0.0"
flutter: ">=3.7.0"

View file

@ -33,15 +33,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
cake_backup:
dependency: transitive
description:
path: "."
ref: main
resolved-ref: "3aba867dcab6737f6707782f5db15d71f303db38"
url: "https://github.com/cake-tech/cake_backup.git"
source: git
version: "1.0.0+1"
characters:
dependency: transitive
description:
@ -82,14 +73,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.2"
cryptography:
dependency: transitive
description:
name: cryptography
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
url: "https://pub.dev"
source: hosted
version: "2.5.0"
cupertino_icons:
dependency: "direct main"
description:
@ -254,50 +237,50 @@ packages:
dependency: transitive
description:
name: path_provider
sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
url: "https://pub.dev"
source: hosted
version: "2.0.12"
version: "2.1.1"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
url: "https://pub.dev"
source: hosted
version: "2.0.22"
version: "2.2.1"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74"
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.3.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.1.7"
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
url: "https://pub.dev"
source: hosted
version: "2.0.5"
version: "2.1.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.2.1"
platform:
dependency: transitive
description:
@ -335,6 +318,14 @@ packages:
description: flutter
source: sdk
version: "0.0.99"
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
source_span:
dependency: transitive
description:
@ -383,14 +374,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.5.1"
tuple:
tor:
dependency: transitive
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev"
source: hosted
version: "2.0.2"
path: "."
ref: main
resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5"
url: "https://github.com/cake-tech/tor.git"
source: git
version: "0.0.1"
typed_data:
dependency: transitive
description:
@ -426,3 +418,4 @@ packages:
sdks:
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.0.6"
flutter: ">=3.7.0"

View file

@ -893,6 +893,13 @@ extern "C"
return m_transaction_history->count();
}
FUNCTION_VISABILITY_ATTRIBUTE
TransactionInfoRow* get_transaction(char * txId)
{
Monero::TransactionInfo *row = m_transaction_history->transaction(std::string(txId));
return new TransactionInfoRow(row);
}
FUNCTION_VISABILITY_ATTRIBUTE
int LedgerExchange(
unsigned char *command,
@ -1033,6 +1040,15 @@ extern "C"
return result;
}
void freeze_coin(int index)
{
m_coins->setFrozen(index);
}
void thaw_coin(int index)
{
m_coins->thaw(index);
}
#ifdef __cplusplus
}

View file

@ -16,8 +16,20 @@ final coinNative = moneroApi
.lookup<NativeFunction<coin>>('coin')
.asFunction<GetCoin>();
final freezeCoinNative = moneroApi
.lookup<NativeFunction<freeze_coin>>('freeze_coin')
.asFunction<FreezeCoin>();
final thawCoinNative = moneroApi
.lookup<NativeFunction<thaw_coin>>('thaw_coin')
.asFunction<ThawCoin>();
void refreshCoins(int accountIndex) => refreshCoinsNative(accountIndex);
int countOfCoins() => coinsCountNative();
CoinsInfoRow getCoin(int index) => coinNative(index).ref;
void freezeCoin(int index) => freezeCoinNative(index);
void thawCoin(int index) => thawCoinNative(index);

View file

@ -1,6 +1,7 @@
import 'dart:ffi';
import 'package:cw_monero/api/structs/coins_info_row.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/structs/transaction_info_row.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:ffi/ffi.dart';
@ -81,6 +82,8 @@ typedef account_set_label = Void Function(Int32 accountIndex, Pointer<Utf8> labe
typedef transactions_refresh = Void Function();
typedef get_transaction = Pointer<TransactionInfoRow> Function(Pointer<Utf8> txId);
typedef get_tx_key = Pointer<Utf8>? Function(Pointer<Utf8> txId);
typedef transactions_count = Int64 Function();
@ -139,3 +142,7 @@ typedef coins_count = Int64 Function();
// typedef coins_from_txid = Pointer<CoinsInfoRow> Function(Pointer<Utf8> txid);
typedef coin = Pointer<CoinsInfoRow> Function(Int32 index);
typedef freeze_coin = Void Function(Int32 index);
typedef thaw_coin = Void Function(Int32 index);

View file

@ -1,15 +1,16 @@
import 'dart:ffi';
import 'package:cw_monero/api/convert_utf8_to_string.dart';
import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/monero_output.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/structs/transaction_info_row.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:cw_monero/api/types.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/structs/transaction_info_row.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart';
final transactionsRefreshNative = moneroApi
.lookup<NativeFunction<transactions_refresh>>('transactions_refresh')
@ -38,6 +39,10 @@ final transactionCommitNative = moneroApi
final getTxKeyNative =
moneroApi.lookup<NativeFunction<get_tx_key>>('get_tx_key').asFunction<GetTxKey>();
final getTransactionNative = moneroApi
.lookup<NativeFunction<get_transaction>>('get_transaction')
.asFunction<GetTransaction>();
String getTxKey(String txId) {
final txIdPointer = txId.toNativeUtf8();
final keyPointer = getTxKeyNative(txIdPointer);
@ -65,6 +70,11 @@ List<TransactionInfoRow> getAllTransactions() {
.toList();
}
TransactionInfoRow getTransaction(String txId) {
final txIdPointer = txId.toNativeUtf8();
return getTransactionNative(txIdPointer).ref;
}
PendingTransactionDescription createTransactionSync(
{required String address,
required String paymentId,

View file

@ -1,6 +1,7 @@
import 'dart:ffi';
import 'package:cw_monero/api/structs/coins_info_row.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/structs/transaction_info_row.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:ffi/ffi.dart';
@ -81,6 +82,8 @@ typedef AccountSetLabel = void Function(int accountIndex, Pointer<Utf8> label);
typedef TransactionsRefresh = void Function();
typedef GetTransaction = Pointer<TransactionInfoRow> Function(Pointer<Utf8> txId);
typedef GetTxKey = Pointer<Utf8>? Function(Pointer<Utf8> txId);
typedef TransactionsCount = int Function();
@ -139,3 +142,7 @@ typedef RefreshCoins = void Function(int);
typedef CoinsCount = int Function();
typedef GetCoin = Pointer<CoinsInfoRow> Function(int);
typedef FreezeCoin = void Function(int);
typedef ThawCoin = void Function(int);

View file

@ -1,28 +1,20 @@
import 'package:cw_core/unspent_transaction_output.dart';
import 'package:cw_monero/api/structs/coins_info_row.dart';
class MoneroUnspent {
MoneroUnspent(this.address, this.hash, this.keyImage, this.value, this.isFrozen, this.isUnlocked)
: isSending = true,
note = '';
class MoneroUnspent extends Unspent {
MoneroUnspent(
String address, String hash, String keyImage, int value, bool isFrozen, this.isUnlocked)
: super(address, hash, value, 0, keyImage) {
this.isFrozen = isFrozen;
}
MoneroUnspent.fromCoinsInfoRow(CoinsInfoRow coinsInfoRow)
: address = coinsInfoRow.getAddress(),
hash = coinsInfoRow.getHash(),
keyImage = coinsInfoRow.getKeyImage(),
value = coinsInfoRow.amount,
isFrozen = coinsInfoRow.frozen == 1,
isUnlocked = coinsInfoRow.unlocked == 1,
isSending = true,
note = '';
final String address;
final String hash;
final String keyImage;
final int value;
factory MoneroUnspent.fromCoinsInfoRow(CoinsInfoRow coinsInfoRow) => MoneroUnspent(
coinsInfoRow.getAddress(),
coinsInfoRow.getHash(),
coinsInfoRow.getKeyImage(),
coinsInfoRow.amount,
coinsInfoRow.frozen == 1,
coinsInfoRow.unlocked == 1);
final bool isUnlocked;
bool isFrozen;
bool isSending;
String note;
}

View file

@ -1,8 +1,8 @@
import 'dart:async';
import 'dart:io';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_priority.dart';
import 'dart:io';
import 'package:cw_core/account.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/monero_amount_format.dart';
@ -25,14 +25,14 @@ import 'package:cw_monero/api/transaction_history.dart' as transaction_history;
import 'package:cw_monero/api/wallet.dart' as monero_wallet;
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
import 'package:cw_monero/exceptions/monero_transaction_no_inputs_exception.dart';
import 'package:cw_monero/pending_monero_transaction.dart';
import 'package:cw_monero/monero_transaction_creation_credentials.dart';
import 'package:cw_monero/monero_transaction_history.dart';
import 'package:cw_monero/monero_transaction_info.dart';
import 'package:cw_monero/monero_unspent.dart';
import 'package:cw_monero/monero_wallet_addresses.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_monero/pending_monero_transaction.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
part 'monero_wallet.g.dart';
@ -214,7 +214,7 @@ abstract class MoneroWalletBase
for (final utx in unspentCoins) {
if (utx.isSending) {
allInputsAmount += utx.value;
inputs.add(utx.keyImage);
inputs.add(utx.keyImage!);
}
}
final spendAllCoins = inputs.length == unspentCoins.length;
@ -408,7 +408,9 @@ abstract class MoneroWalletBase
for (var i = 0; i < coinCount; i++) {
final coin = getCoin(i);
if (coin.spent == 0) {
unspentCoins.add(MoneroUnspent.fromCoinsInfoRow(coin));
final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1;
unspentCoins.add(unspent);
}
}
@ -419,8 +421,10 @@ abstract class MoneroWalletBase
if (unspentCoins.isNotEmpty) {
unspentCoins.forEach((coin) {
final coinInfoList = unspentCoinsInfo.values
.where((element) => element.walletId.contains(id) && element.hash.contains(coin.hash));
final coinInfoList = unspentCoinsInfo.values.where((element) =>
element.walletId.contains(id) &&
element.accountIndex == walletAddresses.account!.id &&
element.keyImage!.contains(coin.keyImage!));
if (coinInfoList.isNotEmpty) {
final coinInfo = coinInfoList.first;
@ -448,7 +452,9 @@ abstract class MoneroWalletBase
address: coin.address,
value: coin.value,
vout: 0,
keyImage: coin.keyImage);
keyImage: coin.keyImage,
isChange: coin.isChange,
accountIndex: walletAddresses.account!.id);
await unspentCoinsInfo.add(newInfo);
}
@ -456,12 +462,13 @@ abstract class MoneroWalletBase
Future<void> _refreshUnspentCoinsInfo() async {
try {
final List<dynamic> keys = <dynamic>[];
final currentWalletUnspentCoins =
unspentCoinsInfo.values.where((element) => element.walletId.contains(id));
final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) =>
element.walletId.contains(id) && element.accountIndex == walletAddresses.account!.id);
if (currentWalletUnspentCoins.isNotEmpty) {
currentWalletUnspentCoins.forEach((element) {
final existUnspentCoins = unspentCoins.where((coin) => element.hash.contains(coin.hash));
final existUnspentCoins =
unspentCoins.where((coin) => element.keyImage!.contains(coin.keyImage!));
if (existUnspentCoins.isEmpty) {
keys.add(element.key);
@ -579,7 +586,8 @@ abstract class MoneroWalletBase
int _getFrozenBalance() {
var frozenBalance = 0;
for (var coin in unspentCoinsInfo.values) {
for (var coin in unspentCoinsInfo.values.where((element) =>
element.walletId == id && element.accountIndex == walletAddresses.account!.id)) {
if (coin.isFrozen) frozenBalance += coin.value;
}

View file

@ -122,6 +122,7 @@ packages:
url: "https://github.com/cake-tech/cake_backup.git"
source: git
version: "1.0.0+1"
version: "8.4.3"
characters:
dependency: transitive
description:
@ -194,6 +195,7 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.6"
version: "3.0.2"
cw_core:
dependency: "direct main"
description:
@ -568,6 +570,14 @@ packages:
description: flutter
source: sdk
version: "0.0.99"
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
source_gen:
dependency: transitive
description:
@ -648,14 +658,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
tuple:
tor:
dependency: transitive
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev"
source: hosted
version: "2.0.2"
path: "."
ref: main
resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5"
url: "https://github.com/cake-tech/tor.git"
source: git
version: "0.0.1"
typed_data:
dependency: transitive
description:

View file

@ -8,18 +8,28 @@ import 'package:cw_nano/nano_util.dart';
import 'package:http/http.dart' as http;
import 'package:nanodart/nanodart.dart';
import 'package:cw_core/node.dart';
import 'package:shared_preferences/shared_preferences.dart';
class NanoClient {
static const String DEFAULT_REPRESENTATIVE =
"nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579";
static const Map<String, String> CAKE_HEADERS = {
"Content-Type": "application/json",
"nano-app": "cake-wallet"
};
NanoClient() {
SharedPreferences.getInstance().then((value) => prefs = value);
}
late SharedPreferences prefs;
Node? _node;
Node? _powNode;
static const String _defaultDefaultRepresentative =
"nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579";
String getRepFromPrefs() {
// from preferences_key.dart "defaultNanoRep" key:
return prefs.getString("default_nano_representative") ?? _defaultDefaultRepresentative;
}
bool connect(Node node) {
try {
@ -84,44 +94,45 @@ class NanoClient {
required String repAddress,
required String ourAddress,
}) async {
AccountInfoResponse? accountInfo = await getAccountInfo(ourAddress);
if (accountInfo == null) {
throw Exception(
"error while getting account info, you can't change the rep of an unopened account");
}
// construct the change block:
Map<String, String> changeBlock = {
"type": "state",
"account": ourAddress,
"previous": accountInfo.frontier,
"representative": repAddress,
"balance": accountInfo.balance,
"link": "0000000000000000000000000000000000000000000000000000000000000000",
"link_as_account": "nano_1111111111111111111111111111111111111111111111111111hifc8npp",
};
// sign the change block:
final String hash = NanoBlocks.computeStateHash(
NanoAccountType.NANO,
changeBlock["account"]!,
changeBlock["previous"]!,
changeBlock["representative"]!,
BigInt.parse(changeBlock["balance"]!),
changeBlock["link"]!,
);
final String signature = NanoSignatures.signBlock(hash, privateKey);
// get PoW for the send block:
final String work = await requestWork(accountInfo.frontier);
changeBlock["signature"] = signature;
changeBlock["work"] = work;
try {
AccountInfoResponse? accountInfo = await getAccountInfo(ourAddress);
if (accountInfo == null) {
throw Exception("error while getting account info");
}
// construct the change block:
Map<String, String> changeBlock = {
"type": "state",
"account": ourAddress,
"previous": accountInfo.frontier,
"representative": repAddress,
"balance": accountInfo.balance,
"link": "0000000000000000000000000000000000000000000000000000000000000000",
"link_as_account": "nano_1111111111111111111111111111111111111111111111111111hifc8npp",
};
// sign the change block:
final String hash = NanoBlocks.computeStateHash(
NanoAccountType.NANO,
changeBlock["account"]!,
changeBlock["previous"]!,
changeBlock["representative"]!,
BigInt.parse(changeBlock["balance"]!),
changeBlock["link"]!,
);
final String signature = NanoSignatures.signBlock(hash, privateKey);
// get PoW for the send block:
final String work = await requestWork(accountInfo.frontier);
changeBlock["signature"] = signature;
changeBlock["work"] = work;
return await processBlock(changeBlock, "change");
} catch (e) {
throw Exception("error while changing representative");
throw Exception("error while changing representative: $e");
}
}
@ -191,68 +202,63 @@ class NanoClient {
BigInt? balanceAfterTx,
String? previousHash,
}) async {
try {
// our address:
final String publicAddress = NanoUtil.privateKeyToAddress(privateKey);
// our address:
final String publicAddress = NanoUtil.privateKeyToAddress(privateKey);
// first get the current account balance:
if (balanceAfterTx == null) {
final BigInt currentBalance = (await getBalance(publicAddress)).currentBalance;
final BigInt txAmount = BigInt.parse(amountRaw);
balanceAfterTx = currentBalance - txAmount;
}
// get the account info (we need the frontier and representative):
AccountInfoResponse? infoResponse = await getAccountInfo(publicAddress);
if (infoResponse == null) {
throw Exception(
"error while getting account info! (we probably don't have an open account yet)");
}
String frontier = infoResponse.frontier;
// override if provided:
if (previousHash != null) {
frontier = previousHash;
}
final String representative = infoResponse.representative;
// link = destination address:
final String link = NanoAccounts.extractPublicKey(destinationAddress);
final String linkAsAccount = destinationAddress;
// construct the send block:
Map<String, String> sendBlock = {
"type": "state",
"account": publicAddress,
"previous": frontier,
"representative": representative,
"balance": balanceAfterTx.toString(),
"link": link,
};
// sign the send block:
final String hash = NanoBlocks.computeStateHash(
NanoAccountType.NANO,
sendBlock["account"]!,
sendBlock["previous"]!,
sendBlock["representative"]!,
BigInt.parse(sendBlock["balance"]!),
sendBlock["link"]!,
);
final String signature = NanoSignatures.signBlock(hash, privateKey);
// get PoW for the send block:
final String work = await requestWork(frontier);
sendBlock["link_as_account"] = linkAsAccount;
sendBlock["signature"] = signature;
sendBlock["work"] = work;
// ready to post send block:
return sendBlock;
} catch (e) {
print(e);
rethrow;
// first get the current account balance:
if (balanceAfterTx == null) {
final BigInt currentBalance = (await getBalance(publicAddress)).currentBalance;
final BigInt txAmount = BigInt.parse(amountRaw);
balanceAfterTx = currentBalance - txAmount;
}
// get the account info (we need the frontier and representative):
AccountInfoResponse? infoResponse = await getAccountInfo(publicAddress);
if (infoResponse == null) {
throw Exception(
"error while getting account info! (we probably don't have an open account yet)");
}
String frontier = infoResponse.frontier;
// override if provided:
if (previousHash != null) {
frontier = previousHash;
}
final String representative = infoResponse.representative;
// link = destination address:
final String link = NanoAccounts.extractPublicKey(destinationAddress);
final String linkAsAccount = destinationAddress;
// construct the send block:
Map<String, String> sendBlock = {
"type": "state",
"account": publicAddress,
"previous": frontier,
"representative": representative,
"balance": balanceAfterTx.toString(),
"link": link,
};
// sign the send block:
final String hash = NanoBlocks.computeStateHash(
NanoAccountType.NANO,
sendBlock["account"]!,
sendBlock["previous"]!,
sendBlock["representative"]!,
BigInt.parse(sendBlock["balance"]!),
sendBlock["link"]!,
);
final String signature = NanoSignatures.signBlock(hash, privateKey);
// get PoW for the send block:
final String work = await requestWork(frontier);
sendBlock["link_as_account"] = linkAsAccount;
sendBlock["signature"] = signature;
sendBlock["work"] = work;
// ready to post send block:
return sendBlock;
}
Future<void> receiveBlock({
@ -274,7 +280,7 @@ class NanoClient {
// account is not open yet, we need to create an open block:
openBlock = true;
// we don't have a representative set yet:
representative = DEFAULT_REPRESENTATIVE;
representative = await getRepFromPrefs();
// we don't have a frontier yet:
frontier = "0000000000000000000000000000000000000000000000000000000000000000";
} else {

View file

@ -395,7 +395,7 @@ abstract class NanoWalletBase
_representativeAddress = accountInfo.representative;
} catch (e) {
// account not found:
_representativeAddress = NanoClient.DEFAULT_REPRESENTATIVE;
_representativeAddress = await _client.getRepFromPrefs();
throw Exception("Failed to get representative address $e");
}
}

View file

@ -137,15 +137,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.6.1"
cake_backup:
dependency: transitive
description:
path: "."
ref: main
resolved-ref: "3aba867dcab6737f6707782f5db15d71f303db38"
url: "https://github.com/cake-tech/cake_backup.git"
source: git
version: "1.0.0+1"
characters:
dependency: transitive
description:
@ -202,22 +193,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
cryptography:
dependency: transitive
description:
name: cryptography
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
url: "https://pub.dev"
source: hosted
version: "2.5.0"
cupertino_icons:
dependency: transitive
description:
name: cupertino_icons
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
url: "https://pub.dev"
source: hosted
version: "1.0.6"
cw_core:
dependency: "direct main"
description:
@ -315,6 +290,11 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
@ -511,50 +491,50 @@ packages:
dependency: transitive
description:
name: path_provider
sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
url: "https://pub.dev"
source: hosted
version: "2.0.15"
version: "2.1.1"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86"
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
url: "https://pub.dev"
source: hosted
version: "2.0.27"
version: "2.2.1"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297"
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
version: "2.3.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.1.11"
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
url: "https://pub.dev"
source: hosted
version: "2.0.6"
version: "2.1.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96"
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
version: "2.1.7"
version: "2.2.1"
pinenacl:
dependency: transitive
description:
@ -619,6 +599,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.2"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
url: "https://pub.dev"
source: hosted
version: "2.3.4"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
url: "https://pub.dev"
source: hosted
version: "2.3.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
url: "https://pub.dev"
source: hosted
version: "2.2.1"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
shelf:
dependency: transitive
description:
@ -640,6 +676,14 @@ packages:
description: flutter
source: sdk
version: "0.0.99"
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
source_gen:
dependency: transitive
description:
@ -720,14 +764,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
tuple:
tor:
dependency: transitive
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev"
source: hosted
version: "2.0.2"
path: "."
ref: main
resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5"
url: "https://github.com/cake-tech/tor.git"
source: git
version: "0.0.1"
typed_data:
dependency: transitive
description:
@ -786,4 +831,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.3.0"
flutter: ">=3.7.0"

View file

@ -21,6 +21,7 @@ dependencies:
ed25519_hd_key: ^2.2.0
hex: ^0.2.0
http: ^1.1.0
shared_preferences: ^2.0.15
cw_core:
path: ../cw_core

View file

@ -63,8 +63,6 @@ PODS:
- Flutter
- device_display_brightness (0.0.1):
- Flutter
- device_info (0.0.1):
- Flutter
- device_info_plus (0.0.1):
- Flutter
- devicelocale (0.0.1):
@ -126,13 +124,13 @@ PODS:
- OrderedSet (5.0.0)
- package_info (0.0.1):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.1.1):
- Flutter
- platform_device_id (0.0.1):
- Flutter
- ReachabilitySwift (5.0.0)
- SDWebImage (5.16.0):
- SDWebImage/Core (= 5.16.0)
@ -147,6 +145,8 @@ PODS:
- SwiftProtobuf (1.22.0)
- SwiftyGif (5.4.4)
- Toast (4.0.0)
- tor (0.0.1):
- Flutter
- uni_links (0.0.1):
- Flutter
- UnstoppableDomainsResolution (4.0.0):
@ -154,7 +154,7 @@ PODS:
- CryptoSwift
- url_launcher_ios (0.0.1):
- Flutter
- wakelock (0.0.1):
- wakelock_plus (0.0.1):
- Flutter
- workmanager (0.0.1):
- Flutter
@ -167,7 +167,6 @@ DEPENDENCIES:
- cw_monero (from `.symlinks/plugins/cw_monero/ios`)
- cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`)
- device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`)
- device_info (from `.symlinks/plugins/device_info/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
@ -179,16 +178,17 @@ DEPENDENCIES:
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
- package_info (from `.symlinks/plugins/package_info/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- platform_device_id (from `.symlinks/plugins/platform_device_id/ios`)
- sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- tor (from `.symlinks/plugins/tor/ios`)
- uni_links (from `.symlinks/plugins/uni_links/ios`)
- UnstoppableDomainsResolution (~> 4.0.0)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
- workmanager (from `.symlinks/plugins/workmanager/ios`)
SPEC REPOS:
@ -219,8 +219,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/cw_shared_external/ios"
device_display_brightness:
:path: ".symlinks/plugins/device_display_brightness/ios"
device_info:
:path: ".symlinks/plugins/device_info/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
devicelocale:
@ -243,24 +241,26 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/local_auth_ios/ios"
package_info:
:path: ".symlinks/plugins/package_info/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
platform_device_id:
:path: ".symlinks/plugins/platform_device_id/ios"
sensitive_clipboard:
:path: ".symlinks/plugins/sensitive_clipboard/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
tor:
:path: ".symlinks/plugins/tor/ios"
uni_links:
:path: ".symlinks/plugins/uni_links/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
wakelock:
:path: ".symlinks/plugins/wakelock/ios"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
workmanager:
:path: ".symlinks/plugins/workmanager/ios"
@ -273,12 +273,11 @@ SPEC CHECKSUMS:
cw_monero: 4cf3b96f2da8e95e2ef7d6703dd4d2c509127b7d
cw_shared_external: 2972d872b8917603478117c9957dfca611845a92
device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7
device_info: d7d233b645a32c40dfdc212de5cf646ca482f175
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
devicelocale: b22617f40038496deffba44747101255cee005b0
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_inappwebview: 3d32228f1304635e7c028b0d4252937730bbc6cf
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
@ -289,9 +288,9 @@ SPEC CHECKSUMS:
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
SDWebImage: 2aea163b50bfcb569a2726b6a754c54a4506fcf6
sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
@ -300,10 +299,11 @@ SPEC CHECKSUMS:
SwiftProtobuf: 40bd808372cb8706108f22d28f8ab4a6b9bc6989
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
tor: 662a9f5b980b5c86decb8ba611de9bcd4c8286eb
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
UnstoppableDomainsResolution: c3c67f4d0a5e2437cb00d4bd50c2e00d6e743841
url_launcher_ios: 68d46cc9766d0c41dbdc884310529557e3cd7a86
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
PODFILE CHECKSUM: 09df1114e7c360f55770d35a79356bf5446e0100

View file

@ -3,7 +3,6 @@ import 'dart:convert';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
@ -12,8 +11,7 @@ import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class RobinhoodBuyProvider {
RobinhoodBuyProvider({required WalletBase wallet})
: this._wallet = wallet;
RobinhoodBuyProvider({required WalletBase wallet}) : this._wallet = wallet;
final WalletBase _wallet;
@ -21,10 +19,15 @@ class RobinhoodBuyProvider {
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
String get _applicationId => secrets.robinhoodApplicationId;
String get _apiSecret => secrets.robinhoodCIdApiSecret;
bool get isAvailable =>
[WalletType.bitcoin, WalletType.litecoin, WalletType.ethereum].contains(_wallet.type);
bool get isAvailable => [
WalletType.bitcoin,
WalletType.bitcoinCash,
WalletType.litecoin,
WalletType.ethereum
].contains(_wallet.type);
String getSignature(String message) {
switch (_wallet.type) {
@ -32,6 +35,7 @@ class RobinhoodBuyProvider {
return _wallet.signMessage(message);
case WalletType.litecoin:
case WalletType.bitcoin:
case WalletType.bitcoinCash:
return _wallet.signMessage(message, address: _wallet.walletAddresses.address);
default:
throw Exception("WalletType is not available for Robinhood ${_wallet.type}");
@ -55,7 +59,8 @@ class RobinhoodBuyProvider {
if (response.statusCode == 200) {
return (jsonDecode(response.body) as Map<String, dynamic>)['connectId'] as String;
} else {
throw Exception('Provider currently unavailable. Status: ${response.statusCode} ${response.body}');
throw Exception(
'Provider currently unavailable. Status: ${response.statusCode} ${response.body}');
}
}
@ -76,8 +81,7 @@ class RobinhoodBuyProvider {
try {
final uri = await requestUrl();
await launchUrl(uri, mode: LaunchMode.externalApplication);
} catch (e, s) {
ExceptionHandler.onError(FlutterErrorDetails(exception: e, stack: s));
} catch (_) {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:cake_wallet/core/totp_request_details.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
@ -38,6 +40,11 @@ class AuthService with Store {
Future<void> setPassword(String password) async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
final encodedPassword = encodedPinCode(pin: password);
// secure storage has a weird bug on macOS, where overwriting a key doesn't work, unless
// we delete what's there first:
if (Platform.isMacOS) {
await secureStorage.delete(key: key);
}
await secureStorage.write(key: key, value: encodedPassword);
}
@ -104,8 +111,7 @@ class AuthService with Store {
}
return;
}
}
}
Navigator.of(context).pushNamed(Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
@ -140,8 +146,6 @@ class AuthService with Store {
}
}
}
});
});
}
}

View file

@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cw_core/root_dir.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cw_core/wallet_type.dart';
@ -247,6 +248,8 @@ class BackupService {
final sortBalanceTokensBy = data[PreferencesKey.sortBalanceBy] as int?;
final pinNativeTokenAtTop = data[PreferencesKey.pinNativeTokenAtTop] as bool?;
final useEtherscan = data[PreferencesKey.useEtherscan] as bool?;
final defaultNanoRep = data[PreferencesKey.defaultNanoRep] as String?;
final defaultBananoRep = data[PreferencesKey.defaultBananoRep] as String?;
final lookupsTwitter = data[PreferencesKey.lookupsTwitter] as bool?;
final lookupsMastodon = data[PreferencesKey.lookupsMastodon] as bool?;
final lookupsYatService = data[PreferencesKey.lookupsYatService] as bool?;
@ -255,7 +258,8 @@ class BackupService {
final lookupsENS = data[PreferencesKey.lookupsENS] as bool?;
final syncAll = data[PreferencesKey.syncAllKey] as bool?;
final syncMode = data[PreferencesKey.syncModeKey] as int?;
final autoGenerateSubaddressStatus = data[PreferencesKey.autoGenerateSubaddressStatusKey] as int?;
final autoGenerateSubaddressStatus =
data[PreferencesKey.autoGenerateSubaddressStatusKey] as int?;
await _sharedPreferences.setString(PreferencesKey.currentWalletName, currentWalletName);
@ -292,9 +296,12 @@ class BackupService {
await _sharedPreferences.setInt(
PreferencesKey.currentTransactionPriorityKeyLegacy, currentTransactionPriorityKeyLegacy);
if (allowBiometricalAuthentication != null && !Platform.isMacOS && !Platform.isLinux)
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(
@ -310,14 +317,18 @@ class BackupService {
if (fiatApiMode != null)
await _sharedPreferences.setInt(PreferencesKey.currentFiatApiModeKey, fiatApiMode);
if (autoGenerateSubaddressStatus != null)
await _sharedPreferences.setInt(PreferencesKey.autoGenerateSubaddressStatusKey,
autoGenerateSubaddressStatus);
await _sharedPreferences.setInt(
PreferencesKey.autoGenerateSubaddressStatusKey, autoGenerateSubaddressStatus);
if (currentPinLength != null)
await _sharedPreferences.setInt(PreferencesKey.currentPinLength, currentPinLength);
if (currentTheme != null && DeviceInfo.instance.isMobile)
if (currentTheme != null && DeviceInfo.instance.isMobile) {
await _sharedPreferences.setInt(PreferencesKey.currentTheme, currentTheme);
// enforce dark theme on desktop platforms until the design is ready:
} else if (DeviceInfo.instance.isDesktop) {
await _sharedPreferences.setInt(PreferencesKey.currentTheme, ThemeList.darkTheme.raw);
}
if (exchangeStatus != null)
await _sharedPreferences.setInt(PreferencesKey.exchangeStatusKey, exchangeStatus);
@ -380,6 +391,13 @@ class BackupService {
if (useEtherscan != null)
await _sharedPreferences.setBool(PreferencesKey.useEtherscan, useEtherscan);
if (defaultNanoRep != null)
await _sharedPreferences.setString(PreferencesKey.defaultNanoRep, defaultNanoRep);
if (defaultBananoRep != null)
await _sharedPreferences.setString(PreferencesKey.defaultBananoRep, defaultBananoRep);
if (syncAll != null) await _sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll);
if (lookupsTwitter != null)
await _sharedPreferences.setBool(PreferencesKey.lookupsTwitter, lookupsTwitter);
@ -390,19 +408,17 @@ class BackupService {
await _sharedPreferences.setBool(PreferencesKey.lookupsYatService, lookupsYatService);
if (lookupsUnstoppableDomains != null)
await _sharedPreferences.setBool(PreferencesKey.lookupsUnstoppableDomains, lookupsUnstoppableDomains);
await _sharedPreferences.setBool(
PreferencesKey.lookupsUnstoppableDomains, lookupsUnstoppableDomains);
if (lookupsOpenAlias != null)
await _sharedPreferences.setBool(PreferencesKey.lookupsOpenAlias, lookupsOpenAlias);
if (lookupsENS != null)
await _sharedPreferences.setBool(PreferencesKey.lookupsENS, lookupsENS);
if (lookupsENS != null) await _sharedPreferences.setBool(PreferencesKey.lookupsENS, lookupsENS);
if (syncAll != null)
await _sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll);
if (syncAll != null) await _sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll);
if (syncMode != null)
await _sharedPreferences.setInt(PreferencesKey.syncModeKey, syncMode);
if (syncMode != null) await _sharedPreferences.setInt(PreferencesKey.syncModeKey, syncMode);
await preferencesFile.delete();
}
@ -507,7 +523,8 @@ class BackupService {
_sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey),
PreferencesKey.disableBuyKey: _sharedPreferences.getBool(PreferencesKey.disableBuyKey),
PreferencesKey.disableSellKey: _sharedPreferences.getBool(PreferencesKey.disableSellKey),
PreferencesKey.defaultBuyProvider: _sharedPreferences.getInt(PreferencesKey.defaultBuyProvider),
PreferencesKey.defaultBuyProvider:
_sharedPreferences.getInt(PreferencesKey.defaultBuyProvider),
PreferencesKey.isDarkThemeLegacy:
_sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy),
PreferencesKey.currentPinLength: _sharedPreferences.getInt(PreferencesKey.currentPinLength),
@ -549,28 +566,23 @@ class BackupService {
_sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForCreatingNewWallets),
PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings: _sharedPreferences
.getBool(PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings),
PreferencesKey.sortBalanceBy:
_sharedPreferences.getInt(PreferencesKey.sortBalanceBy),
PreferencesKey.sortBalanceBy: _sharedPreferences.getInt(PreferencesKey.sortBalanceBy),
PreferencesKey.pinNativeTokenAtTop:
_sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop),
PreferencesKey.useEtherscan:
_sharedPreferences.getBool(PreferencesKey.useEtherscan),
PreferencesKey.lookupsTwitter:
_sharedPreferences.getBool(PreferencesKey.lookupsTwitter),
PreferencesKey.lookupsMastodon:
_sharedPreferences.getBool(PreferencesKey.lookupsMastodon),
PreferencesKey.useEtherscan: _sharedPreferences.getBool(PreferencesKey.useEtherscan),
PreferencesKey.defaultNanoRep: _sharedPreferences.getString(PreferencesKey.defaultNanoRep),
PreferencesKey.defaultBananoRep:
_sharedPreferences.getString(PreferencesKey.defaultBananoRep),
PreferencesKey.lookupsTwitter: _sharedPreferences.getBool(PreferencesKey.lookupsTwitter),
PreferencesKey.lookupsMastodon: _sharedPreferences.getBool(PreferencesKey.lookupsMastodon),
PreferencesKey.lookupsYatService:
_sharedPreferences.getBool(PreferencesKey.lookupsYatService),
_sharedPreferences.getBool(PreferencesKey.lookupsYatService),
PreferencesKey.lookupsUnstoppableDomains:
_sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains),
PreferencesKey.lookupsOpenAlias:
_sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias),
PreferencesKey.lookupsENS:
_sharedPreferences.getBool(PreferencesKey.lookupsENS),
PreferencesKey.syncModeKey:
_sharedPreferences.getInt(PreferencesKey.syncModeKey),
PreferencesKey.syncAllKey:
_sharedPreferences.getBool(PreferencesKey.syncAllKey),
_sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains),
PreferencesKey.lookupsOpenAlias: _sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias),
PreferencesKey.lookupsENS: _sharedPreferences.getBool(PreferencesKey.lookupsENS),
PreferencesKey.syncModeKey: _sharedPreferences.getInt(PreferencesKey.syncModeKey),
PreferencesKey.syncAllKey: _sharedPreferences.getBool(PreferencesKey.syncAllKey),
PreferencesKey.autoGenerateSubaddressStatusKey:
_sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey),
};

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:flutter/foundation.dart';
@ -16,6 +17,7 @@ class WalletCreationService {
{required WalletType initialType,
required this.keyService,
required this.sharedPreferences,
required this.settingsStore,
required this.walletInfoSource})
: type = initialType {
changeWalletType(type: type);
@ -23,6 +25,7 @@ class WalletCreationService {
WalletType type;
final SharedPreferences sharedPreferences;
final SettingsStore settingsStore;
final KeyService keyService;
final Box<WalletInfo> walletInfoSource;
WalletService? _service;
@ -58,6 +61,10 @@ class WalletCreationService {
password: credentials.password!, walletName: credentials.name);
}
if (type == WalletType.bitcoinCash || type == WalletType.ethereum) {
credentials.seedPhraseLength = settingsStore.seedPhraseLength.value;
}
final wallet = await _service!.create(credentials, isTestnet: isTestnet);
if (wallet.type == WalletType.monero) {

View file

@ -55,6 +55,8 @@ import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
@ -103,6 +105,7 @@ import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
@ -345,6 +348,7 @@ Future<void> setup({
initialType: type,
keyService: getIt.get<KeyService>(),
sharedPreferences: getIt.get<SharedPreferences>(),
settingsStore: getIt.get<SettingsStore>(),
walletInfoSource: _walletInfoSource));
getIt.registerFactory<WalletLoadingService>(() => WalletLoadingService(
@ -352,9 +356,10 @@ Future<void> setup({
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,
type: type));
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) =>
WalletNewVM(getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
type: type));
getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) {
return WalletRestorationFromQRVM(getIt.get<AppStore>(),
@ -712,6 +717,8 @@ Future<void> setup({
return PrivacySettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!);
});
getIt.registerFactory(() => TrocadorProvidersViewModel(getIt.get<SettingsStore>()));
getIt.registerFactory(() {
return OtherSettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!);
});
@ -765,13 +772,18 @@ Future<void> setup({
getIt.registerFactory(() => PrivacyPage(getIt.get<PrivacySettingsViewModel>()));
getIt.registerFactory(() => TrocadorProvidersPage(getIt.get<TrocadorProvidersViewModel>()));
getIt.registerFactory(() => DomainLookupsPage(getIt.get<PrivacySettingsViewModel>()));
getIt.registerFactory(() => DisplaySettingsPage(getIt.get<DisplaySettingsViewModel>()));
getIt.registerFactory(() => OtherSettingsPage(getIt.get<OtherSettingsViewModel>()));
getIt.registerFactory(() => NanoChangeRepPage(getIt.get<AppStore>().wallet!));
getIt.registerFactory(() => NanoChangeRepPage(
settingsStore: getIt.get<AppStore>().settingsStore,
wallet: getIt.get<AppStore>().wallet!,
));
getIt.registerFactoryParam<NodeCreateOrEditViewModel, WalletType?, bool?>(
(WalletType? type, bool? isPow) => NodeCreateOrEditViewModel(
@ -906,8 +918,9 @@ Future<void> setup({
getIt.registerFactoryParam<NewWalletTypePage, void Function(BuildContext, WalletType), bool?>(
(param1, isCreate) => NewWalletTypePage(onTypeSelected: param1, isCreate: isCreate ?? true));
getIt.registerFactoryParam<PreSeedPage, WalletType, void>(
(WalletType type, _) => PreSeedPage(type));
getIt.registerFactoryParam<PreSeedPage, WalletType, AdvancedPrivacySettingsViewModel>(
(WalletType type, AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel)
=> PreSeedPage(type, advancedPrivacySettingsViewModel));
getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
TradeDetailsViewModel(
@ -1230,6 +1243,7 @@ Future<void> setup({
getIt.registerFactory(
() => WalletConnectConnectionsView(web3walletService: getIt.get<Web3WalletService>()));
getIt.registerFactory<TorPage>(() => TorPage(getIt.get<AppStore>()));
_isSetupFinished = true;
}

View file

@ -2,18 +2,18 @@ import 'package:encrypt/encrypt.dart';
// import 'package:password/password.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
String encrypt({required String source, required String key, int keyLength = 16}) {
String encrypt({required String source, required String key}) {
final _key = Key.fromUtf8(key);
final iv = IV.fromLength(keyLength);
final iv = IV.allZerosOfLength(16);
final encrypter = Encrypter(AES(_key));
final encrypted = encrypter.encrypt(source, iv: iv);
return encrypted.base64;
}
String decrypt({required String source, required String key, int keyLength = 16}) {
String decrypt({required String source, required String key}) {
final _key = Key.fromUtf8(key);
final iv = IV.fromLength(keyLength);
final iv = IV.allZerosOfLength(16);
final encrypter = Encrypter(AES(_key));
final decrypted = encrypter.decrypt64(source, iv: iv);

View file

@ -52,7 +52,7 @@ class MainActions {
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.ethereum:
// case WalletType.bitcoinCash: // TODO: add sign message function to BCH first
case WalletType.bitcoinCash:
switch (defaultBuyProvider) {
case BuyProviderType.AskEachTime:
Navigator.pushNamed(context, Routes.buy);

View file

@ -50,6 +50,8 @@ class PreferencesKey {
static const sortBalanceBy = 'sort_balance_by';
static const pinNativeTokenAtTop = 'pin_native_token_at_top';
static const useEtherscan = 'use_etherscan';
static const defaultNanoRep = 'default_nano_representative';
static const defaultBananoRep = 'default_banano_representative';
static const lookupsTwitter = 'looks_up_twitter';
static const lookupsMastodon = 'looks_up_mastodon';
static const lookupsYatService = 'looks_up_mastodon';
@ -87,4 +89,5 @@ class PreferencesKey {
'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';
}

View file

@ -0,0 +1,26 @@
import 'package:cake_wallet/generated/i18n.dart';
enum SeedPhraseLength {
twelveWords(12),
twentyFourWords(24);
const SeedPhraseLength(this.value);
final int value;
static SeedPhraseLength deserialize({required int raw}) =>
SeedPhraseLength.values.firstWhere((e) => e.value == raw);
@override
String toString() {
String label = '';
switch (this) {
case SeedPhraseLength.twelveWords:
label = '12 Words';
break;
case SeedPhraseLength.twentyFourWords:
label = '24 Words';
break;
}
return label;
}
}

View file

@ -12,11 +12,27 @@ import 'package:cw_core/crypto_currency.dart';
import 'package:http/http.dart';
class TrocadorExchangeProvider extends ExchangeProvider {
TrocadorExchangeProvider({this.useTorOnly = false})
: _lastUsedRateId = '',
TrocadorExchangeProvider({this.useTorOnly = false, this.providerStates = const {}})
: _lastUsedRateId = '', _provider = [],
super(pairList: supportedPairs(_notSupported));
bool useTorOnly;
final Map<String, bool> providerStates;
static const List<String> availableProviders = [
'Swapter',
'StealthEx',
'Simpleswap',
'Swapuz'
'ChangeNow',
'Changehero',
'FixedFloat',
'LetsExchange',
'Exolix',
'Godex',
'Exch',
'CoinCraddle'
];
static const List<CryptoCurrency> _notSupported = [
CryptoCurrency.stx,
@ -33,6 +49,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
static const coinPath = 'api/coin';
String _lastUsedRateId;
List<dynamic> _provider;
@override
String get title => 'Trocador';
@ -105,7 +122,6 @@ class TrocadorExchangeProvider extends ExchangeProvider {
'payment': isFixedRateMode ? 'True' : 'False',
'min_kycrating': 'C',
'markup': markup,
'best_only': 'True',
};
final uri = await _getUri(newRatePath, params);
@ -115,6 +131,9 @@ class TrocadorExchangeProvider extends ExchangeProvider {
final toAmount = double.parse(responseJSON['amount_to'].toString());
final rateId = responseJSON['trade_id'] as String? ?? '';
var quotes = responseJSON['quotes']['quotes'] as List;
_provider = quotes.map((quote) => quote['provider']).toList();
if (rateId.isNotEmpty) _lastUsedRateId = rateId;
return isReceiveAmount ? (amount / fromAmount) : (toAmount / amount);
@ -126,6 +145,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
@override
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
final params = {
'api_key': apiKey,
'ticker_from': _normalizeCurrency(request.fromCurrency),
@ -135,7 +155,6 @@ class TrocadorExchangeProvider extends ExchangeProvider {
'payment': isFixedRateMode ? 'True' : 'False',
'min_kycrating': 'C',
'markup': markup,
'best_only': 'True',
if (!isFixedRateMode) 'amount_from': request.fromAmount,
if (isFixedRateMode) 'amount_to': request.toAmount,
'address': request.toAddress,
@ -153,6 +172,22 @@ class TrocadorExchangeProvider extends ExchangeProvider {
params['id'] = _lastUsedRateId;
}
String firstAvailableProvider = '';
for (var provider in _provider) {
if (providerStates.containsKey(provider) && providerStates[provider] == true) {
firstAvailableProvider = provider as String;
break;
}
}
if (firstAvailableProvider.isEmpty) {
throw Exception('No available provider is enabled');
}
params['provider'] = firstAvailableProvider;
final uri = await _getUri(createTradePath, params);
final response = await get(uri);

View file

@ -6,7 +6,6 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/ionia/ionia_api.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/ionia/ionia_category.dart';
// import 'package:platform_device_id/platform_device_id.dart';
class IoniaService {
IoniaService(this.secureStorage, this.ioniaApi);

View file

@ -321,10 +321,7 @@ class CWMonero extends Monero {
@override
List<Unspent> getUnspents(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.unspentCoins
.map((MoneroUnspent moneroUnspent) => Unspent(moneroUnspent.address, moneroUnspent.hash,
moneroUnspent.value, 0, moneroUnspent.keyImage))
.toList();
return moneroWallet.unspentCoins;
}
@override

View file

@ -166,7 +166,10 @@ class CWNano extends Nano {
@override
Future<void> changeRep(Object wallet, String address) async {
return (wallet as NanoWallet).changeRep(address);
if ((wallet as NanoWallet).transactionHistory.transactions.isEmpty) {
throw Exception("Can't change representative without an existing transaction history");
}
return wallet.changeRep(address);
}
@override

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/update_haven_rate.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_info.dart';

View file

@ -42,6 +42,8 @@ import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
@ -350,6 +352,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<PrivacyPage>());
case Routes.trocadorProvidersPage:
return CupertinoPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<TrocadorProvidersPage>());
case Routes.domainLookupsPage:
return CupertinoPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<DomainLookupsPage>());
@ -452,7 +458,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.preSeed:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<PreSeedPage>(param1: settings.arguments as WalletType));
builder: (_) => getIt.get<PreSeedPage>(
param1: settings.arguments as WalletType,
param2: getIt.get<AdvancedPrivacySettingsViewModel>(
param1: settings.arguments as WalletType)));
case Routes.backup:
return CupertinoPageRoute<void>(
@ -647,6 +656,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
web3walletService: getIt.get<Web3WalletService>(),
launchUri: settings.arguments as Uri?,
));
case Routes.torPage:
return MaterialPageRoute<void>(builder: (_) => getIt.get<TorPage>());
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(

View file

@ -81,6 +81,7 @@ class Routes {
static const connectionSync = '/connection_sync_page';
static const securityBackupPage = '/security_and_backup_page';
static const privacyPage = '/privacy_page';
static const trocadorProvidersPage = '/trocador_providers_page';
static const domainLookupsPage = '/domain_lookups_page';
static const displaySettingsPage = '/display_settings_page';
static const otherSettingsPage = '/other_settings_page';
@ -102,4 +103,5 @@ class Routes {
static const manageNodes = '/manage_nodes';
static const managePowNodes = '/manage_pow_nodes';
static const walletConnectConnectionsListing = '/wallet-connect-connections-listing';
static const torPage = '/tor_page';
}

View file

@ -17,7 +17,6 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:permission_handler/permission_handler.dart';
class BackupPage extends BasePage {
BackupPage(this.backupViewModelBase);
@ -129,15 +128,8 @@ class BackupPage extends BasePage {
alertTitle: S.of(context).export_backup,
alertContent: S.of(context).select_destination,
rightButtonText: S.of(context).save_to_downloads,
leftButtonText:S.of(context).share,
leftButtonText: S.of(context).share,
actionRightButton: () async {
final permission = await Permission.storage.request();
if (permission.isDenied) {
Navigator.of(dialogContext).pop();
return;
}
await backupViewModelBase.saveToDownload(backup.name, backup.content);
Navigator.of(dialogContext).pop();
},

View file

@ -34,16 +34,6 @@ class BuyOptionsPage extends BasePage {
constraints: BoxConstraints(maxWidth: 330),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
image: iconRobinhood,
title: "Robinhood Connect",
description: S.of(context).robinhood_option_description,
onPressed: () async =>
await getIt.get<RobinhoodBuyProvider>().launchProvider(context),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
@ -54,6 +44,16 @@ class BuyOptionsPage extends BasePage {
await getIt.get<OnRamperBuyProvider>().launchProvider(context),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
image: iconRobinhood,
title: "Robinhood Connect",
description: S.of(context).robinhood_option_description,
onPressed: () async =>
await getIt.get<RobinhoodBuyProvider>().launchProvider(context),
),
),
Spacer(),
Padding(
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),

View file

@ -165,7 +165,9 @@ class BalancePage extends StatelessWidget {
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: hasAdditionalBalance ? () => _showBalanceDescription(context) : null,
onTap: hasAdditionalBalance ? () =>
_showBalanceDescription(context, S.current.available_balance_description)
: null,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -225,47 +227,65 @@ class BalancePage extends StatelessWidget {
],
),
if (frozenBalance.isNotEmpty)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 26),
Text(
S.current.frozen_balance,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
height: 1,
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: hasAdditionalBalance ?
() => _showBalanceDescription(context, S.current.unavailable_balance_description)
: null,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 26),
Row(
children: [
Text(
S.current.unavailable_balance,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
height: 1,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Icon(Icons.help_outline,
size: 16,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor),
),
],
),
),
SizedBox(height: 8),
AutoSizeText(
frozenBalance,
style: TextStyle(
fontSize: 20,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
height: 1,
SizedBox(height: 8),
AutoSizeText(
frozenBalance,
style: TextStyle(
fontSize: 20,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
height: 1,
),
maxLines: 1,
textAlign: TextAlign.center,
),
maxLines: 1,
textAlign: TextAlign.center,
),
SizedBox(height: 4),
Text(
frozenFiatBalance,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
height: 1,
SizedBox(height: 4),
Text(
frozenFiatBalance,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
height: 1,
),
),
),
],
],
),
),
if (hasAdditionalBalance)
Column(
@ -316,9 +336,9 @@ class BalancePage extends StatelessWidget {
);
}
void _showBalanceDescription(BuildContext context) {
void _showBalanceDescription(BuildContext context, String content) {
showPopUp<void>(
context: context,
builder: (_) => InformationPage(information: S.current.available_balance_description));
builder: (_) => InformationPage(information: content));
}
}

View file

@ -15,7 +15,7 @@ class MoneroAccountListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
double itemHeight = 80;
double itemHeight = 65;
double buttonHeight = 62;
return Observer(builder: (_) {
@ -31,7 +31,7 @@ class MoneroAccountListPage extends StatelessWidget {
child: ListView.separated(
padding: EdgeInsets.zero,
controller: controller,
separatorBuilder: (context, index) => const VerticalSectionDivider(),
separatorBuilder: (context, index) => const HorizontalSectionDivider(),
itemCount: accounts.length,
itemBuilder: (context, index) {
final account = accounts[index];

View file

@ -1,5 +1,4 @@
import 'package:cake_wallet/themes/extensions/account_list_theme.dart';
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:cake_wallet/generated/i18n.dart';
@ -33,7 +32,7 @@ class AccountTile extends StatelessWidget {
final Widget cell = GestureDetector(
onTap: onTap,
child: Container(
height: 77,
height: 60,
width: double.infinity,
padding: EdgeInsets.only(left: 24, right: 24),
color: color,

View file

@ -1,8 +1,11 @@
import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/extensions/address_theme.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart';
@ -14,21 +17,28 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
class NanoChangeRepPage extends BasePage {
NanoChangeRepPage(WalletBase wallet)
NanoChangeRepPage({required SettingsStore settingsStore, required WalletBase wallet})
: _wallet = wallet,
_addressController = TextEditingController() {
_settingsStore = settingsStore,
_addressController = TextEditingController(),
_formKey = GlobalKey<FormState>() {
_addressController.text = nano!.getRepresentative(wallet);
}
final TextEditingController _addressController;
final WalletBase _wallet;
final SettingsStore _settingsStore;
final GlobalKey<FormState> _formKey;
@override
String get title => S.current.change_rep;
@override
Widget body(BuildContext context) {
return Container(
return Form(
key: _formKey,
child: Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24.0),
@ -38,9 +48,17 @@ class NanoChangeRepPage extends BasePage {
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
child: AddressTextField(
controller: _addressController,
hintText: S.of(context).node_address,
onURIScanned: (uri) {
final paymentRequest = PaymentRequest.fromUri(uri);
_addressController.text = paymentRequest.address;
},
options: [
AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode,
],
buttonColor: Theme.of(context).extension<AddressTheme>()!.actionButtonColor,
validator: AddressValidator(type: CryptoCurrency.nano),
),
)
@ -59,6 +77,11 @@ class NanoChangeRepPage extends BasePage {
padding: EdgeInsets.only(right: 8.0),
child: LoadingPrimaryButton(
onPressed: () async {
if (_formKey.currentState != null &&
!_formKey.currentState!.validate()) {
return;
}
final confirmed = await showPopUp<bool>(
context: context,
builder: (BuildContext context) {
@ -74,8 +97,19 @@ class NanoChangeRepPage extends BasePage {
if (confirmed) {
try {
_settingsStore.defaultNanoRep = _addressController.text;
await nano!.changeRep(_wallet, _addressController.text);
Navigator.of(context).pop();
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).successful,
alertContent: S.of(context).change_rep_successful,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.pop(context));
});
} catch (e) {
await showPopUp<void>(
context: context,
@ -97,6 +131,8 @@ class NanoChangeRepPage extends BasePage {
)),
],
)),
));
),
),
);
}
}

View file

@ -1,7 +1,9 @@
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/seed_phrase_length.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
@ -94,6 +96,17 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
],
);
}),
if (widget.privacySettingsViewModel.hasSeedPhraseLengthOption)
Observer(builder: (_) {
return SettingsPickerCell<SeedPhraseLength>(
title: S.current.seed_phrase_length,
items: SeedPhraseLength.values,
selectedItem: widget.privacySettingsViewModel.seedPhraseLength,
onItemSelected: (SeedPhraseLength length) {
widget.privacySettingsViewModel.setSeedPhraseLength(length);
},
);
}),
],
),
bottomSectionPadding: EdgeInsets.all(24),

View file

@ -78,7 +78,7 @@ class NodeCreateOrEditPage extends BasePage {
'assets/images/qr_code_icon.png',
),
);
final NodeCreateOrEditViewModel nodeCreateOrEditViewModel;
final Node? editingNode;
final bool? isSelected;
@ -133,27 +133,20 @@ class NodeCreateOrEditPage extends BasePage {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: LoadingPrimaryButton(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: LoadingPrimaryButton(
onPressed: () async {
final confirmed = await showPopUp<bool>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle:
S.of(context).remove_node,
alertContent: S
.of(context)
.remove_node_message,
rightButtonText:
S.of(context).remove,
leftButtonText:
S.of(context).cancel,
actionRightButton: () =>
Navigator.pop(context, true),
actionLeftButton: () =>
Navigator.pop(context, false));
alertTitle: S.of(context).remove_node,
alertContent: S.of(context).remove_node_message,
rightButtonText: S.of(context).remove,
leftButtonText: S.of(context).cancel,
actionRightButton: () => Navigator.pop(context, true),
actionLeftButton: () => Navigator.pop(context, false));
}) ??
false;
@ -163,11 +156,14 @@ class NodeCreateOrEditPage extends BasePage {
}
},
text: S.of(context).delete,
isDisabled: !nodeCreateOrEditViewModel.isReady ||
isDisabled: editingNode == null ||
!nodeCreateOrEditViewModel.isReady ||
(isSelected ?? false),
color: Palette.red,
textColor: Colors.white),
)),
textColor: Colors.white,
),
),
),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 8.0),

View file

@ -4,7 +4,6 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
@ -31,10 +30,11 @@ class RestoreFromBackupPage extends BasePage {
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
});
});
}
@ -44,42 +44,97 @@ class RestoreFromBackupPage extends BasePage {
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
child: Padding(
padding: EdgeInsets.only(bottom: 24, left: 24, right: 24),
child: Column(children: [
padding: EdgeInsets.only(bottom: 24, left: 24, right: 24),
child: Column(
children: [
Expanded(
child: Container(
child: Center(
child: TextFormField(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
obscureText: true,
enableSuggestions: false,
autocorrect: false,
decoration: InputDecoration(
hintText: S.of(context).enter_backup_password),
decoration:
InputDecoration(hintText: S.of(context).enter_backup_password),
keyboardType: TextInputType.visiblePassword,
controller: textEditingController,
style: TextStyle(fontSize: 26, color: Colors.black))),
style: TextStyle(fontSize: 26, color: Colors.black),
),
Observer(
builder: (_) {
if (restoreFromBackupViewModel.filePath.isNotEmpty) {
return Column(
children: [
const SizedBox(height: 100),
Row(
children: [
Text(
"File Name: ",
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: titleColor(context),
),
),
Expanded(
child: Text(
restoreFromBackupViewModel.filePath.split("/").last,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: titleColor(context),
),
),
),
],
),
],
);
}
return const SizedBox();
},
),
],
),
),
),
),
Container(
child: Row(children: [
Expanded(
child: PrimaryButton(
child: Row(
children: [
Expanded(
child: PrimaryButton(
onPressed: () => presentFilePicker(),
text: S.of(context).select_backup_file,
color: Colors.grey,
textColor: Colors.white)),
SizedBox(width: 20),
Expanded(child: Observer(builder: (_) {
return LoadingPrimaryButton(
isLoading:
restoreFromBackupViewModel.state is IsExecutingState,
onPressed: () => onImportHandler(context),
text: S.of(context).import,
color: Theme.of(context).primaryColor,
textColor: Colors.white);
}))
])),
])),
textColor: Colors.white,
),
),
SizedBox(width: 20),
Expanded(
child: Observer(
builder: (_) {
return LoadingPrimaryButton(
isLoading: restoreFromBackupViewModel.state is IsExecutingState,
onPressed: () => onImportHandler(context),
text: S.of(context).import,
color: Theme.of(context).primaryColor,
textColor: Colors.white);
},
),
),
],
),
),
],
),
),
),
);
}
@ -87,7 +142,7 @@ class RestoreFromBackupPage extends BasePage {
Future<void> presentFilePicker() async {
final result = await FilePicker.platform.pickFiles();
if (result?.files?.isEmpty ?? true) {
if (result?.files.isEmpty ?? true) {
return;
}
@ -95,8 +150,7 @@ class RestoreFromBackupPage extends BasePage {
}
Future<void> onImportHandler(BuildContext context) async {
if (textEditingController.text.isEmpty ||
(restoreFromBackupViewModel.filePath.isEmpty ?? true)) {
if (textEditingController.text.isEmpty || (restoreFromBackupViewModel.filePath.isEmpty)) {
await showPopUp<void>(
context: context,
builder: (_) {

View file

@ -21,7 +21,6 @@ class RestoreOptionsPage extends BasePage {
@override
String get title => S.current.restore_restore_wallet;
final bool isNewInstall;
final imageSeedKeys = Image.asset('assets/images/restore_wallet_image.png');
final imageBackup = Image.asset('assets/images/backup.png');
@ -38,8 +37,7 @@ class RestoreOptionsPage extends BasePage {
child: Column(
children: <Widget>[
OptionTile(
onPressed: () => Navigator.pushNamed(
context, Routes.restoreWalletFromSeedKeys,
onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletFromSeedKeys,
arguments: isNewInstall),
image: imageSeedKeys,
title: S.of(context).restore_title_from_seed_keys,
@ -58,7 +56,7 @@ class RestoreOptionsPage extends BasePage {
child: OptionTile(
onPressed: () async {
bool isCameraPermissionGranted =
await PermissionHandler.checkPermission(Permission.camera, context);
await PermissionHandler.checkPermission(Permission.camera, context);
if (!isCameraPermissionGranted) return;
bool isPinSet = false;
if (isNewInstall) {
@ -73,7 +71,8 @@ class RestoreOptionsPage extends BasePage {
final restoreWallet =
await WalletRestoreFromQRCode.scanQRCodeForRestoring(context);
final restoreFromQRViewModel = getIt.get<WalletRestorationFromQRVM>(param1: restoreWallet.type);
final restoreFromQRViewModel =
getIt.get<WalletRestorationFromQRVM>(param1: restoreWallet.type);
await restoreFromQRViewModel.create(restoreWallet: restoreWallet);
if (restoreFromQRViewModel.state is FailureState) {

View file

@ -204,23 +204,35 @@ class WalletRestorePage extends BasePage {
),
Padding(
padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24),
child: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: () async {
await _confirmForm(context);
child: Column(
children: [
Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: () async {
await _confirmForm(context);
},
text: S.of(context).restore_recover,
color: Theme.of(context)
.extension<WalletListTheme>()!
.createNewWalletButtonBackgroundColor,
textColor: Theme.of(context)
.extension<WalletListTheme>()!
.restoreWalletButtonTextColor,
isLoading: walletRestoreViewModel.state is IsExecutingState,
isDisabled: !walletRestoreViewModel.isButtonEnabled,
);
},
text: S.of(context).restore_recover,
color: Theme.of(context)
.extension<WalletListTheme>()!
.createNewWalletButtonBackgroundColor,
textColor: Theme.of(context)
.extension<WalletListTheme>()!
.restoreWalletButtonTextColor,
isLoading: walletRestoreViewModel.state is IsExecutingState,
isDisabled: !walletRestoreViewModel.isButtonEnabled,
);
},
),
const SizedBox(height: 25),
GestureDetector(
onTap: () {
Navigator.of(context)
.pushNamed(Routes.advancedPrivacySettings, arguments: walletRestoreViewModel.type);
},
child: Text(S.of(context).advanced_privacy_settings),
),
],
),
)
],

View file

@ -1,5 +1,6 @@
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
@ -9,15 +10,19 @@ import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/material.dart';
class PreSeedPage extends BasePage {
PreSeedPage(this.type)
PreSeedPage(this.type, this.advancedPrivacySettingsViewModel)
: imageLight = Image.asset('assets/images/pre_seed_light.png'),
imageDark = Image.asset('assets/images/pre_seed_dark.png'),
wordsCount = _wordsCount(type);
seedPhraseLength = advancedPrivacySettingsViewModel.seedPhraseLength.value {
wordsCount = _wordsCount(type, seedPhraseLength);
}
final Image imageDark;
final Image imageLight;
final WalletType type;
final int wordsCount;
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
final int seedPhraseLength;
late final int wordsCount;
@override
Widget? leading(BuildContext context) => null;
@ -68,13 +73,13 @@ class PreSeedPage extends BasePage {
));
}
static int _wordsCount(WalletType type) {
static int _wordsCount(WalletType type, int seedPhraseLength) {
switch (type) {
case WalletType.monero:
return 25;
case WalletType.ethereum:
case WalletType.bitcoinCash:
return 12;
return seedPhraseLength;
default:
return 24;
}

View file

@ -3,7 +3,6 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arro
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/wallet_connect_button.dart';
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
@ -90,7 +89,11 @@ class ConnectionSyncPage extends BasePage {
onTap: () => Navigator.of(context).pushNamed(Routes.walletConnectConnectionsListing),
),
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
]
],
SettingsCellWithArrow(
title: S.current.tor_connection,
handler: (context) => Navigator.of(context).pushNamed(Routes.torPage),
),
],
),
);

View file

@ -22,77 +22,83 @@ class PrivacyPage extends BasePage {
@override
Widget body(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Observer(builder: (_) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SettingsChoicesCell(
ChoicesListItem<FiatApiMode>(
title: S.current.fiat_api,
items: FiatApiMode.all,
selectedItem: _privacySettingsViewModel.fiatApiMode,
onItemSelected: (FiatApiMode fiatApiMode) =>
_privacySettingsViewModel.setFiatMode(fiatApiMode),
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(top: 10),
child: Observer(builder: (_) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SettingsChoicesCell(
ChoicesListItem<FiatApiMode>(
title: S.current.fiat_api,
items: FiatApiMode.all,
selectedItem: _privacySettingsViewModel.fiatApiMode,
onItemSelected: (FiatApiMode fiatApiMode) =>
_privacySettingsViewModel.setFiatMode(fiatApiMode),
),
),
),
SettingsChoicesCell(
ChoicesListItem<ExchangeApiMode>(
title: S.current.exchange,
items: ExchangeApiMode.all,
selectedItem: _privacySettingsViewModel.exchangeStatus,
onItemSelected: (ExchangeApiMode mode) =>
_privacySettingsViewModel.setExchangeApiMode(mode),
SettingsChoicesCell(
ChoicesListItem<ExchangeApiMode>(
title: S.current.exchange,
items: ExchangeApiMode.all,
selectedItem: _privacySettingsViewModel.exchangeStatus,
onItemSelected: (ExchangeApiMode mode) =>
_privacySettingsViewModel.setExchangeApiMode(mode),
),
),
),
SettingsSwitcherCell(
title: S.current.settings_save_recipient_address,
value: _privacySettingsViewModel.shouldSaveRecipientAddress,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setShouldSaveRecipientAddress(value);
}),
if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible)
SettingsSwitcherCell(
title: S.current.auto_generate_subaddresses,
value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setAutoGenerateSubaddresses(value);
},
),
if (DeviceInfo.instance.isMobile)
SettingsSwitcherCell(
title: S.current.prevent_screenshots,
value: _privacySettingsViewModel.isAppSecure,
title: S.current.settings_save_recipient_address,
value: _privacySettingsViewModel.shouldSaveRecipientAddress,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setIsAppSecure(value);
_privacySettingsViewModel.setShouldSaveRecipientAddress(value);
}),
SettingsSwitcherCell(
title: S.current.disable_buy,
value: _privacySettingsViewModel.disableBuy,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setDisableBuy(value);
}),
SettingsSwitcherCell(
title: S.current.disable_sell,
value: _privacySettingsViewModel.disableSell,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setDisableSell(value);
}),
if (_privacySettingsViewModel.canUseEtherscan)
SettingsSwitcherCell(
title: S.current.etherscan_history,
value: _privacySettingsViewModel.useEtherscan,
if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible)
SettingsSwitcherCell(
title: S.current.auto_generate_subaddresses,
value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setUseEtherscan(value);
_privacySettingsViewModel.setAutoGenerateSubaddresses(value);
},
),
if (DeviceInfo.instance.isMobile)
SettingsSwitcherCell(
title: S.current.prevent_screenshots,
value: _privacySettingsViewModel.isAppSecure,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setIsAppSecure(value);
}),
SettingsSwitcherCell(
title: S.current.disable_buy,
value: _privacySettingsViewModel.disableBuy,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setDisableBuy(value);
}),
SettingsCellWithArrow(
title: S.current.domain_looks_up,
handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage),
),
],
);
}),
SettingsSwitcherCell(
title: S.current.disable_sell,
value: _privacySettingsViewModel.disableSell,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setDisableSell(value);
}),
if (_privacySettingsViewModel.canUseEtherscan)
SettingsSwitcherCell(
title: S.current.etherscan_history,
value: _privacySettingsViewModel.useEtherscan,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setUseEtherscan(value);
}),
SettingsCellWithArrow(
title: S.current.domain_looks_up,
handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage),
),
SettingsCellWithArrow(
title: 'Trocador providers',
handler: (context) => Navigator.of(context).pushNamed(Routes.trocadorProvidersPage),
),
],
);
}),
),
);
}
}

View file

@ -0,0 +1,267 @@
import 'dart:async';
import 'dart:io';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:flutter/material.dart';
import 'package:tor/tor.dart';
class TorPage extends BasePage {
final AppStore appStore;
TorPage(this.appStore);
@override
Widget body(BuildContext context) {
return TorPageBody(appStore);
}
}
class TorPageBody extends StatefulWidget {
final AppStore appStore;
const TorPageBody(this.appStore, {Key? key}) : super(key: key);
@override
State<TorPageBody> createState() => _TorPageBodyState();
}
class _TorPageBodyState extends State<TorPageBody> {
bool torEnabled = false;
bool connecting = false;
// Set the default text for the host input field.
final hostController = TextEditingController(text: 'https://icanhazip.com/');
// https://check.torproject.org is another good option.
Future<void> startTor() async {
setState(() {
connecting = true; // Update flag
});
await Tor.init();
// Start the proxy
await Tor.instance.start();
// Toggle started flag.
setState(() {
torEnabled = Tor.instance.enabled; // Update flag
connecting = false;
});
final node = widget.appStore.settingsStore.getCurrentNode(widget.appStore.wallet!.type);
if (node.socksProxyAddress?.isEmpty ?? true) {
node.socksProxyAddress = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}";
}
widget.appStore.wallet!.connectToNode(node: node);
print('Done awaiting; tor should be running');
}
Future<void> endTor() async {
// Start the proxy
Tor.instance.disable();
// Toggle started flag.
setState(() {
torEnabled = Tor.instance.enabled; // Update flag
});
print('Done awaiting; tor should be stopped');
}
@override
void initState() {
super.initState();
torEnabled = Tor.instance.enabled;
}
@override
void dispose() {
// Clean up the controller when the widget is disposed.
hostController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(10),
child: connecting
? ConnectingScreen()
: torEnabled
? DisconnectScreen(disconnect: endTor)
: ConnectScreen(connect: startTor),
),
);
}
}
class ConnectScreen extends StatelessWidget {
final Function() connect;
const ConnectScreen({super.key, required this.connect});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 200,
height: 200,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
child: Icon(
Icons.lock,
color: Colors.white,
size: 100,
),
),
SizedBox(height: 20),
Text(
'Connect to Tor',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 10),
Text(
'Your connection to the Tor network ensures privacy and security.',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
textAlign: TextAlign.center,
),
SizedBox(height: 30),
ElevatedButton(
onPressed: connect,
style: ElevatedButton.styleFrom(
primary: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
child: Text(
'Connect',
style: TextStyle(
fontSize: 18,
color: Colors.white,
),
),
),
],
),
);
}
}
class DisconnectScreen extends StatelessWidget {
final Function() disconnect;
const DisconnectScreen({super.key, required this.disconnect});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 200,
height: 200,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.green,
),
child: Icon(
Icons.check,
color: Colors.white,
size: 100,
),
),
SizedBox(height: 20),
Text(
'Connected to Tor',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 10),
Text(
'You are currently connected to the Tor network.',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
textAlign: TextAlign.center,
),
SizedBox(height: 30),
ElevatedButton(
onPressed: disconnect,
style: ElevatedButton.styleFrom(
primary: Colors.red,
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
child: Text(
'Disconnect',
style: TextStyle(
fontSize: 18,
color: Colors.white,
),
),
),
],
),
);
}
}
class ConnectingScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 200,
height: 200,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.yellow,
),
child: Icon(
Icons.hourglass_bottom,
color: Colors.white,
size: 100,
),
),
SizedBox(height: 20),
Text(
'Connecting...',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 10),
],
),
);
}
}

View file

@ -0,0 +1,37 @@
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class TrocadorProvidersPage extends BasePage {
TrocadorProvidersPage(this.trocadorProvidersViewModel);
@override
String get title => 'Trocador Providers';
final TrocadorProvidersViewModel trocadorProvidersViewModel;
@override
Widget body(BuildContext context) {
final availableProviders = TrocadorExchangeProvider.availableProviders;
final providerStates = trocadorProvidersViewModel.providerStates;
return Container(
padding: EdgeInsets.only(top: 10),
child: ListView.builder(
itemCount: availableProviders.length,
itemBuilder: (_, index) {
String provider = availableProviders[index];
return Observer(
builder: (_) => SettingsSwitcherCell(
title: provider,
value: providerStates[provider] ?? false,
onValueChange: (BuildContext _, bool value) {
trocadorProvidersViewModel.toggleProviderState(provider);
}));
},
),
);
}
}

View file

@ -1,15 +1,13 @@
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
class UnspentCoinsListPage extends BasePage {
UnspentCoinsListPage({required this.unspentCoinsListViewModel});
@ -17,31 +15,10 @@ class UnspentCoinsListPage extends BasePage {
@override
String get title => S.current.unspent_coins_title;
//@override
//Widget trailing(BuildContext context) {
// final questionImage = Image.asset('assets/images/question_mark.png',
// color: Theme.of(context).extension<CakeTextTheme>()!.titleColor);
// return SizedBox(
// height: 20.0,
// width: 20.0,
// child: ButtonTheme(
// minWidth: double.minPositive,
// child: FlatButton(
// highlightColor: Colors.transparent,
// splashColor: Colors.transparent,
// padding: EdgeInsets.all(0),
// onPressed: () => showUnspentCoinsAlert(context),
// child: questionImage),
// ),
// );
//}
final UnspentCoinsListViewModel unspentCoinsListViewModel;
@override
Widget body(BuildContext context) =>
UnspentCoinsListForm(unspentCoinsListViewModel);
Widget body(BuildContext context) => UnspentCoinsListForm(unspentCoinsListViewModel);
}
class UnspentCoinsListForm extends StatefulWidget {
@ -50,8 +27,7 @@ class UnspentCoinsListForm extends StatefulWidget {
final UnspentCoinsListViewModel unspentCoinsListViewModel;
@override
UnspentCoinsListFormState createState() =>
UnspentCoinsListFormState(unspentCoinsListViewModel);
UnspentCoinsListFormState createState() => UnspentCoinsListFormState(unspentCoinsListViewModel);
}
class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
@ -59,16 +35,6 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
final UnspentCoinsListViewModel unspentCoinsListViewModel;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
}
void afterLayout(dynamic _) {
//showUnspentCoinsAlert(context);
}
@override
Widget build(BuildContext context) {
return Container(
@ -76,8 +42,7 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
child: Observer(
builder: (_) => ListView.separated(
itemCount: unspentCoinsListViewModel.items.length,
separatorBuilder: (_, __) =>
SizedBox(height: 15),
separatorBuilder: (_, __) => SizedBox(height: 15),
itemBuilder: (_, int index) {
return Observer(builder: (_) {
final item = unspentCoinsListViewModel.items[index];
@ -86,27 +51,23 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
: item.address;
return GestureDetector(
onTap: () =>
Navigator.of(context)
.pushNamed(Routes.unspentCoinsDetails,
arguments: [item, unspentCoinsListViewModel]),
onTap: () => Navigator.of(context).pushNamed(Routes.unspentCoinsDetails,
arguments: [item, unspentCoinsListViewModel]),
child: UnspentCoinsListItem(
note: item.note,
amount: item.amount,
address: address,
isSending: item.isSending,
isFrozen: item.isFrozen,
isChange: item.isChange,
onCheckBoxTap: item.isFrozen
? null
: () async {
item.isSending = !item.isSending;
await unspentCoinsListViewModel
.saveUnspentCoinInfo(item);}));
? null
: () async {
item.isSending = !item.isSending;
await unspentCoinsListViewModel.saveUnspentCoinInfo(item);
}));
});
}
)
)
);
})));
}
}

View file

@ -1,8 +1,8 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
class UnspentCoinsListItem extends StatelessWidget {
UnspentCoinsListItem({
@ -11,6 +11,7 @@ class UnspentCoinsListItem extends StatelessWidget {
required this.address,
required this.isSending,
required this.isFrozen,
required this.isChange,
this.onCheckBoxTap,
});
@ -19,6 +20,7 @@ class UnspentCoinsListItem extends StatelessWidget {
final String address;
final bool isSending;
final bool isFrozen;
final bool isChange;
final Function()? onCheckBoxTap;
@override
@ -27,9 +29,8 @@ class UnspentCoinsListItem extends StatelessWidget {
final selectedItemColor = Theme.of(context).primaryColor;
final itemColor = isSending ? selectedItemColor : unselectedItemColor;
final amountColor = isSending
? Colors.white
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
final amountColor =
isSending ? Colors.white : Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
final addressColor = isSending
? Colors.white.withOpacity(0.5)
: Theme.of(context).extension<CakeTextTheme>()!.buttonSecondaryTextColor;
@ -47,7 +48,8 @@ class UnspentCoinsListItem extends StatelessWidget {
child: StandardCheckbox(
iconColor: amountColor,
borderColor: addressColor,
value: isSending, onChanged: (value) => onCheckBoxTap?.call())),
value: isSending,
onChanged: (value) => onCheckBoxTap?.call())),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -57,9 +59,7 @@ class UnspentCoinsListItem extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
if (note.isNotEmpty)
AutoSizeText(
note,
@ -69,8 +69,8 @@ class UnspentCoinsListItem extends StatelessWidget {
),
AutoSizeText(
amount,
style:
TextStyle(color: amountColor, fontSize: 15, fontWeight: FontWeight.w600),
style: TextStyle(
color: amountColor, fontSize: 15, fontWeight: FontWeight.w600),
maxLines: 1,
)
]),
@ -84,23 +84,41 @@ class UnspentCoinsListItem extends StatelessWidget {
alignment: Alignment.center,
child: Text(
S.of(context).frozen,
style:
TextStyle(color: amountColor, fontSize: 7, fontWeight: FontWeight.w600),
))
style: TextStyle(
color: amountColor, fontSize: 7, fontWeight: FontWeight.w600),
)),
],
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AutoSizeText(
'${address.substring(0, 5)}...${address.substring(address.length-5)}', // ToDo: Maybe use address label
'${address.substring(0, 5)}...${address.substring(address.length - 5)}', // ToDo: Maybe use address label
style: TextStyle(
color: addressColor,
fontSize: 12,
),
maxLines: 1,
),
if (isChange)
Container(
height: 17,
padding: EdgeInsets.only(left: 6, right: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.5)),
color: Colors.white),
alignment: Alignment.center,
child: Text(
S.of(context).unspent_change,
style: TextStyle(
color: itemColor,
fontSize: 7,
fontWeight: FontWeight.w600,
),
),
),
],
),
),

View file

@ -3,7 +3,6 @@ import 'dart:ui';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/themes/extensions/alert_theme.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
class BaseAlertDialog extends StatelessWidget {
String get titleText => '';
@ -49,7 +48,7 @@ class BaseAlertDialog extends StatelessWidget {
Widget actionButtons(BuildContext context) {
return Container(
height: 52,
height: 60,
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,

View file

@ -52,7 +52,7 @@ class OptionTile extends StatelessWidget {
child: Text(
description,
style: TextStyle(
fontSize: 16,
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context).extension<OptionTileTheme>()!.descriptionColor,
),

View file

@ -9,7 +9,9 @@ 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/seed_phrase_length.dart';
import 'package:cake_wallet/entities/sort_balance_types.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
@ -71,6 +73,7 @@ abstract class SettingsStoreBase with Store {
required this.isBitcoinBuyEnabled,
required this.actionlistDisplayMode,
required this.pinTimeOutDuration,
required this.seedPhraseLength,
required Cake2FAPresetsOptions initialCake2FAPresetOptions,
required bool initialShouldRequireTOTP2FAForAccessingWallet,
required bool initialShouldRequireTOTP2FAForSendsToContact,
@ -84,6 +87,8 @@ abstract class SettingsStoreBase with Store {
required this.sortBalanceBy,
required this.pinNativeTokenAtTop,
required this.useEtherscan,
required this.defaultNanoRep,
required this.defaultBananoRep,
required this.lookupsTwitter,
required this.lookupsMastodon,
required this.lookupsYatService,
@ -162,6 +167,8 @@ abstract class SettingsStoreBase with Store {
priority[WalletType.bitcoinCash] = initialBitcoinCashTransactionPriority;
}
initializeTrocadorProviderStates();
reaction(
(_) => fiatCurrency,
(FiatCurrency fiatCurrency) => sharedPreferences.setString(
@ -327,6 +334,11 @@ abstract class SettingsStoreBase with Store {
(String languageCode) =>
sharedPreferences.setString(PreferencesKey.currentLanguageCode, languageCode));
reaction(
(_) => seedPhraseLength,
(SeedPhraseLength seedPhraseWordCount) =>
sharedPreferences.setInt(PreferencesKey.currentSeedPhraseLength, seedPhraseWordCount.value));
reaction(
(_) => pinTimeOutDuration,
(PinCodeRequiredDuration pinCodeInterval) =>
@ -369,6 +381,13 @@ abstract class SettingsStoreBase with Store {
(bool useEtherscan) =>
_sharedPreferences.setBool(PreferencesKey.useEtherscan, useEtherscan));
reaction((_) => defaultNanoRep,
(String nanoRep) => _sharedPreferences.setString(PreferencesKey.defaultNanoRep, nanoRep));
reaction(
(_) => defaultBananoRep,
(String bananoRep) =>
_sharedPreferences.setString(PreferencesKey.defaultBananoRep, bananoRep));
reaction(
(_) => lookupsTwitter,
(bool looksUpTwitter) =>
@ -417,6 +436,7 @@ abstract class SettingsStoreBase with Store {
static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenminutes;
static const defaultAutoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.initialized;
static final walletPasswordDirectInput = Platform.isLinux;
static const defaultSeedPhraseLength = SeedPhraseLength.twelveWords;
@observable
FiatCurrency fiatCurrency;
@ -508,6 +528,9 @@ abstract class SettingsStoreBase with Store {
@observable
PinCodeRequiredDuration pinTimeOutDuration;
@observable
SeedPhraseLength seedPhraseLength;
@computed
ThemeData get theme => currentTheme.themeData;
@ -517,6 +540,9 @@ abstract class SettingsStoreBase with Store {
@observable
ObservableMap<WalletType, TransactionPriority> priority;
@observable
ObservableMap<String, bool> trocadorProviderStates = ObservableMap<String, bool>();
@observable
SortBalanceBy sortBalanceBy;
@ -526,6 +552,12 @@ abstract class SettingsStoreBase with Store {
@observable
bool useEtherscan;
@observable
String defaultNanoRep;
@observable
String defaultBananoRep;
@observable
bool lookupsTwitter;
@ -603,8 +635,8 @@ abstract class SettingsStoreBase with Store {
TransactionPriority? moneroTransactionPriority = monero?.deserializeMoneroTransactionPriority(
raw: sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!);
TransactionPriority? bitcoinTransactionPriority =
bitcoin?.deserializeBitcoinTransactionPriority(
sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority)!);
bitcoin?.deserializeBitcoinTransactionPriority(
sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority)!);
TransactionPriority? havenTransactionPriority;
TransactionPriority? litecoinTransactionPriority;
@ -643,8 +675,8 @@ abstract class SettingsStoreBase with Store {
final isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false;
final disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? false;
final disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false;
final defaultBuyProvider = BuyProviderType.values[sharedPreferences.getInt(
PreferencesKey.defaultBuyProvider) ?? 0];
final defaultBuyProvider =
BuyProviderType.values[sharedPreferences.getInt(PreferencesKey.defaultBuyProvider) ?? 0];
final currentFiatApiMode = FiatApiMode.deserialize(
raw: sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey) ??
FiatApiMode.enabled.raw);
@ -663,7 +695,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToInternalWallets) ??
false;
final shouldRequireTOTP2FAForExchangesToInternalWallets = sharedPreferences
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets) ??
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets) ??
false;
final shouldRequireTOTP2FAForExchangesToExternalWallets = sharedPreferences
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToExternalWallets) ??
@ -674,7 +706,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForCreatingNewWallets) ??
false;
final shouldRequireTOTP2FAForAllSecurityAndBackupSettings = sharedPreferences
.getBool(PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings) ??
.getBool(PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings) ??
false;
final useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? false;
final totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? '';
@ -695,14 +727,20 @@ abstract class SettingsStoreBase with Store {
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];
SortBalanceBy.values[sharedPreferences.getInt(PreferencesKey.sortBalanceBy) ?? 0];
final pinNativeTokenAtTop =
sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true;
final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
final defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? "";
final lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true;
final lookupsMastodon = sharedPreferences.getBool(PreferencesKey.lookupsMastodon) ?? true;
final lookupsYatService = sharedPreferences.getBool(PreferencesKey.lookupsYatService) ?? true;
@ -719,11 +757,11 @@ abstract class SettingsStoreBase with Store {
await LanguageService.localeDetection();
final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
final bitcoinElectrumServerId =
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
final litecoinElectrumServerId =
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
final bitcoinCashElectrumServerId =
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
@ -740,7 +778,7 @@ abstract class SettingsStoreBase with Store {
final deviceName = await _getDeviceName() ?? '';
final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true;
final generateSubaddresses =
sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey);
sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey);
final autoGenerateSubaddressStatus = generateSubaddresses != null
? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses)
@ -781,10 +819,10 @@ abstract class SettingsStoreBase with Store {
powNodes[WalletType.nano] = nanoPowNode;
}
final savedSyncMode = SyncMode.all.firstWhere((element) {
return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 1);
});
final savedSyncAll = sharedPreferences.getBool(PreferencesKey.syncAllKey) ?? true;
final savedSyncMode = SyncMode.all.firstWhere((element) {
return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 1);
});
final savedSyncAll = sharedPreferences.getBool(PreferencesKey.syncAllKey) ?? true;
return SettingsStore(
sharedPreferences: sharedPreferences,
@ -813,10 +851,13 @@ abstract class SettingsStoreBase with Store {
actionlistDisplayMode: actionListDisplayMode,
initialPinLength: pinLength,
pinTimeOutDuration: pinCodeTimeOutDuration,
seedPhraseLength: seedPhraseWordCount,
initialLanguageCode: savedLanguageCode,
sortBalanceBy: sortBalanceBy,
pinNativeTokenAtTop: pinNativeTokenAtTop,
useEtherscan: useEtherscan,
defaultNanoRep: defaultNanoRep,
defaultBananoRep: defaultBananoRep,
lookupsTwitter: lookupsTwitter,
lookupsMastodon: lookupsMastodon,
lookupsYatService: lookupsYatService,
@ -855,35 +896,35 @@ abstract class SettingsStoreBase with Store {
raw: sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!);
priority[WalletType.monero] = monero?.deserializeMoneroTransactionPriority(
raw: sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ??
raw: sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ??
priority[WalletType.monero]!;
priority[WalletType.bitcoin] = bitcoin?.deserializeBitcoinTransactionPriority(
sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ??
sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ??
priority[WalletType.bitcoin]!;
if (sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) {
priority[WalletType.haven] = monero?.deserializeMoneroTransactionPriority(
raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!) ??
raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!) ??
priority[WalletType.haven]!;
}
if (sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority) != null) {
priority[WalletType.litecoin] = bitcoin?.deserializeLitecoinTransactionPriority(
sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!) ??
sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!) ??
priority[WalletType.litecoin]!;
}
if (sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority) != null) {
priority[WalletType.ethereum] = ethereum?.deserializeEthereumTransactionPriority(
sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority)!) ??
sharedPreferences.getInt(PreferencesKey.ethereumTransactionPriority)!) ??
priority[WalletType.ethereum]!;
}
if (sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority) != null) {
priority[WalletType.bitcoinCash] = bitcoinCash?.deserializeBitcoinCashTransactionPriority(
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!) ??
sharedPreferences.getInt(PreferencesKey.bitcoinCashTransactionPriority)!) ??
priority[WalletType.bitcoinCash]!;
}
final generateSubaddresses =
sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey);
sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey);
autoGenerateSubaddressStatus = generateSubaddresses != null
? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses)
@ -902,7 +943,7 @@ abstract class SettingsStoreBase with Store {
disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy;
disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell;
defaultBuyProvider =
BuyProviderType.values[sharedPreferences.getInt(PreferencesKey.defaultBuyProvider) ?? 0];
BuyProviderType.values[sharedPreferences.getInt(PreferencesKey.defaultBuyProvider) ?? 0];
allowBiometricalAuthentication =
sharedPreferences.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
allowBiometricalAuthentication;
@ -919,7 +960,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForSendsToInternalWallets) ??
false;
shouldRequireTOTP2FAForExchangesToInternalWallets = sharedPreferences
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets) ??
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToInternalWallets) ??
false;
shouldRequireTOTP2FAForExchangesToExternalWallets = sharedPreferences
.getBool(PreferencesKey.shouldRequireTOTP2FAForExchangesToExternalWallets) ??
@ -930,7 +971,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getBool(PreferencesKey.shouldRequireTOTP2FAForCreatingNewWallets) ??
false;
shouldRequireTOTP2FAForAllSecurityAndBackupSettings = sharedPreferences
.getBool(PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings) ??
.getBool(PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings) ??
false;
shouldShowMarketPlaceInDashboard =
sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ??
@ -963,6 +1004,8 @@ abstract class SettingsStoreBase with Store {
.values[sharedPreferences.getInt(PreferencesKey.sortBalanceBy) ?? sortBalanceBy.index];
pinNativeTokenAtTop = sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true;
useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? "";
lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true;
lookupsMastodon = sharedPreferences.getBool(PreferencesKey.lookupsMastodon) ?? true;
lookupsYatService = sharedPreferences.getBool(PreferencesKey.lookupsYatService) ?? true;
@ -972,11 +1015,11 @@ abstract class SettingsStoreBase with Store {
final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
final bitcoinElectrumServerId =
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
final litecoinElectrumServerId =
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
final bitcoinCashElectrumServerId =
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
@ -1038,7 +1081,8 @@ abstract class SettingsStoreBase with Store {
await _sharedPreferences.setInt(PreferencesKey.currentEthereumNodeIdKey, node.key as int);
break;
case WalletType.bitcoinCash:
await _sharedPreferences.setInt(PreferencesKey.currentBitcoinCashNodeIdKey, node.key as int);
await _sharedPreferences.setInt(
PreferencesKey.currentBitcoinCashNodeIdKey, node.key as int);
break;
case WalletType.nano:
await _sharedPreferences.setInt(PreferencesKey.currentNanoNodeIdKey, node.key as int);
@ -1062,6 +1106,19 @@ abstract class SettingsStoreBase with Store {
powNodes[walletType] = node;
}
void initializeTrocadorProviderStates() {
for (var provider in TrocadorExchangeProvider.availableProviders) {
final savedState = _sharedPreferences.getBool(provider) ?? true;
trocadorProviderStates[provider] = savedState;
}
}
void saveTrocadorProviderState(String providerName, bool state) {
_sharedPreferences.setBool(providerName, state);
trocadorProviderStates[providerName] = state;
}
static Future<String?> _getDeviceName() async {
String? deviceName = '';
final deviceInfoPlugin = DeviceInfoPlugin();

View file

@ -17,7 +17,9 @@ class PermissionHandler {
var status = await permission.status;
if (status.isDenied) {
status = await permission.request();
try {
status = await permission.request();
} catch (_) {}
}
if (status.isPermanentlyDenied || status.isDenied) {

View file

@ -1,5 +1,6 @@
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/seed_phrase_length.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:mobx/mobx.dart';
@ -22,9 +23,15 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
final SettingsStore _settingsStore;
bool get hasSeedPhraseLengthOption =>
type == WalletType.bitcoinCash || type == WalletType.ethereum;
@computed
bool get addCustomNode => _addCustomNode;
@computed
SeedPhraseLength get seedPhraseLength => _settingsStore.seedPhraseLength;
@action
void setFiatApiMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode;
@ -33,4 +40,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
@action
void toggleAddCustomNode() => _addCustomNode = !_addCustomNode;
@action
void setSeedPhraseLength(SeedPhraseLength length) => _settingsStore.seedPhraseLength = length;
}

View file

@ -175,10 +175,8 @@ abstract class BalanceViewModelBase with Store {
return '---';
}
return _getFiatBalance(
price: price,
cryptoAmount: getFormattedFrozenBalance(walletBalance)) + ' ' + fiatCurrency.toString();
return _getFiatBalance(price: price, cryptoAmount: getFormattedFrozenBalance(walletBalance)) +
' ${fiatCurrency.toString()}';
}
@computed
@ -201,10 +199,8 @@ abstract class BalanceViewModelBase with Store {
return '---';
}
return _getFiatBalance(
price: price,
cryptoAmount: walletBalance.formattedAvailableBalance) + ' ' + fiatCurrency.toString();
return _getFiatBalance(price: price, cryptoAmount: walletBalance.formattedAvailableBalance) +
' ${fiatCurrency.toString()}';
}
@computed
@ -216,10 +212,8 @@ abstract class BalanceViewModelBase with Store {
return '---';
}
return _getFiatBalance(
price: price,
cryptoAmount: walletBalance.formattedAdditionalBalance) + ' ' + fiatCurrency.toString();
return _getFiatBalance(price: price, cryptoAmount: walletBalance.formattedAdditionalBalance) +
' ${fiatCurrency.toString()}';
}
@computed
@ -398,6 +392,6 @@ abstract class BalanceViewModelBase with Store {
}
}
String getFormattedFrozenBalance(Balance walletBalance) => walletBalance.formattedFrozenBalance;
String getFormattedFrozenBalance(Balance walletBalance) => walletBalance.formattedUnAvailableBalance;
}

View file

@ -146,7 +146,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
ChangeNowExchangeProvider(settingsStore: _settingsStore),
SideShiftExchangeProvider(),
SimpleSwapExchangeProvider(),
TrocadorExchangeProvider(useTorOnly: _useTorOnly),
TrocadorExchangeProvider(useTorOnly: _useTorOnly,
providerStates: _settingsStore.trocadorProviderStates),
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
];

View file

@ -0,0 +1,21 @@
import 'package:cake_wallet/store/settings_store.dart';
import 'package:mobx/mobx.dart';
part 'trocador_providers_view_model.g.dart';
class TrocadorProvidersViewModel = TrocadorProvidersViewModelBase with _$TrocadorProvidersViewModel;
abstract class TrocadorProvidersViewModelBase with Store {
TrocadorProvidersViewModelBase(this._settingsStore);
final SettingsStore _settingsStore;
@computed
Map<String, bool> get providerStates => _settingsStore.trocadorProviderStates;
@action
void toggleProviderState(String providerName) {
final currentState = providerStates[providerName] ?? false;
_settingsStore.saveTrocadorProviderState(providerName, !currentState);
}
}

View file

@ -12,6 +12,7 @@ abstract class UnspentCoinsItemBase with Store {
required this.isFrozen,
required this.note,
required this.isSending,
required this.isChange,
required this.amountRaw,
required this.vout,
required this.keyImage
@ -35,6 +36,9 @@ abstract class UnspentCoinsItemBase with Store {
@observable
bool isSending;
@observable
bool isChange;
@observable
int amountRaw;

View file

@ -1,10 +1,8 @@
import 'package:collection/collection.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cw_core/unspent_transaction_output.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/unspent_transaction_output.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
@ -26,66 +24,49 @@ abstract class UnspentCoinsListViewModelBase with Store {
@computed
ObservableList<UnspentCoinsItem> get items => ObservableList.of(_getUnspents().map((elem) {
final amount = formatAmountToString(elem.value) + ' ${wallet.currency.title}';
final info =
getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage);
return UnspentCoinsItem(
address: elem.address,
amount: amount,
amount: '${formatAmountToString(elem.value)} ${wallet.currency.title}',
hash: elem.hash,
isFrozen: info?.isFrozen ?? false,
note: info?.note ?? '',
isSending: info?.isSending ?? true,
isFrozen: info.isFrozen,
note: info.note,
isSending: info.isSending,
amountRaw: elem.value,
vout: elem.vout,
keyImage: elem.keyImage);
keyImage: elem.keyImage,
isChange: elem.isChange,
);
}));
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
try {
final info =
getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout, item.keyImage);
if (info == null) {
final newInfo = UnspentCoinsInfo(
walletId: wallet.id,
hash: item.hash,
address: item.address,
value: item.amountRaw,
vout: item.vout,
isFrozen: item.isFrozen,
isSending: item.isSending,
noteRaw: item.note,
keyImage: item.keyImage);
await _unspentCoinsInfo.add(newInfo);
_updateUnspents();
wallet.updateBalance();
return;
}
info.isFrozen = item.isFrozen;
info.isSending = item.isSending;
info.note = item.note;
await info.save();
_updateUnspents();
wallet.updateBalance();
await _updateUnspents();
await wallet.updateBalance();
} catch (e) {
print(e.toString());
}
}
UnspentCoinsInfo? getUnspentCoinInfo(
String hash, String address, int value, int vout, String? keyImage) {
return _unspentCoinsInfo.values.firstWhereOrNull((element) =>
element.walletId == wallet.id &&
element.hash == hash &&
element.address == address &&
element.value == value &&
element.vout == vout &&
element.keyImage == keyImage);
}
UnspentCoinsInfo getUnspentCoinInfo(
String hash, String address, int value, int vout, String? keyImage) =>
_unspentCoinsInfo.values.firstWhere((element) =>
element.walletId == wallet.id &&
element.hash == hash &&
element.address == address &&
element.value == value &&
element.vout == vout &&
element.keyImage == keyImage);
String formatAmountToString(int fullBalance) {
if (wallet.type == WalletType.monero)
@ -95,7 +76,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
return '';
}
void _updateUnspents() {
Future<void> _updateUnspents() async {
if (wallet.type == WalletType.monero) return monero!.updateUnspents(wallet);
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type))
return bitcoin!.updateUnspents(wallet);

View file

@ -8,6 +8,7 @@
#include <cw_monero/cw_monero_plugin.h>
#include <devicelocale/devicelocale_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
@ -17,6 +18,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) devicelocale_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DevicelocalePlugin");
devicelocale_plugin_register_with_registrar(devicelocale_registrar);
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

View file

@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
cw_monero
devicelocale
flutter_secure_storage_linux
url_launcher_linux
)

View file

@ -8,6 +8,7 @@ import Foundation
import cw_monero
import device_info_plus
import devicelocale
import flutter_secure_storage_macos
import in_app_review
import package_info_plus
import path_provider_foundation
@ -20,6 +21,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))

View file

@ -30,13 +30,11 @@ PODS:
- FlutterMacOS
- package_info (0.0.1):
- FlutterMacOS
- package_info_plus (0.0.1):
- FlutterMacOS
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- platform_device_id (0.0.1):
- FlutterMacOS
- platform_device_id_macos (0.0.1):
- FlutterMacOS
- ReachabilitySwift (5.0.0)
- share_plus_macos (0.0.1):
- FlutterMacOS
@ -45,7 +43,7 @@ PODS:
- FlutterMacOS
- url_launcher_macos (0.0.1):
- FlutterMacOS
- wakelock_macos (0.0.1):
- wakelock_plus (0.0.1):
- FlutterMacOS
DEPENDENCIES:
@ -57,13 +55,12 @@ DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`)
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
- package_info (from `Flutter/ephemeral/.symlinks/plugins/package_info/macos`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- platform_device_id (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos`)
- platform_device_id_macos (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos`)
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
SPEC REPOS:
trunk:
@ -86,20 +83,18 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/in_app_review/macos
package_info:
:path: Flutter/ephemeral/.symlinks/plugins/package_info/macos
package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
platform_device_id:
:path: Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos
platform_device_id_macos:
:path: Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos
share_plus_macos:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
wakelock_macos:
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos
wakelock_plus:
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
SPEC CHECKSUMS:
connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308
@ -110,14 +105,13 @@ SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0
package_info: 6eba2fd8d3371dda2d85c8db6fe97488f24b74b2
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
platform_device_id: 3e414428f45df149bbbfb623e2c0ca27c545b763
platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
PODFILE CHECKSUM: 5107934592df7813b33d744aebc8ddc6b5a5445f

View file

@ -42,7 +42,7 @@ dependencies:
lottie: ^1.3.0
animate_do: ^2.1.0
cupertino_icons: ^1.0.5
encrypt: 5.0.1
encrypt: 5.0.2
crypto: ^3.0.2
# password: ^1.0.0
basic_utils: ^5.6.1
@ -53,7 +53,10 @@ dependencies:
another_flushbar: ^1.12.29
archive: ^3.3.0
cryptography: ^2.0.5
file_picker: ^5.2.5
file_picker:
git:
url: https://github.com/cake-tech/flutter_file_picker.git
ref: master
unorm_dart: ^0.2.0
# check unorm_dart for usage and for replace
permission_handler: ^10.0.0
@ -62,10 +65,9 @@ dependencies:
url: https://github.com/cake-tech/device_display_brightness.git
ref: master
workmanager: ^0.5.1
# platform_device_id: ^1.0.1
wakelock_plus: ^1.1.1
wakelock_plus: ^1.1.3
flutter_mailer: ^2.0.2
device_info_plus: 8.1.0
device_info_plus: ^9.1.0
base32: 2.1.3
in_app_review: ^2.0.6
cake_backup:
@ -74,7 +76,7 @@ dependencies:
ref: main
version: 1.0.0
flutter_plugin_android_lifecycle: 2.0.9
path_provider_android: 2.0.24
path_provider_android: ^2.2.1
shared_preferences_android: 2.0.17
url_launcher_android: 6.0.24
sensitive_clipboard: ^1.0.0
@ -87,6 +89,11 @@ dependencies:
bitcoin_flutter:
path: /home/rafael/Storage/Repositories/bitcoin_flutter
fluttertoast: 8.1.4
tor:
git:
url: https://github.com/cake-tech/tor.git
ref: main
socks5_proxy: ^1.0.4
dev_dependencies:
flutter_test:

View file

@ -591,7 +591,6 @@
"sweeping_wallet_alert": "لن يستغرق هذا وقتًا طويلاً. لا تترك هذه الشاشة وإلا فقد يتم فقد أموال سويبت",
"decimal_places_error": "عدد كبير جدًا من المنازل العشرية",
"edit_node": "تحرير العقدة",
"frozen_balance": "الرصيد المجمد",
"invoice_details": "تفاصيل الفاتورة",
"donation_link_details": "تفاصيل رابط التبرع",
"anonpay_description": "توليد ${type}. يمكن للمستلم ${method} بأي عملة مشفرة مدعومة ، وستتلقى أموالاً في هذه",
@ -727,6 +726,7 @@
"enterWalletConnectURI": "WalletConnect ـﻟ URI ﻞﺧﺩﺃ",
"seed_key": "مفتاح البذور",
"enter_seed_phrase": "أدخل عبارة البذور الخاصة بك",
"change_rep_successful": "تم تغيير ممثل بنجاح",
"add_contact": "ﻝﺎﺼﺗﺍ ﺔﻬﺟ ﺔﻓﺎﺿﺇ",
"exchange_provider_unsupported": "${providerName} لم يعد مدعومًا!",
"domain_looks_up": "ﻝﺎﺠﻤﻟﺍ ﺚﺤﺑ ﺕﺎﻴﻠﻤﻋ",
@ -736,5 +736,10 @@
"use_testnet": "استخدم testnet",
"address_and_silent_addresses": "العنوان والعناوين الصامتة",
"silent_addresses": "عناوين صامتة",
"Block_remaining": "كتلة ${status} المتبقية"
}
"Block_remaining": "كتلة ${status} المتبقية",
"seed_phrase_length": " ﺭﻭﺬﺒﻟﺍ ﺓﺭﺎﺒﻌﻟﺍ ﻝﻮﻃ",
"unavailable_balance": " ﺮﻓﻮﺘﻣ ﺮﻴﻏ ﺪﻴﺻﺭ",
"unavailable_balance_description": ".ﺎﻫﺪﻴﻤﺠﺗ ءﺎﻐﻟﺇ ﺭﺮﻘﺗ ﻰﺘﺣ ﺕﻼﻣﺎﻌﻤﻠﻟ ﻝﻮﺻﻮﻠﻟ ﺔﻠﺑﺎﻗ ﺮﻴﻏ ﺓﺪﻤﺠﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﻞﻈﺗ ﺎﻤﻨﻴﺑ ،ﺎﻬﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻣﺎﻌﻤﻟﺍ ﻝﺎﻤﺘﻛﺍ ﺩﺮﺠﻤﺑ ﺔﺣﺎﺘﻣ ﺔﻠﻔﻘﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﺢﺒﺼﺘﺳ .ﻚﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻤﻌﻟﺍ ﻲﻓ ﻢﻜﺤﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻂﺸﻧ ﻞﻜﺸﺑ ﺎﻫﺪﻴﻤﺠﺘﺑ ﺖﻤﻗ",
"unspent_change": "يتغير",
"tor_connection": " ﺭﻮﺗ ﻝﺎﺼﺗﺍ"
}

View file

@ -588,7 +588,6 @@
"error_dialog_content": "Получихме грешка.\n\nМоля, изпратете доклада до нашия отдел поддръжка, за да подобрим приложението.",
"decimal_places_error": "Твърде много знаци след десетичната запетая",
"edit_node": "Редактиране на възел",
"frozen_balance": "Замразен баланс",
"invoice_details": "IДанни за фактура",
"donation_link_details": "Подробности за връзката за дарение",
"anonpay_description": "Генерирайте ${type}. Получателят може да ${method} с всяка поддържана криптовалута и вие ще получите средства в този портфейл.",
@ -723,6 +722,7 @@
"enterWalletConnectURI": "Въведете URI на WalletConnect",
"seed_key": "Ключ за семена",
"enter_seed_phrase": "Въведете вашата фраза за семена",
"change_rep_successful": "Успешно промени представител",
"add_contact": "Добави контакт",
"exchange_provider_unsupported": "${providerName} вече не се поддържа!",
"domain_looks_up": "Търсене на домейни",
@ -732,5 +732,10 @@
"use_testnet": "Използвайте TestNet",
"address_and_silent_addresses": "Адрес и мълчаливи адреси",
"silent_addresses": "Безшумни адреси",
"Block_remaining": "${status} останал блок"
}
"Block_remaining": "${status} останал блок",
"seed_phrase_length": "Дължина на началната фраза",
"unavailable_balance": "Неналично салдо",
"unavailable_balance_description": "Неналично салдо: Тази обща сума включва средства, които са заключени в чакащи транзакции и тези, които сте замразили активно в настройките за контрол на монетите. Заключените баланси ще станат достъпни, след като съответните им транзакции бъдат завършени, докато замразените баланси остават недостъпни за транзакции, докато не решите да ги размразите.",
"unspent_change": "Промяна",
"tor_connection": "Tor връзка"
}

View file

@ -588,7 +588,6 @@
"error_dialog_content": "Nastala chyba.\n\nProsím odešlete zprávu o chybě naší podpoře, aby mohli zajistit opravu.",
"decimal_places_error": "Příliš mnoho desetinných míst",
"edit_node": "Upravit uzel",
"frozen_balance": "Zmrazená bilance",
"invoice_details": "detaily faktury",
"donation_link_details": "Podrobnosti odkazu na darování",
"anonpay_description": "Vygenerujte ${type}. Příjemce může ${method} s jakoukoli podporovanou kryptoměnou a vy obdržíte prostředky v této peněžence.",
@ -723,6 +722,7 @@
"enterWalletConnectURI": "Zadejte identifikátor URI WalletConnect",
"seed_key": "Klíč semen",
"enter_seed_phrase": "Zadejte svou frázi semen",
"change_rep_successful": "Úspěšně změnil zástupce",
"add_contact": "Přidat kontakt",
"exchange_provider_unsupported": "${providerName} již není podporováno!",
"domain_looks_up": "Vyhledávání domén",
@ -732,5 +732,10 @@
"use_testnet": "Použijte testNet",
"address_and_silent_addresses": "Adresa a tiché adresy",
"silent_addresses": "Tiché adresy",
"Block_remaining": "${status} Blok zbývající"
}
"Block_remaining": "${status} Blok zbývající",
"seed_phrase_length": "Délka fráze semene",
"unavailable_balance": "Nedostupný zůstatek",
"unavailable_balance_description": "Nedostupný zůstatek: Tento součet zahrnuje prostředky, které jsou uzamčeny v nevyřízených transakcích a ty, které jste aktivně zmrazili v nastavení kontroly mincí. Uzamčené zůstatky budou k dispozici po dokončení příslušných transakcí, zatímco zmrazené zůstatky zůstanou pro transakce nepřístupné, dokud se nerozhodnete je uvolnit.",
"unspent_change": "Změna",
"tor_connection": "Připojení Tor"
}

View file

@ -595,7 +595,6 @@
"sweeping_wallet_alert": "Das sollte nicht lange dauern. VERLASSEN SIE DIESEN BILDSCHIRM NICHT, ANDERNFALLS KÖNNEN DIE GELDER VERLOREN GEHEN",
"decimal_places_error": "Zu viele Nachkommastellen",
"edit_node": "Knoten bearbeiten",
"frozen_balance": "Gefrorenes Guthaben",
"invoice_details": "Rechnungs-Details",
"donation_link_details": "Details zum Spendenlink",
"anonpay_description": "Generieren Sie ${type}. Der Empfänger kann ${method} mit jeder unterstützten Kryptowährung verwenden, und Sie erhalten Geld in dieser Wallet.",
@ -731,6 +730,7 @@
"enterWalletConnectURI": "Geben Sie den WalletConnect-URI ein",
"seed_key": "Samenschlüssel",
"enter_seed_phrase": "Geben Sie Ihre Samenphrase ein",
"change_rep_successful": "Erfolgreich veränderte Vertreter",
"add_contact": "Kontakt hinzufügen",
"exchange_provider_unsupported": "${providerName} wird nicht mehr unterstützt!",
"domain_looks_up": "Domain-Suchen",
@ -740,5 +740,10 @@
"use_testnet": "TESTNET verwenden",
"address_and_silent_addresses": "Adresse und stille Adressen",
"silent_addresses": "Stille Adressen",
"Block_remaining": "${status} Block verbleibend"
}
"Block_remaining": "${status} Block verbleibend",
"seed_phrase_length": "Länge der Seed-Phrase",
"unavailable_balance": "Nicht verfügbares Guthaben",
"unavailable_balance_description": "Nicht verfügbares Guthaben: Diese Summe umfasst Gelder, die in ausstehenden Transaktionen gesperrt sind, und solche, die Sie in Ihren Münzkontrolleinstellungen aktiv eingefroren haben. Gesperrte Guthaben werden verfügbar, sobald die entsprechenden Transaktionen abgeschlossen sind, während eingefrorene Guthaben für Transaktionen nicht zugänglich bleiben, bis Sie sich dazu entschließen, sie wieder freizugeben.",
"unspent_change": "Wechselgeld",
"tor_connection": "Tor-Verbindung"
}

View file

@ -607,7 +607,6 @@
"onion_link": "Onion link",
"decimal_places_error": "Too many decimal places",
"edit_node": "Edit Node",
"frozen_balance": "Frozen Balance",
"settings": "Settings",
"sell_monero_com_alert_content": "Selling Monero is not supported yet",
"error_text_input_below_minimum_limit": "Amount is less than the minimum",
@ -733,6 +732,7 @@
"enterWalletConnectURI": "Enter WalletConnect URI",
"seed_key": "Seed key",
"enter_seed_phrase": "Enter your seed phrase",
"change_rep_successful": "Successfully changed representative",
"add_contact": "Add contact",
"exchange_provider_unsupported": "${providerName} is no longer supported!",
"domain_looks_up": "Domain lookups",
@ -741,5 +741,10 @@
"switchToETHWallet": "Please switch to an Ethereum wallet and try again",
"use_testnet": "Use testnet",
"address_and_silent_addresses": "Address and Silent Addresses",
"silent_addresses": "Silent Addresses"
"silent_addresses": "Silent Addresses",
"seed_phrase_length": "Seed phrase length",
"unavailable_balance": "Unavailable balance",
"unavailable_balance_description": "Unavailable Balance: This total includes funds that are locked in pending transactions and those you have actively frozen in your coin control settings. Locked balances will become available once their respective transactions are completed, while frozen balances remain inaccessible for transactions until you decide to unfreeze them.",
"unspent_change": "Change",
"tor_connection": "Tor connection"
}

View file

@ -595,7 +595,6 @@
"sweeping_wallet_alert": "Esto no debería llevar mucho tiempo. NO DEJES ESTA PANTALLA O SE PUEDEN PERDER LOS FONDOS BARRIDOS",
"decimal_places_error": "Demasiados lugares decimales",
"edit_node": "Editar nodo",
"frozen_balance": "Balance congelado",
"invoice_details": "Detalles de la factura",
"donation_link_details": "Detalles del enlace de donación",
"anonpay_description": "Genera ${type}. El destinatario puede ${method} con cualquier criptomoneda admitida, y recibirá fondos en esta billetera.",
@ -731,6 +730,7 @@
"enterWalletConnectURI": "Ingrese el URI de WalletConnect",
"seed_key": "Llave de semilla",
"enter_seed_phrase": "Ingrese su frase de semillas",
"change_rep_successful": "Representante cambiado con éxito",
"add_contact": "Agregar contacto",
"exchange_provider_unsupported": "¡${providerName} ya no es compatible!",
"domain_looks_up": "Búsquedas de dominio",
@ -740,5 +740,10 @@
"use_testnet": "Use TestNet",
"address_and_silent_addresses": "Dirección y direcciones silenciosas",
"silent_addresses": "Direcciones silenciosas",
"Block_remaining": "${status} bloque restante"
}
"Block_remaining": "${status} bloque restante",
"seed_phrase_length": "Longitud de la frase inicial",
"unavailable_balance": "Saldo no disponible",
"unavailable_balance_description": "Saldo no disponible: este total incluye fondos que están bloqueados en transacciones pendientes y aquellos que usted ha congelado activamente en su configuración de control de monedas. Los saldos bloqueados estarán disponibles una vez que se completen sus respectivas transacciones, mientras que los saldos congelados permanecerán inaccesibles para las transacciones hasta que usted decida descongelarlos.",
"unspent_change": "Cambiar",
"tor_connection": "conexión tor"
}

View file

@ -595,7 +595,6 @@
"sweeping_wallet_alert": "Cette opération ne devrait pas prendre longtemps. NE QUITTEZ PAS CET ÉCRAN OU LES FONDS CONSOLIDÉS POURRAIENT ÊTRE PERDUS",
"decimal_places_error": "Trop de décimales",
"edit_node": "Modifier le nœud",
"frozen_balance": "Solde gelé",
"invoice_details": "Détails de la facture",
"donation_link_details": "Détails du lien de don",
"anonpay_description": "Générez ${type}. Le destinataire peut ${method} avec n'importe quelle crypto-monnaie prise en charge, et vous recevrez des fonds dans ce portefeuille (wallet).",
@ -726,11 +725,12 @@
"message": "Message",
"do_not_have_enough_gas_asset": "Vous n'avez pas assez de ${currency} pour effectuer une transaction avec les conditions actuelles du réseau blockchain. Vous avez besoin de plus de ${currency} pour payer les frais du réseau blockchain, même si vous envoyez un actif différent.",
"totp_auth_url": "URL D'AUTORISATION TOTP",
"awaitDAppProcessing": "Veuillez attendre que le dApp termine le traitement.",
"copyWalletConnectLink": "Copiez le lien WalletConnect depuis dApp et collez-le ici",
"awaitDAppProcessing": "Veuillez attendre que l'application décentralisée (dApp) termine le traitement.",
"copyWalletConnectLink": "Copiez le lien WalletConnect depuis l'application décentralisée (dApp) et collez-le ici",
"enterWalletConnectURI": "Saisissez l'URI de WalletConnect.",
"seed_key": "Clé de graines",
"enter_seed_phrase": "Entrez votre phrase de semence",
"seed_key": "Clé secrète (seed key)",
"enter_seed_phrase": "Entrez votre phrase secrète (seed)",
"change_rep_successful": "Représentant changé avec succès",
"add_contact": "Ajouter le contact",
"exchange_provider_unsupported": "${providerName} n'est plus pris en charge!",
"domain_looks_up": "Recherches de domaine",
@ -740,5 +740,15 @@
"use_testnet": "Utiliser TestNet",
"address_and_silent_addresses": "Adresse et adresses silencieuses",
"silent_addresses": "Adresses silencieuses",
"Block_remaining": "${status} bloc restant"
}
"Block_remaining": "${status} bloc restant",
"exchange_provider_unsupported": "${providerName} n'est plus pris en charge !",
"domain_looks_up": "Résolution de nom",
"require_for_exchanges_to_external_wallets": "Exiger pour les échanges vers des portefeuilles externes",
"seed_phrase_length": "Longueur de la phrase de départ",
"unavailable_balance": "Solde indisponible",
"unavailable_balance_description": "Solde indisponible : ce total comprend les fonds bloqués dans les transactions en attente et ceux que vous avez activement gelés dans vos paramètres de contrôle des pièces. Les soldes bloqués deviendront disponibles une fois leurs transactions respectives terminées, tandis que les soldes gelés resteront inaccessibles aux transactions jusqu'à ce que vous décidiez de les débloquer.",
"camera_permission_is_required": "L'autorisation d'accès à la caméra est requise.\nVeuillez l'activer depuis les paramètres de l'application.",
"switchToETHWallet": "Veuillez passer à un portefeuille (wallet) Ethereum et réessayer",
"unspent_change": "Changement",
"tor_connection": "Connexion Tor"
}

View file

@ -602,7 +602,6 @@
"onion_link": "Lambar onion",
"decimal_places_error": "Wadannan suna da tsawon harsuna",
"edit_node": "Shirya Node",
"frozen_balance": "Falin kuma maɓallin",
"settings": "Saiti",
"sell_monero_com_alert_content": "Selling Monero bai sami ƙarshen mai bukatar samun ba",
"error_text_input_below_minimum_limit": "Kudin ba a kamai",
@ -709,6 +708,7 @@
"enterWalletConnectURI": "Shigar da WalletConnect URI",
"seed_key": "Maɓallin iri",
"enter_seed_phrase": "Shigar da Sert Sentarku",
"change_rep_successful": "An samu nasarar canzawa wakilin",
"add_contact": "Ƙara lamba",
"exchange_provider_unsupported": "${providerName}",
"domain_looks_up": "Binciken yanki",
@ -718,5 +718,10 @@
"use_testnet": "Amfani da gwaji",
"address_and_silent_addresses": "Adireshin da adreshin shiru",
"silent_addresses": "Adireshin Shiru",
"Block_remaining": "${status} toshe ragowar"
}
"Block_remaining": "${status} toshe ragowar",
"seed_phrase_length": "Tsawon jimlar iri",
"unavailable_balance": "Ma'aunin da ba ya samuwa",
"unavailable_balance_description": "Ma'auni Babu: Wannan jimlar ya haɗa da kuɗi waɗanda ke kulle a cikin ma'amaloli da ke jiran aiki da waɗanda kuka daskare sosai a cikin saitunan sarrafa kuɗin ku. Ma'auni da aka kulle za su kasance da zarar an kammala ma'amalolinsu, yayin da daskararrun ma'auni ba za su iya samun damar yin ciniki ba har sai kun yanke shawarar cire su.",
"unspent_change": "Canza",
"tor_connection": "Tor haɗin gwiwa"
}

View file

@ -595,7 +595,6 @@
"sweeping_wallet_alert": "इसमें अधिक समय नहीं लगना चाहिए। इस स्क्रीन को न छोड़ें या स्वैप्ट फंड खो सकते हैं",
"decimal_places_error": "बहुत अधिक दशमलव स्थान",
"edit_node": "नोड संपादित करें",
"frozen_balance": "जमे हुए संतुलन",
"invoice_details": "चालान विवरण",
"donation_link_details": "दान लिंक विवरण",
"anonpay_description": "${type} उत्पन्न करें। प्राप्तकर्ता किसी भी समर्थित क्रिप्टोकरेंसी के साथ ${method} कर सकता है, और आपको इस वॉलेट में धन प्राप्त होगा।",
@ -731,6 +730,7 @@
"enterWalletConnectURI": "वॉलेटकनेक्ट यूआरआई दर्ज करें",
"seed_key": "बीज कुंजी",
"enter_seed_phrase": "अपना बीज वाक्यांश दर्ज करें",
"change_rep_successful": "सफलतापूर्वक बदलकर प्रतिनिधि",
"add_contact": "संपर्क जोड़ें",
"exchange_provider_unsupported": "${providerName} अब समर्थित नहीं है!",
"domain_looks_up": "डोमेन लुकअप",
@ -740,5 +740,10 @@
"use_testnet": "टेस्टनेट का उपयोग करें",
"address_and_silent_addresses": "पता और मूक पते",
"silent_addresses": "मूक पते",
"Block_remaining": "${status} शेष ब्लॉक"
}
"Block_remaining": "${status} शेष ब्लॉक",
"seed_phrase_length": "बीज वाक्यांश की लंबाई",
"unavailable_balance": "अनुपलब्ध शेष",
"unavailable_balance_description": "अनुपलब्ध शेष राशि: इस कुल में वे धनराशि शामिल हैं जो लंबित लेनदेन में बंद हैं और जिन्हें आपने अपनी सिक्का नियंत्रण सेटिंग्स में सक्रिय रूप से जमा कर रखा है। लॉक किए गए शेष उनके संबंधित लेन-देन पूरे होने के बाद उपलब्ध हो जाएंगे, जबकि जमे हुए शेष लेन-देन के लिए अप्राप्य रहेंगे जब तक कि आप उन्हें अनफ्रीज करने का निर्णय नहीं लेते।",
"unspent_change": "परिवर्तन",
"tor_connection": "टोर कनेक्शन"
}

View file

@ -595,7 +595,6 @@
"sweeping_wallet_alert": "Ovo ne bi trebalo dugo trajati. NE NAPUŠTAJTE OVAJ ZASLON INAČE SE POBREŠENA SREDSTVA MOGU IZGUBITI",
"decimal_places_error": "Previše decimalnih mjesta",
"edit_node": "Uredi čvor",
"frozen_balance": "Zamrznuti saldo",
"invoice_details": "Podaci o fakturi",
"donation_link_details": "Detalji veza za donacije",
"anonpay_description": "Generiraj ${type}. Primatelj može ${method} s bilo kojom podržanom kriptovalutom, a vi ćete primiti sredstva u ovaj novčanik.",
@ -729,6 +728,7 @@
"enterWalletConnectURI": "Unesite WalletConnect URI",
"seed_key": "Sjemenski ključ",
"enter_seed_phrase": "Unesite svoju sjemensku frazu",
"change_rep_successful": "Uspješno promijenjena reprezentativna",
"add_contact": "Dodaj kontakt",
"exchange_provider_unsupported": "${providerName} više nije podržan!",
"domain_looks_up": "Pretraga domena",
@ -738,5 +738,10 @@
"use_testnet": "Koristite TestNet",
"address_and_silent_addresses": "Adresa i tihe adrese",
"silent_addresses": "Tihe adrese",
"Block_remaining": "${status} blok preostaje"
}
"Block_remaining": "${status} blok preostaje",
"seed_phrase_length": "Duljina početne fraze",
"unavailable_balance": "Nedostupno stanje",
"unavailable_balance_description": "Nedostupno stanje: Ovaj ukupni iznos uključuje sredstva koja su zaključana u transakcijama na čekanju i ona koja ste aktivno zamrznuli u postavkama kontrole novčića. Zaključani saldi postat će dostupni kada se dovrše njihove transakcije, dok zamrznuti saldi ostaju nedostupni za transakcije sve dok ih ne odlučite odmrznuti.",
"unspent_change": "Promijeniti",
"tor_connection": "Tor veza"
}

View file

@ -584,7 +584,6 @@
"contact_list_wallets": "Dompet Saya",
"decimal_places_error": "Terlalu banyak tempat desimal",
"edit_node": "Sunting Node",
"frozen_balance": "Saldo Beku",
"invoice_details": "Detail faktur",
"donation_link_details": "Detail tautan donasi",
"anonpay_description": "Hasilkan ${type}. Penerima dapat ${method} dengan cryptocurrency apa pun yang didukung, dan Anda akan menerima dana di dompet ini.",
@ -719,6 +718,7 @@
"enterWalletConnectURI": "Masukkan URI WalletConnect",
"seed_key": "Kunci benih",
"enter_seed_phrase": "Masukkan frasa benih Anda",
"change_rep_successful": "Berhasil mengubah perwakilan",
"add_contact": "Tambah kontak",
"exchange_provider_unsupported": "${providerName} tidak lagi didukung!",
"domain_looks_up": "Pencarian domain",
@ -728,5 +728,10 @@
"use_testnet": "Gunakan TestNet",
"address_and_silent_addresses": "Alamat dan alamat diam",
"silent_addresses": "Alamat diam",
"Block_remaining": "${status} blok tersisa"
}
"Block_remaining": "${status} blok tersisa",
"seed_phrase_length": "Panjang frase benih",
"unavailable_balance": "Saldo tidak tersedia",
"unavailable_balance_description": "Saldo Tidak Tersedia: Total ini termasuk dana yang terkunci dalam transaksi yang tertunda dan dana yang telah Anda bekukan secara aktif di pengaturan kontrol koin Anda. Saldo yang terkunci akan tersedia setelah transaksi masing-masing selesai, sedangkan saldo yang dibekukan tetap tidak dapat diakses untuk transaksi sampai Anda memutuskan untuk mencairkannya.",
"unspent_change": "Mengubah",
"tor_connection": "koneksi Tor"
}

View file

@ -595,7 +595,6 @@
"sweeping_wallet_alert": "Questo non dovrebbe richiedere molto tempo. NON LASCIARE QUESTA SCHERMATA O I FONDI SPAZZATI POTREBBERO ANDARE PERSI",
"decimal_places_error": "Troppe cifre decimali",
"edit_node": "Modifica nodo",
"frozen_balance": "Equilibrio congelato",
"invoice_details": "Dettagli della fattura",
"donation_link_details": "Dettagli del collegamento alla donazione",
"anonpay_description": "Genera ${type}. Il destinatario può ${method} con qualsiasi criptovaluta supportata e riceverai fondi in questo portafoglio.",
@ -731,6 +730,7 @@
"enterWalletConnectURI": "Inserisci l'URI di WalletConnect",
"seed_key": "Chiave di semi",
"enter_seed_phrase": "Inserisci la tua frase di semi",
"change_rep_successful": "Rappresentante modificato con successo",
"add_contact": "Aggiungi contatto",
"exchange_provider_unsupported": "${providerName} non è più supportato!",
"domain_looks_up": "Ricerche di domini",
@ -740,5 +740,10 @@
"use_testnet": "Usa TestNet",
"address_and_silent_addresses": "Indirizzo e indirizzi silenziosi",
"silent_addresses": "Indirizzi silenziosi",
"Block_remaining": "${status} blocco rimanente"
}
"Block_remaining": "${status} blocco rimanente",
"seed_phrase_length": "Lunghezza della frase seed",
"unavailable_balance": "Saldo non disponibile",
"unavailable_balance_description": "Saldo non disponibile: questo totale include i fondi bloccati nelle transazioni in sospeso e quelli che hai congelato attivamente nelle impostazioni di controllo delle monete. I saldi bloccati diventeranno disponibili una volta completate le rispettive transazioni, mentre i saldi congelati rimarranno inaccessibili per le transazioni finché non deciderai di sbloccarli.",
"unspent_change": "Modifica",
"tor_connection": "Connessione Tor"
}

View file

@ -595,7 +595,6 @@
"sweeping_wallet_alert": "これには時間がかかりません。この画面から離れないでください。そうしないと、スイープ ファンドが失われる可能性があります",
"decimal_places_error": "小数点以下の桁数が多すぎる",
"edit_node": "ノードを編集",
"frozen_balance": "冷凍残高",
"invoice_details": "請求の詳細",
"donation_link_details": "寄付リンクの詳細",
"anonpay_description": "${type} を生成します。受取人はサポートされている任意の暗号通貨で ${method} でき、あなたはこのウォレットで資金を受け取ります。",
@ -731,6 +730,7 @@
"enterWalletConnectURI": "WalletConnect URI を入力してください",
"seed_key": "シードキー",
"enter_seed_phrase": "シードフレーズを入力してください",
"change_rep_successful": "代表者の変更に成功しました",
"add_contact": "連絡先を追加",
"exchange_provider_unsupported": "${providerName}はサポートされなくなりました!",
"domain_looks_up": "ドメイン検索",
@ -740,5 +740,10 @@
"use_testnet": "TestNetを使用します",
"address_and_silent_addresses": "住所とサイレントアドレス",
"silent_addresses": "サイレントアドレス",
"Block_remaining": "${status}ブロックの残り"
}
"Block_remaining": "${status}ブロックの残り",
"seed_phrase_length": "シードフレーズの長さ",
"unavailable_balance": "利用できない残高",
"unavailable_balance_description": "利用不可能な残高: この合計には、保留中のトランザクションにロックされている資金と、コイン管理設定でアクティブに凍結した資金が含まれます。ロックされた残高は、それぞれの取引が完了すると利用可能になりますが、凍結された残高は、凍結を解除するまで取引にアクセスできません。",
"unspent_change": "変化",
"tor_connection": "Tor接続"
}

View file

@ -595,7 +595,6 @@
"sweeping_wallet_alert": "오래 걸리지 않습니다. 이 화면을 떠나지 마십시오. 그렇지 않으면 스웹트 자금이 손실될 수 있습니다.",
"decimal_places_error": "소수점 이하 자릿수가 너무 많습니다.",
"edit_node": "노드 편집",
"frozen_balance": "얼어붙은 균형",
"invoice_details": "인보이스 세부정보",
"donation_link_details": "기부 링크 세부정보",
"anonpay_description": "${type} 생성. 수신자는 지원되는 모든 암호화폐로 ${method}할 수 있으며 이 지갑에서 자금을 받게 됩니다.",
@ -729,6 +728,7 @@
"enterWalletConnectURI": "WalletConnect URI를 입력하세요.",
"seed_key": "시드 키",
"enter_seed_phrase": "시드 문구를 입력하십시오",
"change_rep_successful": "대리인이 성공적으로 변경되었습니다",
"add_contact": "주소록에 추가",
"exchange_provider_unsupported": "${providerName}은 더 이상 지원되지 않습니다!",
"domain_looks_up": "도메인 조회",
@ -738,5 +738,10 @@
"use_testnet": "TestNet을 사용하십시오",
"address_and_silent_addresses": "주소 및 조용한 주소",
"silent_addresses": "조용한 주소",
"Block_remaining": "${status} 나머지 블록"
}
"Block_remaining": "${status} 나머지 블록",
"seed_phrase_length": "시드 문구 길이",
"unavailable_balance": "사용할 수 없는 잔액",
"unavailable_balance_description": "사용할 수 없는 잔액: 이 총계에는 보류 중인 거래에 잠겨 있는 자금과 코인 관리 설정에서 적극적으로 동결된 자금이 포함됩니다. 잠긴 잔액은 해당 거래가 완료되면 사용할 수 있게 되며, 동결된 잔액은 동결을 해제하기 전까지 거래에 액세스할 수 없습니다.",
"unspent_change": "변화",
"tor_connection": "토르 연결"
}

View file

@ -593,7 +593,6 @@
"sweeping_wallet_alert": "ဒါက ကြာကြာမခံသင့်ပါဘူး။ ဤစခရင်ကို ချန်မထားပါနှင့် သို့မဟုတ် ထုတ်ယူထားသော ရန်ပုံငွေများ ဆုံးရှုံးနိုင်သည်",
"decimal_places_error": "ဒဿမနေရာများ များလွန်းသည်။",
"edit_node": "Node ကို တည်းဖြတ်ပါ။",
"frozen_balance": "ေးခဲမှူ",
"invoice_details": "ပြေစာအသေးစိတ်",
"donation_link_details": "လှူဒါန်းရန်လင့်ခ်အသေးစိတ်",
"anonpay_description": "${type} ကို ဖန်တီးပါ။ လက်ခံသူက ${method} ကို ပံ့ပိုးပေးထားသည့် cryptocurrency တစ်ခုခုဖြင့် လုပ်ဆောင်နိုင်ပြီး၊ သင်သည် ဤပိုက်ဆံအိတ်တွင် ရံပုံငွေများ ရရှိမည်ဖြစ်သည်။",
@ -729,6 +728,7 @@
"enterWalletConnectURI": "WalletConnect URI ကိုရိုက်ထည့်ပါ။",
"seed_key": "မျိုးစေ့သော့",
"enter_seed_phrase": "သင့်ရဲ့မျိုးစေ့စကားစုကိုရိုက်ထည့်ပါ",
"change_rep_successful": "အောင်မြင်စွာကိုယ်စားလှယ်ပြောင်းလဲသွားတယ်",
"add_contact": "အဆက်အသွယ်ထည့်ပါ။",
"exchange_provider_unsupported": "${providerName} မရှိတော့ပါ!",
"domain_looks_up": "ဒိုမိန်းရှာဖွေမှုများ",
@ -738,5 +738,10 @@
"use_testnet": "testnet ကိုသုံးပါ",
"address_and_silent_addresses": "လိပ်စာနှင့်အသံတိတ်လိပ်စာများ",
"silent_addresses": "အသံတိတ်လိပ်စာများ",
"Block_remaining": "ကျန်ရှိသော ${status}"
}
"Block_remaining": "ကျန်ရှိသော ${status}",
"seed_phrase_length": "မျိုးစေ့စာပိုဒ်တိုအရှည်",
"unavailable_balance": "လက်ကျန်ငွေ မရရှိနိုင်ပါ။",
"unavailable_balance_description": "မရရှိနိုင်သော လက်ကျန်ငွေ- ဤစုစုပေါင်းတွင် ဆိုင်းငံ့ထားသော ငွေပေးငွေယူများတွင် သော့ခတ်ထားသော ငွေကြေးများနှင့် သင်၏ coin ထိန်းချုပ်မှုဆက်တင်များတွင် သင် တက်ကြွစွာ အေးခဲထားသော ငွေများ ပါဝင်သည်။ သော့ခတ်ထားသော လက်ကျန်ငွေများကို ၎င်းတို့၏ သက်ဆိုင်ရာ ငွေပေးငွေယူများ ပြီးမြောက်သည်နှင့် တပြိုင်နက် ရရှိနိုင်မည်ဖြစ်ပြီး၊ အေးခဲထားသော လက်ကျန်များကို ၎င်းတို့အား ပြန်ဖြုတ်ရန် သင်ဆုံးဖြတ်သည်အထိ ငွေပေးငွေယူများအတွက် ဆက်လက်၍မရနိုင်ပါ။",
"unspent_change": "ပေြာင်းလဲခြင်း",
"tor_connection": "Tor ချိတ်ဆက်မှု"
}

Some files were not shown because too many files have changed in this diff Show more