Merge remote-tracking branch 'origin/main' into electrum-sp-refactors
25
.gitignore
vendored
|
@ -142,9 +142,28 @@ lib/wownero/wownero.dart
|
||||||
lib/zano/zano.dart
|
lib/zano/zano.dart
|
||||||
lib/decred/decred.dart
|
lib/decred/decred.dart
|
||||||
|
|
||||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png
|
||||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png
|
||||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@3x.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@3x.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@3x.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@3x.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png
|
||||||
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ipad.png
|
||||||
|
|
||||||
ios/Runner/Info.plist
|
ios/Runner/Info.plist
|
||||||
android/app/src/main/res/mipmap-*
|
android/app/src/main/res/mipmap-*
|
||||||
android/app/src/main/res/drawable/ic_launcher.png
|
android/app/src/main/res/drawable/ic_launcher.png
|
||||||
|
|
11
README.md
|
@ -26,10 +26,13 @@ Cake Wallet includes support for several cryptocurrencies, including:
|
||||||
* Ethereum (ETH)
|
* Ethereum (ETH)
|
||||||
* Litecoin (LTC)
|
* Litecoin (LTC)
|
||||||
* Bitcoin Cash (BCH)
|
* Bitcoin Cash (BCH)
|
||||||
* Polygon (Pol)
|
* Polygon (POL)
|
||||||
* Solana (SOL)
|
* Solana (SOL)
|
||||||
|
* Tron (TRX)
|
||||||
* Nano (XNO)
|
* Nano (XNO)
|
||||||
* Haven (XHV)
|
* Zano (ZANO)
|
||||||
|
* Decred (DCR)
|
||||||
|
* Wownero (WOW)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
@ -81,10 +84,6 @@ Cake Wallet includes support for several cryptocurrencies, including:
|
||||||
* Automatically generate new addresses
|
* Automatically generate new addresses
|
||||||
* Specify multiple recipients for batch sending
|
* Specify multiple recipients for batch sending
|
||||||
|
|
||||||
### Haven Specific Features
|
|
||||||
|
|
||||||
* Send, receive, and store XHV and all xAssets like xUSD, xEUR, xAG, etc.
|
|
||||||
|
|
||||||
# Monero.com by Cake Wallet for Android and iOS
|
# Monero.com by Cake Wallet for Android and iOS
|
||||||
|
|
||||||
## Open Source Monero-Only Wallet
|
## Open Source Monero-Only Wallet
|
||||||
|
|
|
@ -107,6 +107,9 @@
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||||
|
android:value="false" />
|
||||||
<provider
|
<provider
|
||||||
android:name="com.pichillilorenzo.flutter_inappwebview_android.InAppWebViewFileProvider"
|
android:name="com.pichillilorenzo.flutter_inappwebview_android.InAppWebViewFileProvider"
|
||||||
android:authorities="${applicationId}.flutter_inappwebview_android.fileprovider"
|
android:authorities="${applicationId}.flutter_inappwebview_android.fileprovider"
|
||||||
|
|
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon-20@2x.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon-20@3x.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon-20~ipad.png
Normal file
After Width: | Height: | Size: 591 B |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon-29.png
Normal file
After Width: | Height: | Size: 1,019 B |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon-29@2x.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.5 KiB |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon-29@3x.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon-29~ipad.png
Normal file
After Width: | Height: | Size: 1,019 B |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon-40@2x.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon-40@3x.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon-40~ipad.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 9.4 KiB |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon@2x.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon@2x~ipad.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon@3x.png
Normal file
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 201 KiB |
BIN
assets/images/ios_icons/cakewallet_ios_icons/AppIcon~ipad.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-20@2x.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-20@2x~ipad.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-20@3x.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-20~ipad.png
Normal file
After Width: | Height: | Size: 688 B |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-29.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-29@2x.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-29@2x~ipad.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-29@3x.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-29~ipad.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-40@2x.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-40@2x~ipad.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-40@3x.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-40~ipad.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-60@2x~car.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon-60@3x~car.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 6.5 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon@2x.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon@2x~ipad.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon@3x.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 68 KiB |
BIN
assets/images/ios_icons/monero_ios_icons/AppIcon~ipad.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 206 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7 KiB |
Before Width: | Height: | Size: 678 B After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.1 KiB |
|
@ -13,9 +13,3 @@
|
||||||
-
|
-
|
||||||
uri: nodes.hashvault.pro:18081
|
uri: nodes.hashvault.pro:18081
|
||||||
is_default: false
|
is_default: false
|
||||||
-
|
|
||||||
uri: node.c3pool.com:18081
|
|
||||||
is_default: false
|
|
||||||
-
|
|
||||||
uri: node.community.rino.io:18081
|
|
||||||
is_default: false
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
Add background sync to Monero
|
UI/UX enhancements
|
||||||
Enhance Backup files
|
Performance improvements
|
||||||
Improve app usability and user experience
|
|
||||||
User interface enhancements
|
|
||||||
Bug fixes
|
Bug fixes
|
|
@ -1,8 +1,4 @@
|
||||||
Add background sync to Monero
|
New App Logo
|
||||||
Add Decred Wallet
|
UI/UX enhancements
|
||||||
Remove Haven Wallet
|
Performance improvements
|
||||||
Fix and Improve Solana Wallet
|
|
||||||
Enhance Backup files
|
|
||||||
Improve app usability and user experience
|
|
||||||
User interface enhancements
|
|
||||||
Bug fixes
|
Bug fixes
|
|
@ -26,7 +26,7 @@ modules:
|
||||||
- "cp com.cakewallet.CakeWallet.desktop /app/share/applications"
|
- "cp com.cakewallet.CakeWallet.desktop /app/share/applications"
|
||||||
sources:
|
sources:
|
||||||
- type: dir
|
- type: dir
|
||||||
path: build/linux/current/release
|
path: build/linux/x64/release
|
||||||
- type: file
|
- type: file
|
||||||
path: assets/images/cakewallet_icon_180.png
|
path: assets/images/cakewallet_icon_180.png
|
||||||
- type: file
|
- type: file
|
||||||
|
|
|
@ -17,11 +17,11 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin {
|
||||||
@HiveField(4, defaultValue: true)
|
@HiveField(4, defaultValue: true)
|
||||||
bool _enabled;
|
bool _enabled;
|
||||||
@HiveField(5)
|
@HiveField(5)
|
||||||
final String? iconPath;
|
String? iconPath;
|
||||||
@HiveField(6)
|
@HiveField(6)
|
||||||
final String? tag;
|
final String? tag;
|
||||||
@HiveField(7, defaultValue: false)
|
@HiveField(7, defaultValue: false)
|
||||||
final bool isPotentialScam;
|
bool isPotentialScam;
|
||||||
|
|
||||||
bool get enabled => _enabled;
|
bool get enabled => _enabled;
|
||||||
|
|
||||||
|
|
|
@ -28,4 +28,9 @@ class Unspent with UnspentComparable {
|
||||||
|
|
||||||
bool get isP2wpkh =>
|
bool get isP2wpkh =>
|
||||||
address.startsWith('bc') || address.startsWith('tb') || address.startsWith('ltc');
|
address.startsWith('bc') || address.startsWith('tb') || address.startsWith('ltc');
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'Unspent(address: $address, hash: $hash, value: $value, vout: $vout, keyImage: $keyImage, isSending: $isSending, isFrozen: $isFrozen, isChange: $isChange, note: $note)';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,7 +193,7 @@ String walletTypeToDisplayName(WalletType type) {
|
||||||
case WalletType.banano:
|
case WalletType.banano:
|
||||||
return 'Banano (BAN)';
|
return 'Banano (BAN)';
|
||||||
case WalletType.polygon:
|
case WalletType.polygon:
|
||||||
return 'Polygon (MATIC)';
|
return 'Polygon (POL)';
|
||||||
case WalletType.solana:
|
case WalletType.solana:
|
||||||
return 'Solana (SOL)';
|
return 'Solana (SOL)';
|
||||||
case WalletType.tron:
|
case WalletType.tron:
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:io';
|
||||||
import 'package:cw_core/exceptions.dart';
|
import 'package:cw_core/exceptions.dart';
|
||||||
import 'package:cw_core/transaction_direction.dart';
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_decred/amount_format.dart';
|
||||||
import 'package:cw_decred/pending_transaction.dart';
|
import 'package:cw_decred/pending_transaction.dart';
|
||||||
import 'package:cw_decred/transaction_credentials.dart';
|
import 'package:cw_decred/transaction_credentials.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -122,6 +123,9 @@ abstract class DecredWalletBase extends WalletBase<DecredBalance, DecredTransact
|
||||||
return _pubkey;
|
return _pubkey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String formatCryptoAmount(String amount) => decredAmountToString(amount: int.parse(amount));
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
final getSeed = () async {
|
final getSeed = () async {
|
||||||
if (!watchingOnly) {
|
if (!watchingOnly) {
|
||||||
|
|
|
@ -76,9 +76,13 @@ class EthereumWallet extends EVMChainWallet {
|
||||||
await erc20TokensBox.deleteFromDisk();
|
await erc20TokensBox.deleteFromDisk();
|
||||||
|
|
||||||
// Add all the previous tokens with configs to the new box
|
// Add all the previous tokens with configs to the new box
|
||||||
evmChainErc20TokensBox.addAll(allValues);
|
await evmChainErc20TokensBox.addAll(allValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get getDefaultTokenContractAddresses =>
|
||||||
|
DefaultEthereumErc20Tokens().initialErc20Tokens.map((e) => e.contractAddress).toList();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
EVMChainTransactionInfo getTransactionInfo(
|
EVMChainTransactionInfo getTransactionInfo(
|
||||||
EVMChainTransactionModel transactionModel, String address) {
|
EVMChainTransactionModel transactionModel, String address) {
|
||||||
|
|
|
@ -146,6 +146,8 @@ abstract class EVMChainWalletBase extends WalletBase<
|
||||||
// required WalletInfo walletInfo,
|
// required WalletInfo walletInfo,
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
List<String> get getDefaultTokenContractAddresses;
|
||||||
|
|
||||||
Future<void> initErc20TokensBox();
|
Future<void> initErc20TokensBox();
|
||||||
|
|
||||||
String getTransactionHistoryFileName();
|
String getTransactionHistoryFileName();
|
||||||
|
@ -173,6 +175,9 @@ abstract class EVMChainWalletBase extends WalletBase<
|
||||||
await walletAddresses.init();
|
await walletAddresses.init();
|
||||||
await transactionHistory.init();
|
await transactionHistory.init();
|
||||||
|
|
||||||
|
// check for Already existing scam tokens, cuz users can get scammed twice ¯\_(ツ)_/¯
|
||||||
|
await _checkForExistingScamTokens();
|
||||||
|
|
||||||
if (walletInfo.isHardwareWallet) {
|
if (walletInfo.isHardwareWallet) {
|
||||||
_evmChainPrivateKey = EvmLedgerCredentials(walletInfo.address);
|
_evmChainPrivateKey = EvmLedgerCredentials(walletInfo.address);
|
||||||
walletAddresses.address = walletInfo.address;
|
walletAddresses.address = walletInfo.address;
|
||||||
|
@ -188,6 +193,31 @@ abstract class EVMChainWalletBase extends WalletBase<
|
||||||
await save();
|
await save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _checkForExistingScamTokens() async {
|
||||||
|
final baseCurrencySymbols = CryptoCurrency.all.map((e) => e.title.toUpperCase()).toList();
|
||||||
|
|
||||||
|
for (var token in erc20Currencies) {
|
||||||
|
bool isPotentialScam = false;
|
||||||
|
|
||||||
|
bool isWhitelisted =
|
||||||
|
getDefaultTokenContractAddresses.any((element) => element == token.contractAddress);
|
||||||
|
|
||||||
|
final tokenSymbol = token.title.toUpperCase();
|
||||||
|
|
||||||
|
// check if the token symbol is the same as any of the base currencies symbols (ETH, SOL, POL, TRX, etc):
|
||||||
|
// if it is, then it's probably a scam unless it's in the whitelist
|
||||||
|
if (baseCurrencySymbols.contains(tokenSymbol.trim().toUpperCase()) && !isWhitelisted) {
|
||||||
|
isPotentialScam = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPotentialScam) {
|
||||||
|
token.isPotentialScam = true;
|
||||||
|
token.iconPath = null;
|
||||||
|
await token.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> calculateEstimatedFee(TransactionPriority priority) async {
|
Future<int> calculateEstimatedFee(TransactionPriority priority) async {
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,21 +4,32 @@ import 'package:cw_monero/api/coins_info.dart';
|
||||||
import 'package:monero/monero.dart' as monero;
|
import 'package:monero/monero.dart' as monero;
|
||||||
|
|
||||||
class MoneroUnspent extends Unspent {
|
class MoneroUnspent extends Unspent {
|
||||||
|
static Future<MoneroUnspent> fromUnspent(String address, String hash, String keyImage, int value, bool isFrozen, bool isUnlocked) async {
|
||||||
|
return MoneroUnspent(
|
||||||
|
address: address,
|
||||||
|
hash: hash,
|
||||||
|
keyImage: keyImage,
|
||||||
|
value: value,
|
||||||
|
isFrozen: isFrozen,
|
||||||
|
isUnlocked: isUnlocked);
|
||||||
|
}
|
||||||
|
|
||||||
MoneroUnspent(
|
MoneroUnspent(
|
||||||
String address, String hash, String keyImage, int value, bool isFrozen, this.isUnlocked)
|
{required String address,
|
||||||
|
required String hash,
|
||||||
|
required String keyImage,
|
||||||
|
required int value,
|
||||||
|
required bool isFrozen,
|
||||||
|
required this.isUnlocked})
|
||||||
: super(address, hash, value, 0, keyImage) {
|
: super(address, hash, value, 0, keyImage) {
|
||||||
getCoinByKeyImage(keyImage).then((coinId) {
|
_frozen = isFrozen;
|
||||||
if (coinId == null) return;
|
|
||||||
getCoin(coinId).then((coin) {
|
|
||||||
_frozen = monero.CoinsInfo_frozen(coin);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _frozen = false;
|
bool _frozen = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
set isFrozen(bool freeze) {
|
set isFrozen(bool freeze) {
|
||||||
|
_frozen = freeze;
|
||||||
printV("set isFrozen: $freeze ($keyImage): $freeze");
|
printV("set isFrozen: $freeze ($keyImage): $freeze");
|
||||||
getCoinByKeyImage(keyImage!).then((coinId) async {
|
getCoinByKeyImage(keyImage!).then((coinId) async {
|
||||||
if (coinId == null) return;
|
if (coinId == null) return;
|
||||||
|
|
|
@ -592,7 +592,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, MoneroTransact
|
||||||
final coin = await getCoin(i);
|
final coin = await getCoin(i);
|
||||||
final coinSpent = monero.CoinsInfo_spent(coin);
|
final coinSpent = monero.CoinsInfo_spent(coin);
|
||||||
if (coinSpent == false && monero.CoinsInfo_subaddrAccount(coin) == walletAddresses.account!.id) {
|
if (coinSpent == false && monero.CoinsInfo_subaddrAccount(coin) == walletAddresses.account!.id) {
|
||||||
final unspent = MoneroUnspent(
|
final unspent = await MoneroUnspent.fromUnspent(
|
||||||
monero.CoinsInfo_address(coin),
|
monero.CoinsInfo_address(coin),
|
||||||
monero.CoinsInfo_hash(coin),
|
monero.CoinsInfo_hash(coin),
|
||||||
monero.CoinsInfo_keyImage(coin),
|
monero.CoinsInfo_keyImage(coin),
|
||||||
|
|
|
@ -49,6 +49,10 @@ class PolygonWallet extends EVMChainWallet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get getDefaultTokenContractAddresses =>
|
||||||
|
DefaultPolygonErc20Tokens().initialPolygonErc20Tokens.map((e) => e.contractAddress).toList();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> checkIfScanProviderIsEnabled() async {
|
Future<bool> checkIfScanProviderIsEnabled() async {
|
||||||
bool isPolygonScanEnabled = (await sharedPrefs.future).getBool("use_polygonscan") ?? true;
|
bool isPolygonScanEnabled = (await sharedPrefs.future).getBool("use_polygonscan") ?? true;
|
||||||
|
|
|
@ -1,114 +1,134 @@
|
||||||
{
|
{
|
||||||
"images" : [
|
"images": [
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-40x40@1x.png",
|
"filename": "AppIcon@2x.png",
|
||||||
"idiom" : "iphone",
|
"idiom": "iphone",
|
||||||
"scale" : "2x",
|
"scale": "2x",
|
||||||
"size" : "20x20"
|
"size": "60x60"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-20x20@3x.png",
|
"filename": "AppIcon@3x.png",
|
||||||
"idiom" : "iphone",
|
"idiom": "iphone",
|
||||||
"scale" : "3x",
|
"scale": "3x",
|
||||||
"size" : "20x20"
|
"size": "60x60"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-29x29@2x 1.png",
|
"filename": "AppIcon~ipad.png",
|
||||||
"idiom" : "iphone",
|
"idiom": "ipad",
|
||||||
"scale" : "2x",
|
"scale": "1x",
|
||||||
"size" : "29x29"
|
"size": "76x76"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-29x29@3x.png",
|
"filename": "AppIcon@2x~ipad.png",
|
||||||
"idiom" : "iphone",
|
"idiom": "ipad",
|
||||||
"scale" : "3x",
|
"scale": "2x",
|
||||||
"size" : "29x29"
|
"size": "76x76"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "iphone",
|
"filename": "AppIcon-83.5@2x~ipad.png",
|
||||||
"scale" : "2x",
|
"idiom": "ipad",
|
||||||
"size" : "40x40"
|
"scale": "2x",
|
||||||
|
"size": "83.5x83.5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-40x40@3x.png",
|
"filename": "AppIcon-40@2x.png",
|
||||||
"idiom" : "iphone",
|
"idiom": "iphone",
|
||||||
"scale" : "3x",
|
"scale": "2x",
|
||||||
"size" : "40x40"
|
"size": "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "app_icon_120.png",
|
"filename": "AppIcon-40@3x.png",
|
||||||
"idiom" : "iphone",
|
"idiom": "iphone",
|
||||||
"scale" : "2x",
|
"scale": "3x",
|
||||||
"size" : "60x60"
|
"size": "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "app_icon_180.png",
|
"filename": "AppIcon-40~ipad.png",
|
||||||
"idiom" : "iphone",
|
"idiom": "ipad",
|
||||||
"scale" : "3x",
|
"scale": "1x",
|
||||||
"size" : "60x60"
|
"size": "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-20x20@1x.png",
|
"filename": "AppIcon-40@2x~ipad.png",
|
||||||
"idiom" : "ipad",
|
"idiom": "ipad",
|
||||||
"scale" : "1x",
|
"scale": "2x",
|
||||||
"size" : "20x20"
|
"size": "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
"filename": "AppIcon-20@2x.png",
|
||||||
"idiom" : "ipad",
|
"idiom": "iphone",
|
||||||
"scale" : "2x",
|
"scale": "2x",
|
||||||
"size" : "20x20"
|
"size": "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
"filename": "AppIcon-20@3x.png",
|
||||||
"idiom" : "ipad",
|
"idiom": "iphone",
|
||||||
"scale" : "1x",
|
"scale": "3x",
|
||||||
"size" : "29x29"
|
"size": "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
"filename": "AppIcon-20~ipad.png",
|
||||||
"idiom" : "ipad",
|
"idiom": "ipad",
|
||||||
"scale" : "2x",
|
"scale": "1x",
|
||||||
"size" : "29x29"
|
"size": "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-40x40@1x 1.png",
|
"filename": "AppIcon-20@2x~ipad.png",
|
||||||
"idiom" : "ipad",
|
"idiom": "ipad",
|
||||||
"scale" : "1x",
|
"scale": "2x",
|
||||||
"size" : "40x40"
|
"size": "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "ipad",
|
"filename": "AppIcon-29.png",
|
||||||
"scale" : "2x",
|
"idiom": "iphone",
|
||||||
"size" : "40x40"
|
"scale": "1x",
|
||||||
|
"size": "29x29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-76x76@1x.png",
|
"filename": "AppIcon-29@2x.png",
|
||||||
"idiom" : "ipad",
|
"idiom": "iphone",
|
||||||
"scale" : "1x",
|
"scale": "2x",
|
||||||
"size" : "76x76"
|
"size": "29x29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-76x76@2x.png",
|
"filename": "AppIcon-29@3x.png",
|
||||||
"idiom" : "ipad",
|
"idiom": "iphone",
|
||||||
"scale" : "2x",
|
"scale": "3x",
|
||||||
"size" : "76x76"
|
"size": "29x29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
"filename": "AppIcon-29~ipad.png",
|
||||||
"idiom" : "ipad",
|
"idiom": "ipad",
|
||||||
"scale" : "2x",
|
"scale": "1x",
|
||||||
"size" : "83.5x83.5"
|
"size": "29x29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "app_icon_1024.png",
|
"filename": "AppIcon-29@2x~ipad.png",
|
||||||
"idiom" : "ios-marketing",
|
"idiom": "ipad",
|
||||||
"scale" : "1x",
|
"scale": "2x",
|
||||||
"size" : "1024x1024"
|
"size": "29x29"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "AppIcon-60@2x~car.png",
|
||||||
|
"idiom": "car",
|
||||||
|
"scale": "2x",
|
||||||
|
"size": "60x60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "AppIcon-60@3x~car.png",
|
||||||
|
"idiom": "car",
|
||||||
|
"scale": "3x",
|
||||||
|
"size": "60x60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "AppIcon~ios-marketing.png",
|
||||||
|
"idiom": "ios-marketing",
|
||||||
|
"scale": "1x",
|
||||||
|
"size": "1024x1024"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info": {
|
||||||
"author" : "xcode",
|
"author": "iconkitchen",
|
||||||
"version" : 1
|
"version": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 880 B |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 7 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 10 KiB |
|
@ -6,7 +6,6 @@ import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||||
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
|
||||||
|
|
||||||
enum ProviderType { robinhood, dfx, onramper, moonpay, meld, kriptonim }
|
enum ProviderType { robinhood, dfx, onramper, moonpay, meld, kriptonim }
|
||||||
|
|
||||||
|
@ -47,7 +46,7 @@ extension ProviderTypeName on ProviderType {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProvidersHelper {
|
class ProvidersHelper {
|
||||||
static List<ProviderType> getAvailableBuyProviderTypes(WalletType walletType) => [
|
static List<ProviderType> getAvailableBuyProviderTypes() => [
|
||||||
ProviderType.robinhood,
|
ProviderType.robinhood,
|
||||||
ProviderType.dfx,
|
ProviderType.dfx,
|
||||||
ProviderType.onramper,
|
ProviderType.onramper,
|
||||||
|
@ -55,7 +54,7 @@ class ProvidersHelper {
|
||||||
ProviderType.kriptonim
|
ProviderType.kriptonim
|
||||||
];
|
];
|
||||||
|
|
||||||
static List<ProviderType> getAvailableSellProviderTypes(WalletType walletType) => [
|
static List<ProviderType> getAvailableSellProviderTypes() => [
|
||||||
ProviderType.robinhood,
|
ProviderType.robinhood,
|
||||||
ProviderType.dfx,
|
ProviderType.dfx,
|
||||||
ProviderType.onramper,
|
ProviderType.onramper,
|
||||||
|
@ -63,7 +62,7 @@ class ProvidersHelper {
|
||||||
ProviderType.kriptonim
|
ProviderType.kriptonim
|
||||||
];
|
];
|
||||||
|
|
||||||
static BuyProvider? getProviderByType(ProviderType type) {
|
static BuyProvider getProviderByType(ProviderType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ProviderType.robinhood:
|
case ProviderType.robinhood:
|
||||||
return getIt.get<RobinhoodBuyProvider>();
|
return getIt.get<RobinhoodBuyProvider>();
|
||||||
|
@ -77,8 +76,6 @@ class ProvidersHelper {
|
||||||
return getIt.get<MeldBuyProvider>();
|
return getIt.get<MeldBuyProvider>();
|
||||||
case ProviderType.kriptonim:
|
case ProviderType.kriptonim:
|
||||||
return getIt.get<KryptonimBuyProvider>();
|
return getIt.get<KryptonimBuyProvider>();
|
||||||
default:
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,6 +139,7 @@ class BackupPage extends BasePage {
|
||||||
await backupViewModelBase.saveToDownload(
|
await backupViewModelBase.saveToDownload(
|
||||||
backup.name, backup.file);
|
backup.name, backup.file);
|
||||||
Navigator.of(dialogContext).pop();
|
Navigator.of(dialogContext).pop();
|
||||||
|
await showBar<void>(context, S.of(context).file_saved);
|
||||||
},
|
},
|
||||||
actionLeftButton: () async {
|
actionLeftButton: () async {
|
||||||
Navigator.of(dialogContext).pop();
|
Navigator.of(dialogContext).pop();
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
@ -68,7 +67,9 @@ class EditBackupPasswordPage extends BasePage {
|
||||||
actionRightButton: () async {
|
actionRightButton: () async {
|
||||||
await editBackupPasswordViewModel.save();
|
await editBackupPasswordViewModel.save();
|
||||||
Navigator.of(dialogContext).pop();
|
Navigator.of(dialogContext).pop();
|
||||||
Navigator.of(context).pop();
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
actionLeftButton: () => Navigator.of(dialogContext).pop());
|
actionLeftButton: () => Navigator.of(dialogContext).pop());
|
||||||
});
|
});
|
||||||
|
|
|
@ -162,7 +162,7 @@ class AddressPage extends BasePage {
|
||||||
}
|
}
|
||||||
|
|
||||||
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
|
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
|
||||||
if (bitcoin!.isBitcoinReceivePageOption(option)) {
|
if (dashboardViewModel.type == WalletType.bitcoin && bitcoin!.isBitcoinReceivePageOption(option)) {
|
||||||
addressListViewModel.setAddressType(bitcoin!.getOptionToType(option));
|
addressListViewModel.setAddressType(bitcoin!.getOptionToType(option));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ class NavigationDock extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
|
margin: const EdgeInsets.only(left: 8, right: 8, bottom: 16),
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(50),
|
borderRadius: BorderRadius.circular(50),
|
||||||
child: BackdropFilter(
|
child: BackdropFilter(
|
||||||
|
@ -48,7 +48,7 @@ class NavigationDock extends StatelessWidget {
|
||||||
Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||||
child: IntrinsicHeight(
|
child: IntrinsicHeight(
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
|
|
@ -45,20 +45,17 @@ class TransactionsPage extends StatelessWidget {
|
||||||
Observer(builder: (_) {
|
Observer(builder: (_) {
|
||||||
final status = dashboardViewModel.status;
|
final status = dashboardViewModel.status;
|
||||||
if (status is SyncingSyncStatus) {
|
if (status is SyncingSyncStatus) {
|
||||||
return Padding(
|
return DashBoardRoundedCardWidget(
|
||||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 8),
|
key: ValueKey('transactions_page_syncing_alert_card_key'),
|
||||||
child: DashBoardRoundedCardWidget(
|
onTap: () {
|
||||||
key: ValueKey('transactions_page_syncing_alert_card_key'),
|
try {
|
||||||
onTap: () {
|
final uri = Uri.parse(
|
||||||
try {
|
"https://docs.cakewallet.com/faq/funds-not-appearing");
|
||||||
final uri = Uri.parse(
|
launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
"https://docs.cakewallet.com/faq/funds-not-appearing");
|
} catch (_) {}
|
||||||
launchUrl(uri, mode: LaunchMode.externalApplication);
|
},
|
||||||
} catch (_) {}
|
title: S.of(context).syncing_wallet_alert_title,
|
||||||
},
|
subTitle: S.of(context).syncing_wallet_alert_content,
|
||||||
title: S.of(context).syncing_wallet_alert_title,
|
|
||||||
subTitle: S.of(context).syncing_wallet_alert_content,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Container();
|
return Container();
|
||||||
|
@ -72,7 +69,6 @@ class TransactionsPage extends StatelessWidget {
|
||||||
child: Observer(
|
child: Observer(
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
final items = dashboardViewModel.items;
|
final items = dashboardViewModel.items;
|
||||||
final amount = items.length + 1;
|
|
||||||
return items.isNotEmpty
|
return items.isNotEmpty
|
||||||
? ListView.builder(
|
? ListView.builder(
|
||||||
key: ValueKey('transactions_page_list_view_builder_key'),
|
key: ValueKey('transactions_page_list_view_builder_key'),
|
||||||
|
|
|
@ -47,6 +47,8 @@ class ActionButton extends StatelessWidget {
|
||||||
SizedBox(height: 4),
|
SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.visible,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
color: textColor ??
|
color: textColor ??
|
||||||
|
|
|
@ -182,7 +182,9 @@ class NodeCreateOrEditPage extends BasePage {
|
||||||
|
|
||||||
await nodeCreateOrEditViewModel.save(
|
await nodeCreateOrEditViewModel.save(
|
||||||
editingNode: editingNode, saveAsCurrent: isSelected ?? false);
|
editingNode: editingNode, saveAsCurrent: isSelected ?? false);
|
||||||
Navigator.of(context).pop();
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
text: S.of(context).save,
|
text: S.of(context).save,
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
|
|
|
@ -98,7 +98,7 @@ class SendPage extends BasePage {
|
||||||
return MergeSemantics(
|
return MergeSemantics(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: isMobileView ? 37 : 45,
|
height: isMobileView ? 37 : 45,
|
||||||
width: isMobileView ? 47: 45,
|
width: isMobileView ? 47 : 45,
|
||||||
child: ButtonTheme(
|
child: ButtonTheme(
|
||||||
minWidth: double.minPositive,
|
minWidth: double.minPositive,
|
||||||
child: Semantics(
|
child: Semantics(
|
||||||
|
@ -397,7 +397,6 @@ class SendPage extends BasePage {
|
||||||
return LoadingPrimaryButton(
|
return LoadingPrimaryButton(
|
||||||
key: ValueKey('send_page_send_button_key'),
|
key: ValueKey('send_page_send_button_key'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|
||||||
//Request dummy node to get the focus out of the text fields
|
//Request dummy node to get the focus out of the text fields
|
||||||
FocusScope.of(context).requestFocus(FocusNode());
|
FocusScope.of(context).requestFocus(FocusNode());
|
||||||
|
|
||||||
|
@ -507,7 +506,6 @@ class SendPage extends BasePage {
|
||||||
Navigator.of(loadingBottomSheetContext!).pop();
|
Navigator.of(loadingBottomSheetContext!).pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (state is FailureState) {
|
if (state is FailureState) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
showPopUp<void>(
|
showPopUp<void>(
|
||||||
|
@ -525,7 +523,6 @@ class SendPage extends BasePage {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state is IsExecutingState) {
|
if (state is IsExecutingState) {
|
||||||
|
|
||||||
// wait a bit to avoid showing the loading dialog if transaction is failed
|
// wait a bit to avoid showing the loading dialog if transaction is failed
|
||||||
await Future.delayed(const Duration(milliseconds: 300));
|
await Future.delayed(const Duration(milliseconds: 300));
|
||||||
final currentState = sendViewModel.state;
|
final currentState = sendViewModel.state;
|
||||||
|
@ -584,8 +581,6 @@ class SendPage extends BasePage {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (state is TransactionCommitted) {
|
if (state is TransactionCommitted) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
|
@ -594,7 +589,8 @@ class SendPage extends BasePage {
|
||||||
|
|
||||||
newContactAddress = newContactAddress ?? sendViewModel.newContactAddress();
|
newContactAddress = newContactAddress ?? sendViewModel.newContactAddress();
|
||||||
|
|
||||||
if (newContactAddress?.address != null && isRegularElectrumAddress(newContactAddress!.address)) {
|
if (newContactAddress?.address != null &&
|
||||||
|
isRegularElectrumAddress(newContactAddress!.address)) {
|
||||||
newContactAddress = null;
|
newContactAddress = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,47 +602,53 @@ class SendPage extends BasePage {
|
||||||
builder: (BuildContext bottomSheetContext) {
|
builder: (BuildContext bottomSheetContext) {
|
||||||
return showContactSheet
|
return showContactSheet
|
||||||
? InfoBottomSheet(
|
? InfoBottomSheet(
|
||||||
currentTheme: currentTheme,
|
currentTheme: currentTheme,
|
||||||
showDontAskMeCheckbox: true,
|
showDontAskMeCheckbox: true,
|
||||||
onCheckboxChanged: (value) => sendViewModel.setShowAddressBookPopup(!value),
|
onCheckboxChanged: (value) => sendViewModel.setShowAddressBookPopup(!value),
|
||||||
titleText: S.of(bottomSheetContext).transaction_sent,
|
titleText: S.of(bottomSheetContext).transaction_sent,
|
||||||
contentImage: 'assets/images/contact_icon.svg',
|
contentImage: 'assets/images/contact_icon.svg',
|
||||||
contentImageColor: Theme.of(context)
|
contentImageColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
.extension<CakeTextTheme>()!
|
content: S.of(bottomSheetContext).add_contact_to_address_book,
|
||||||
.titleColor,
|
isTwoAction: true,
|
||||||
content: S.of(bottomSheetContext).add_contact_to_address_book,
|
leftButtonText: 'No',
|
||||||
isTwoAction: true,
|
rightButtonText: 'Yes',
|
||||||
leftButtonText: 'No',
|
actionLeftButton: () {
|
||||||
rightButtonText: 'Yes',
|
Navigator.of(bottomSheetContext).pop();
|
||||||
actionLeftButton: () {
|
if (context.mounted) {
|
||||||
Navigator.of(bottomSheetContext).pop();
|
Navigator.of(context)
|
||||||
Navigator.of(context)
|
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
||||||
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
}
|
||||||
RequestReviewHandler.requestReview();
|
RequestReviewHandler.requestReview();
|
||||||
newContactAddress = null;
|
newContactAddress = null;
|
||||||
},
|
},
|
||||||
actionRightButton: () {
|
actionRightButton: () {
|
||||||
Navigator.of(bottomSheetContext).pop();
|
Navigator.of(bottomSheetContext).pop();
|
||||||
RequestReviewHandler.requestReview();
|
RequestReviewHandler.requestReview();
|
||||||
Navigator.of(context)
|
if (context.mounted) {
|
||||||
.pushNamed(Routes.addressBookAddContact, arguments: newContactAddress);
|
Navigator.of(context).pushNamed(Routes.addressBookAddContact,
|
||||||
newContactAddress = null;
|
arguments: newContactAddress);
|
||||||
},
|
}
|
||||||
)
|
newContactAddress = null;
|
||||||
|
},
|
||||||
|
)
|
||||||
: InfoBottomSheet(
|
: InfoBottomSheet(
|
||||||
currentTheme: currentTheme,
|
currentTheme: currentTheme,
|
||||||
titleText: S.of(bottomSheetContext).transaction_sent,
|
titleText: S.of(bottomSheetContext).transaction_sent,
|
||||||
contentImage: 'assets/images/birthday_cake.svg',
|
contentImage: 'assets/images/birthday_cake.svg',
|
||||||
actionButtonText: S.of(bottomSheetContext).close,
|
actionButtonText: S.of(bottomSheetContext).close,
|
||||||
actionButtonKey: ValueKey('send_page_sent_dialog_ok_button_key'),
|
actionButtonKey: ValueKey('send_page_sent_dialog_ok_button_key'),
|
||||||
actionButton: () {
|
actionButton: () {
|
||||||
Navigator.of(bottomSheetContext).pop();
|
Navigator.of(bottomSheetContext).pop();
|
||||||
Navigator.of(context)
|
Future.delayed(Duration.zero, () {
|
||||||
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
if (context.mounted) {
|
||||||
RequestReviewHandler.requestReview();
|
Navigator.of(context)
|
||||||
newContactAddress = null;
|
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
||||||
},
|
}
|
||||||
);
|
RequestReviewHandler.requestReview();
|
||||||
|
newContactAddress = null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -678,8 +680,7 @@ class SendPage extends BasePage {
|
||||||
currentTheme: currentTheme,
|
currentTheme: currentTheme,
|
||||||
titleText: S.of(bottomSheetContext).proceed_on_device,
|
titleText: S.of(bottomSheetContext).proceed_on_device,
|
||||||
contentImage: 'assets/images/hardware_wallet/ledger_nano_x.png',
|
contentImage: 'assets/images/hardware_wallet/ledger_nano_x.png',
|
||||||
contentImageColor:
|
contentImageColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
|
||||||
content: S.of(bottomSheetContext).proceed_on_device_description,
|
content: S.of(bottomSheetContext).proceed_on_device_description,
|
||||||
isTwoAction: false,
|
isTwoAction: false,
|
||||||
actionButtonText: S.of(context).cancel,
|
actionButtonText: S.of(context).cancel,
|
||||||
|
@ -778,5 +779,4 @@ class SendPage extends BasePage {
|
||||||
|
|
||||||
return isValid;
|
return isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||||
|
sendViewModel.updateSendingBalance();
|
||||||
|
});
|
||||||
|
|
||||||
/// if the current wallet doesn't match the one in the qr code
|
/// if the current wallet doesn't match the one in the qr code
|
||||||
if (initialPaymentRequest != null &&
|
if (initialPaymentRequest != null &&
|
||||||
|
@ -240,38 +243,47 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
currencyValueValidator: output.sendAll
|
currencyValueValidator: output.sendAll
|
||||||
? sendViewModel.allAmountValidator
|
? sendViewModel.allAmountValidator
|
||||||
: sendViewModel.amountValidator,
|
: sendViewModel.amountValidator,
|
||||||
allAmountCallback: () async => output.setSendAll(sendViewModel.sendingBalance)),
|
allAmountCallback: () async => output.setSendAll(await sendViewModel.sendingBalance)),
|
||||||
Divider(
|
Divider(
|
||||||
height: 1,
|
height: 1,
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
|
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
|
||||||
Observer(
|
Observer(
|
||||||
builder: (_) => Padding(
|
builder: (_) {
|
||||||
padding: EdgeInsets.only(top: 10),
|
// force rebuild on mobx
|
||||||
child: Row(
|
final _ = sendViewModel.coinTypeToSpendFrom;
|
||||||
mainAxisSize: MainAxisSize.max,
|
return Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
padding: EdgeInsets.only(top: 10),
|
||||||
children: <Widget>[
|
child: Row(
|
||||||
Expanded(
|
mainAxisSize: MainAxisSize.max,
|
||||||
child: Text(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
S.of(context).available_balance + ':',
|
children: <Widget>[
|
||||||
style: TextStyle(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
S.of(context).available_balance + ':',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color:
|
||||||
|
Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FutureBuilder<String>(
|
||||||
|
future: sendViewModel.sendingBalance,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
return Text(
|
||||||
|
snapshot.data ?? sendViewModel.balance, // default to balance while loading
|
||||||
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color:
|
color:
|
||||||
Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
|
Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
Text(
|
)
|
||||||
sendViewModel.sendingBalance,
|
],
|
||||||
style: TextStyle(
|
),
|
||||||
fontSize: 12,
|
);
|
||||||
fontWeight: FontWeight.w600,
|
},
|
||||||
color:
|
|
||||||
Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (!sendViewModel.isFiatDisabled)
|
if (!sendViewModel.isFiatDisabled)
|
||||||
CurrencyAmountTextField(
|
CurrencyAmountTextField(
|
||||||
|
@ -523,9 +535,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
reaction((_) => sendViewModel.selectedCryptoCurrency, (Currency currency) {
|
reaction((_) => sendViewModel.selectedCryptoCurrency, (Currency currency) async {
|
||||||
if (output.sendAll) {
|
if (output.sendAll) {
|
||||||
output.setSendAll(sendViewModel.sendingBalance);
|
output.setSendAll(await sendViewModel.sendingBalance);
|
||||||
}
|
}
|
||||||
|
|
||||||
output.setCryptoAmount(cryptoAmountController.text);
|
output.setCryptoAmount(cryptoAmountController.text);
|
||||||
|
|
|
@ -101,7 +101,7 @@ class WCCDetailsWidget extends BasePage {
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
backgroundImage: (pairing.peerMetadata!.icons.isNotEmpty
|
backgroundImage: (pairing.peerMetadata!.icons.isNotEmpty
|
||||||
? NetworkImage(pairing.peerMetadata!.icons[0])
|
? NetworkImage(pairing.peerMetadata!.icons[0])
|
||||||
: const AssetImage('assets/images/default_icon.png'))
|
: const AssetImage('assets/images/app_logo.png'))
|
||||||
as ImageProvider<Object>,
|
as ImageProvider<Object>,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -15,7 +15,6 @@ import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
|
@ -61,21 +60,17 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
|
||||||
late Timer bestRateSync;
|
late Timer bestRateSync;
|
||||||
|
|
||||||
List<BuyProvider> get availableBuyProviders {
|
List<BuyProvider> get availableBuyProviders {
|
||||||
final providerTypes = ProvidersHelper.getAvailableBuyProviderTypes(
|
final providerTypes = ProvidersHelper.getAvailableBuyProviderTypes();
|
||||||
walletTypeForCurrency(cryptoCurrency) ?? wallet.type);
|
|
||||||
return providerTypes
|
return providerTypes
|
||||||
.map((type) => ProvidersHelper.getProviderByType(type))
|
.map((type) => ProvidersHelper.getProviderByType(type))
|
||||||
.where((provider) => provider != null)
|
|
||||||
.cast<BuyProvider>()
|
.cast<BuyProvider>()
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BuyProvider> get availableSellProviders {
|
List<BuyProvider> get availableSellProviders {
|
||||||
final providerTypes = ProvidersHelper.getAvailableSellProviderTypes(
|
final providerTypes = ProvidersHelper.getAvailableSellProviderTypes();
|
||||||
walletTypeForCurrency(cryptoCurrency) ?? wallet.type);
|
|
||||||
return providerTypes
|
return providerTypes
|
||||||
.map((type) => ProvidersHelper.getProviderByType(type))
|
.map((type) => ProvidersHelper.getProviderByType(type))
|
||||||
.where((provider) => provider != null)
|
|
||||||
.cast<BuyProvider>()
|
.cast<BuyProvider>()
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,7 +245,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
String get sendingBalance {
|
Future<String> get sendingBalance async {
|
||||||
// only for electrum, monero, wownero, decred wallets atm:
|
// only for electrum, monero, wownero, decred wallets atm:
|
||||||
switch (wallet.type) {
|
switch (wallet.type) {
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
|
@ -255,7 +255,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
case WalletType.wownero:
|
case WalletType.wownero:
|
||||||
case WalletType.decred:
|
case WalletType.decred:
|
||||||
return wallet.formatCryptoAmount(
|
return wallet.formatCryptoAmount(
|
||||||
unspentCoinsListViewModel.getSendingBalance(coinTypeToSpendFrom).toString());
|
(await unspentCoinsListViewModel.getSendingBalance(coinTypeToSpendFrom)).toString());
|
||||||
default:
|
default:
|
||||||
return balance;
|
return balance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,12 +149,15 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
int getSendingBalance(UnspentCoinType overrideCoinTypeToSpendFrom) {
|
Future<int> getSendingBalance(UnspentCoinType overrideCoinTypeToSpendFrom) async {
|
||||||
// return items.where((element) => element.isSending).fold(0, (previousValue, element) => previousValue + element.value);
|
// return items.where((element) => element.isSending).fold(0, (previousValue, element) => previousValue + element.value);
|
||||||
// go through all unspent coins and add up the value minus frozen and non sending:
|
// go through all unspent coins and add up the value minus frozen and non sending:
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
await _updateUnspents();
|
||||||
|
Set<String> seen = {};
|
||||||
for (final item in _getSpecificUnspents(overrideCoinTypeToSpendFrom)) {
|
for (final item in _getSpecificUnspents(overrideCoinTypeToSpendFrom)) {
|
||||||
|
if (seen.contains(item.toString())) continue;
|
||||||
|
seen.add(item.toString());
|
||||||
if (item.isFrozen || !item.isSending) continue;
|
if (item.isFrozen || !item.isSending) continue;
|
||||||
total += item.value;
|
total += item.value;
|
||||||
}
|
}
|
||||||
|
@ -163,8 +166,6 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void _updateUnspentCoinsInfo() {
|
void _updateUnspentCoinsInfo() {
|
||||||
items.clear();
|
|
||||||
|
|
||||||
final unspents = _getUnspents()
|
final unspents = _getUnspents()
|
||||||
.map((elem) {
|
.map((elem) {
|
||||||
try {
|
try {
|
||||||
|
@ -198,7 +199,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
unspents.sort((a, b) => b.value.compareTo(a.value));
|
unspents.sort((a, b) => b.value.compareTo(a.value));
|
||||||
|
items.clear();
|
||||||
items.addAll(unspents);
|
items.addAll(unspents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -333,6 +333,7 @@
|
||||||
"fiat_api": "Fiat API",
|
"fiat_api": "Fiat API",
|
||||||
"fiat_balance": "الرصيد فيات",
|
"fiat_balance": "الرصيد فيات",
|
||||||
"field_required": "هذه الخانة مطلوبه",
|
"field_required": "هذه الخانة مطلوبه",
|
||||||
|
"file_saved": "تم حفظ الملف",
|
||||||
"fill_code": "يرجى ملء رمز التحقق المرسل إلى بريدك الإلكتروني",
|
"fill_code": "يرجى ملء رمز التحقق المرسل إلى بريدك الإلكتروني",
|
||||||
"filter_by": "تصفية حسب",
|
"filter_by": "تصفية حسب",
|
||||||
"first_wallet_text": "محفظة رائعة ل Monero, Bitcoin, Ethereum, Litecoin و Haven",
|
"first_wallet_text": "محفظة رائعة ل Monero, Bitcoin, Ethereum, Litecoin و Haven",
|
||||||
|
|
|
@ -333,6 +333,7 @@
|
||||||
"fiat_api": "Fiat API",
|
"fiat_api": "Fiat API",
|
||||||
"fiat_balance": "Фиат Баланс",
|
"fiat_balance": "Фиат Баланс",
|
||||||
"field_required": "Това поле е задължително",
|
"field_required": "Това поле е задължително",
|
||||||
|
"file_saved": "Запасен файл",
|
||||||
"fill_code": "Моля, въведето кода за потвърждаване, изпратен на Вашия имейл",
|
"fill_code": "Моля, въведето кода за потвърждаване, изпратен на Вашия имейл",
|
||||||
"filter_by": "Филтрирай по",
|
"filter_by": "Филтрирай по",
|
||||||
"first_wallet_text": "Невероятен портфейл за Monero, Bitcoin, Ethereum, Litecoin и Haven",
|
"first_wallet_text": "Невероятен портфейл за Monero, Bitcoin, Ethereum, Litecoin и Haven",
|
||||||
|
|
|
@ -333,6 +333,7 @@
|
||||||
"fiat_api": "Fiat API",
|
"fiat_api": "Fiat API",
|
||||||
"fiat_balance": "Fiat Balance",
|
"fiat_balance": "Fiat Balance",
|
||||||
"field_required": "Toto pole je povinné",
|
"field_required": "Toto pole je povinné",
|
||||||
|
"file_saved": "Uložen soubor",
|
||||||
"fill_code": "Prosím vyplňte ověřovací kód zaslaný na Váš e-mail",
|
"fill_code": "Prosím vyplňte ověřovací kód zaslaný na Váš e-mail",
|
||||||
"filter_by": "Filtrovat podle",
|
"filter_by": "Filtrovat podle",
|
||||||
"first_wallet_text": "Úžasná peněženka pro Monero, Bitcoin, Ethereum, Litecoin a Haven",
|
"first_wallet_text": "Úžasná peněženka pro Monero, Bitcoin, Ethereum, Litecoin a Haven",
|
||||||
|
|
|
@ -333,6 +333,7 @@
|
||||||
"fiat_api": "Fiat API",
|
"fiat_api": "Fiat API",
|
||||||
"fiat_balance": "Fiat Balance",
|
"fiat_balance": "Fiat Balance",
|
||||||
"field_required": "Dieses Feld ist erforderlich",
|
"field_required": "Dieses Feld ist erforderlich",
|
||||||
|
"file_saved": "Datei gespeichert",
|
||||||
"fill_code": "Geben Sie den Bestätigungscode ein, den Sie per E-Mail erhalten haben",
|
"fill_code": "Geben Sie den Bestätigungscode ein, den Sie per E-Mail erhalten haben",
|
||||||
"filter_by": "Filtern nach",
|
"filter_by": "Filtern nach",
|
||||||
"first_wallet_text": "Eine großartige Wallet für Monero, Bitcoin, Ethereum, Litecoin, und Haven",
|
"first_wallet_text": "Eine großartige Wallet für Monero, Bitcoin, Ethereum, Litecoin, und Haven",
|
||||||
|
|
|
@ -333,6 +333,7 @@
|
||||||
"fiat_api": "Fiat API",
|
"fiat_api": "Fiat API",
|
||||||
"fiat_balance": "Fiat Balance",
|
"fiat_balance": "Fiat Balance",
|
||||||
"field_required": "This field is required",
|
"field_required": "This field is required",
|
||||||
|
"file_saved": "File saved",
|
||||||
"fill_code": "Please fill in the verification code provided to your email",
|
"fill_code": "Please fill in the verification code provided to your email",
|
||||||
"filter_by": "Filter by",
|
"filter_by": "Filter by",
|
||||||
"first_wallet_text": "Awesome wallet for Monero, Bitcoin, Ethereum, Litecoin, and Haven",
|
"first_wallet_text": "Awesome wallet for Monero, Bitcoin, Ethereum, Litecoin, and Haven",
|
||||||
|
|
|
@ -333,6 +333,7 @@
|
||||||
"fiat_api": "Fiat API",
|
"fiat_api": "Fiat API",
|
||||||
"fiat_balance": "Balance fiat",
|
"fiat_balance": "Balance fiat",
|
||||||
"field_required": "Este campo es obligatorio",
|
"field_required": "Este campo es obligatorio",
|
||||||
|
"file_saved": "Archivo guardado",
|
||||||
"fill_code": "Por favor completa el código de verificación proporcionado en tu correo electrónico",
|
"fill_code": "Por favor completa el código de verificación proporcionado en tu correo electrónico",
|
||||||
"filter_by": "Filtrado por",
|
"filter_by": "Filtrado por",
|
||||||
"first_wallet_text": "Impresionante billetera para Monero, Bitcoin, Ethereum, Litecoin, y Haven",
|
"first_wallet_text": "Impresionante billetera para Monero, Bitcoin, Ethereum, Litecoin, y Haven",
|
||||||
|
@ -1035,4 +1036,4 @@
|
||||||
"you_will_receive_estimated_amount": "Recibirá(estimado )",
|
"you_will_receive_estimated_amount": "Recibirá(estimado )",
|
||||||
"you_will_send": "Convertir de",
|
"you_will_send": "Convertir de",
|
||||||
"yy": "YY"
|
"yy": "YY"
|
||||||
}
|
}
|
|
@ -333,6 +333,7 @@
|
||||||
"fiat_api": "Fiat API",
|
"fiat_api": "Fiat API",
|
||||||
"fiat_balance": "Solde fiat",
|
"fiat_balance": "Solde fiat",
|
||||||
"field_required": "Ce champ est obligatoire",
|
"field_required": "Ce champ est obligatoire",
|
||||||
|
"file_saved": "Dossier enregistré",
|
||||||
"fill_code": "Veuillez remplir le code de vérification fourni sur votre e-mail",
|
"fill_code": "Veuillez remplir le code de vérification fourni sur votre e-mail",
|
||||||
"filter_by": "Filtrer par",
|
"filter_by": "Filtrer par",
|
||||||
"first_wallet_text": "Super portefeuille (wallet) pour Monero, Bitcoin, Ethereum, Litecoin et Haven",
|
"first_wallet_text": "Super portefeuille (wallet) pour Monero, Bitcoin, Ethereum, Litecoin et Haven",
|
||||||
|
|