Merge remote-tracking branch 'origin/main' into CW-679-Add-Ledger-Litecoin-Support

# Conflicts:
#	cw_bitcoin/lib/electrum_wallet.dart
#	lib/src/screens/send/send_page.dart
This commit is contained in:
Konstantin Ullrich 2024-09-16 17:23:40 +02:00
commit 01718086a7
No known key found for this signature in database
GPG key ID: 6B3199AD9B3D23B8
80 changed files with 2557 additions and 293 deletions

View file

@ -149,6 +149,8 @@ jobs:
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
@ -168,6 +170,10 @@ jobs:
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart
- name: Rename app
run: |

View file

@ -125,6 +125,8 @@ jobs:
echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart
echo "const simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> lib/.secrets.g.dart
echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart
echo "const anypayToken = '${{ secrets.ANY_PAY_TOKEN }}';" >> lib/.secrets.g.dart
echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
@ -154,6 +156,10 @@ jobs:
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart
- name: Rename app
run: |

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M16 1.37854C16 0.764286 16.6636 0.379192 17.1969 0.68395L23 4L29.4961 7.71208C29.8077 7.89012 30 8.22147 30 8.58032V16L23.9923 12.567C23.3774 12.2157 22.6226 12.2157 22.0077 12.567L16 16V8V1.37854ZM2 16V8.58032C2 8.22147 2.19229 7.89012 2.50386 7.71208L8.00772 4.56702C8.62259 4.21566 9.37741 4.21566 9.99228 4.56702L16 8L2 16ZM16 30.6215C16 31.2357 15.3364 31.6208 14.8031 31.3161L9 28L2.50386 24.2879C2.19229 24.1099 2 23.7785 2 23.4197V16L8.00772 19.433C8.62259 19.7843 9.37741 19.7843 9.99228 19.433L16 16V24V30.6215ZM22.0077 27.433C22.6226 27.7843 23.3774 27.7843 23.9923 27.433L29.4961 24.2879C29.8077 24.1099 30 23.7785 30 23.4197V16L16 24L22.0077 27.433Z"
fill="#159DFF"></path>
</svg>

After

Width:  |  Height:  |  Size: 846 B

BIN
assets/images/stealthex.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

@ -17,6 +17,3 @@
-
uri: node.community.rino.io:18081
is_default: false
-
uri: node.moneroworld.com:18089
is_default: false

View file

@ -113,9 +113,13 @@ class ElectrumClient {
},
onDone: () {
unterminatedString = '';
if (host == socket?.address.host) {
socket = null;
_setConnectionStatus(ConnectionStatus.disconnected);
try {
if (host == socket?.address.host) {
socket?.destroy();
_setConnectionStatus(ConnectionStatus.disconnected);
}
} catch(e) {
print(e.toString());
}
},
cancelOnError: true,

View file

@ -1143,6 +1143,8 @@ abstract class ElectrumWalletBase extends WalletBase<
});
}
unspentCoins.removeWhere((utxo) => estimatedTx.utxos.any((e) => e.utxo.txHash == utxo.hash));
await updateBalance();
});
} catch (e) {
@ -2173,7 +2175,7 @@ abstract class ElectrumWalletBase extends WalletBase<
_isTryingToConnect = true;
Timer(Duration(seconds: 10), () {
Timer(Duration(seconds: 5), () {
if (this.syncStatus is NotConnectedSyncStatus ||
this.syncStatus is LostConnectionSyncStatus) {
this.electrumClient.connectToUri(

View file

@ -320,18 +320,18 @@ packages:
dependency: transitive
description:
name: ffigen
sha256: d3e76c2ad48a4e7f93a29a162006f00eba46ce7c08194a77bb5c5e97d1b5ff0a
sha256: "3e12e80ccb6539bb3917217bb6f32709220efb737de0d0fa8736da0b7cb507da"
url: "https://pub.dev"
source: hosted
version: "8.0.2"
version: "12.0.0"
file:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
version: "7.0.0"
fixnum:
dependency: transitive
description:
@ -856,8 +856,8 @@ packages:
description:
path: "."
ref: "sp_v4.0.0"
resolved-ref: "3b8ae38592c0584f53560071dc18bc570758fe13"
url: "https://github.com/rafael-xmr/sp_scanner"
resolved-ref: "9b04f4b0af80dd7dae9274b496a53c23dcc80ea5"
url: "https://github.com/cake-tech/sp_scanner"
source: git
version: "0.0.1"
stack_trace:

View file

@ -32,7 +32,7 @@ dependencies:
ref: cake-update-v2
sp_scanner:
git:
url: https://github.com/rafael-xmr/sp_scanner
url: https://github.com/cake-tech/sp_scanner
ref: sp_v4.0.0
ledger_flutter_plus:
git:

View file

@ -230,10 +230,10 @@ packages:
dependency: "direct main"
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
version: "7.0.0"
fixnum:
dependency: transitive
description:

View file

@ -13,7 +13,7 @@ dependencies:
flutter:
sdk: flutter
http: ^1.1.0
file: ^6.1.4
file: ^7.0.0
path_provider: ^2.0.11
mobx: ^2.0.7+4
flutter_mobx: ^2.0.6+1

View file

@ -237,10 +237,10 @@ packages:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
version: "7.0.0"
fixnum:
dependency: transitive
description:

View file

@ -138,11 +138,17 @@ PendingTransactionDescription createTransactionMultDestSync(
int accountIndex = 0,
List<String> preferredInputs = const []}) {
final dstAddrs = outputs.map((e) => e.address).toList();
final amounts = outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList();
// print("multDest: dstAddrs: $dstAddrs");
// print("multDest: amounts: $amounts");
final txptr = monero.Wallet_createTransactionMultDest(
wptr!,
dstAddr: outputs.map((e) => e.address).toList(),
dstAddr: dstAddrs,
isSweepAll: false,
amounts: outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList(),
amounts: amounts,
mixinCount: 0,
pendingTransactionPriority: priorityRaw,
subaddr_account: accountIndex,
@ -307,7 +313,34 @@ class Transaction {
confirmations = monero.TransactionInfo_confirmations(txInfo),
fee = monero.TransactionInfo_fee(txInfo),
description = monero.TransactionInfo_description(txInfo),
key = monero.Wallet_getTxKey(wptr!, txid: monero.TransactionInfo_hash(txInfo));
key = getTxKey(txInfo);
static String getTxKey(monero.TransactionInfo txInfo) {
final txKey = monero.Wallet_getTxKey(wptr!, txid: monero.TransactionInfo_hash(txInfo));
final status = monero.Wallet_status(wptr!);
if (status != 0) {
return monero.Wallet_errorString(wptr!);
}
return breakTxKey(txKey);
}
static String breakTxKey(String input) {
final x = 64;
StringBuffer buffer = StringBuffer();
for (int i = 0; i < input.length; i += x) {
int endIndex = i + x;
if (endIndex > input.length) {
endIndex = input.length;
}
buffer.write(input.substring(i, endIndex));
if (endIndex != input.length) {
buffer.write('\n\n');
}
}
return buffer.toString().trim();
}
Transaction.dummy({
required this.displayLabel,

View file

@ -123,7 +123,7 @@ void restoreWalletFromKeysSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
final newWptr = spendKey != ""
var newWptr = (spendKey != "")
? monero.WalletManager_createDeterministicWalletFromSpendKey(
wmPtr,
path: path,
@ -149,6 +149,32 @@ void restoreWalletFromKeysSync(
message: monero.Wallet_errorString(newWptr));
}
// CW-712 - Try to restore deterministic wallet first, if the view key doesn't
// match the view key provided
if (spendKey != "") {
final viewKeyRestored = monero.Wallet_secretViewKey(newWptr);
if (viewKey != viewKeyRestored && viewKey != "") {
monero.WalletManager_closeWallet(wmPtr, newWptr, false);
File(path).deleteSync();
File(path+".keys").deleteSync();
newWptr = monero.WalletManager_createWalletFromKeys(
wmPtr,
path: path,
password: password,
restoreHeight: restoreHeight,
addressString: address,
viewKeyString: viewKey,
spendKeyString: spendKey,
nettype: 0,
);
final status = monero.Wallet_status(newWptr);
if (status != 0) {
throw WalletRestoreFromKeysException(
message: monero.Wallet_errorString(newWptr));
}
}
}
wptr = newWptr;
openedWalletsByPath[path] = wptr!;

View file

@ -237,10 +237,10 @@ packages:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
version: "7.0.0"
fixnum:
dependency: transitive
description:
@ -463,8 +463,8 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
resolved-ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
ref: "3cb38bee9385faf46b03fd73aab85f3ac4115bf7"
resolved-ref: "3cb38bee9385faf46b03fd73aab85f3ac4115bf7"
url: "https://github.com/mrcyjanek/monero_c"
source: git
version: "0.0.0"

View file

@ -25,7 +25,7 @@ dependencies:
monero:
git:
url: https://github.com/mrcyjanek/monero_c
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b # monero_c hash
ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7 # monero_c hash
path: impls/monero.dart
mutex: ^3.1.0

View file

@ -466,21 +466,25 @@ class NanoClient {
blocks = blocks as Map<String, dynamic>;
// confirm all receivable blocks:
for (final blockHash in blocks.keys) {
final block = blocks[blockHash];
final String amountRaw = block["amount"] as String;
await receiveBlock(
blockHash: blockHash,
amountRaw: amountRaw,
privateKey: privateKey,
destinationAddress: destinationAddress,
);
// a bit of a hack:
await Future<void>.delayed(const Duration(seconds: 2));
try {
// confirm all receivable blocks:
for (final blockHash in blocks.keys) {
final block = blocks[blockHash];
final String amountRaw = block["amount"] as String;
await receiveBlock(
blockHash: blockHash,
amountRaw: amountRaw,
privateKey: privateKey,
destinationAddress: destinationAddress,
);
// a bit of a hack:
await Future<void>.delayed(const Duration(seconds: 2));
}
return blocks.keys.length;
} catch (_) {
// we failed to confirm all receivable blocks for w/e reason (PoW / node outage / etc)
return 0;
}
return blocks.keys.length;
}
void stop() {}

View file

@ -14,8 +14,11 @@ import 'package:bip39/bip39.dart' as bip39;
import 'package:nanodart/nanodart.dart';
import 'package:nanoutil/nanoutil.dart';
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials, NanoNewWalletCredentials> {
class NanoWalletService extends WalletService<
NanoNewWalletCredentials,
NanoRestoreWalletFromSeedCredentials,
NanoRestoreWalletFromKeysCredentials,
NanoNewWalletCredentials> {
NanoWalletService(this.walletInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
@ -33,8 +36,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
String seedKey = NanoSeeds.generateSeed();
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
// ensure default if not present:
credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: DerivationType.nano);
// should never happen but just in case:
if (credentials.walletInfo!.derivationInfo == null) {
credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
} else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
}
final wallet = NanoWallet(
walletInfo: credentials.walletInfo!,
@ -86,7 +93,8 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
}
@override
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials,
{bool? isTestnet}) async {
if (credentials.seedKey.contains(' ')) {
throw Exception("Invalid key!");
} else {
@ -106,6 +114,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
}
}
// should never happen but just in case:
if (credentials.walletInfo!.derivationInfo == null) {
credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
} else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
}
final wallet = await NanoWallet(
password: credentials.password!,
mnemonic: mnemonic ?? credentials.seedKey,
@ -119,11 +134,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
@override
Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) {
throw UnimplementedError("Restoring a Nano wallet from a hardware wallet is not yet supported!");
throw UnimplementedError(
"Restoring a Nano wallet from a hardware wallet is not yet supported!");
}
@override
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async {
if (credentials.mnemonic.contains(' ')) {
if (!bip39.validateMnemonic(credentials.mnemonic)) {
throw nm.NanoMnemonicIsIncorrectException();

View file

@ -140,7 +140,7 @@ void restoreWalletFromKeysSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
final newWptr = spendKey != ""
var newWptr = (spendKey != "")
? wownero.WalletManager_createDeterministicWalletFromSpendKey(
wmPtr,
path: path,
@ -165,7 +165,31 @@ void restoreWalletFromKeysSync(
throw WalletRestoreFromKeysException(
message: wownero.Wallet_errorString(newWptr));
}
// CW-712 - Try to restore deterministic wallet first, if the view key doesn't
// match the view key provided
if (spendKey != "") {
final viewKeyRestored = wownero.Wallet_secretViewKey(newWptr);
if (viewKey != viewKeyRestored && viewKey != "") {
wownero.WalletManager_closeWallet(wmPtr, newWptr, false);
File(path).deleteSync();
File(path+".keys").deleteSync();
newWptr = wownero.WalletManager_createWalletFromKeys(
wmPtr,
path: path,
password: password,
restoreHeight: restoreHeight,
addressString: address,
viewKeyString: viewKey,
spendKeyString: spendKey,
nettype: 0,
);
final status = wownero.Wallet_status(newWptr);
if (status != 0) {
throw WalletRestoreFromKeysException(
message: wownero.Wallet_errorString(newWptr));
}
}
}
wptr = newWptr;
openedWalletsByPath[path] = wptr!;

View file

@ -237,10 +237,10 @@ packages:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
version: "7.0.0"
fixnum:
dependency: transitive
description:
@ -463,8 +463,8 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
resolved-ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
ref: "3cb38bee9385faf46b03fd73aab85f3ac4115bf7"
resolved-ref: "3cb38bee9385faf46b03fd73aab85f3ac4115bf7"
url: "https://github.com/mrcyjanek/monero_c"
source: git
version: "0.0.0"

View file

@ -25,7 +25,7 @@ dependencies:
monero:
git:
url: https://github.com/mrcyjanek/monero_c
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b # monero_c hash
ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7 # monero_c hash
path: impls/monero.dart
mutex: ^3.1.0

View file

@ -40,6 +40,7 @@ const solanaDefaultNodeUri = 'rpc.ankr.com';
const tronDefaultNodeUri = 'trx.nownodes.io';
const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002';
const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568';
const moneroWorldNodeUri = '.moneroworld.com';
Future<void> defaultSettingsMigration(
{required int version,
@ -245,6 +246,9 @@ Future<void> defaultSettingsMigration(
_fixNodesUseSSLFlag(nodes);
await changeDefaultNanoNode(nodes, sharedPreferences);
break;
case 40:
await removeMoneroWorld(sharedPreferences: sharedPreferences, nodes: nodes);
break;
default:
break;
}
@ -488,15 +492,7 @@ Node? getBitcoinCashDefaultElectrumServer({required Box<Node> nodes}) {
Node getMoneroDefaultNode({required Box<Node> nodes}) {
final timeZone = DateTime.now().timeZoneOffset.inHours;
var nodeUri = '';
if (timeZone >= 1) {
// Eurasia
nodeUri = 'xmr-node-eu.cakewallet.com:18081';
} else if (timeZone <= -4) {
// America
nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
}
var nodeUri = newCakeWalletMoneroUri;
try {
return nodes.values.firstWhere((Node node) => node.uriRaw == nodeUri);
@ -1260,3 +1256,22 @@ Future<void> replaceTronDefaultNode({
// If it's not, we switch user to the new default node: NowNodes
await changeTronCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
}
Future<void> removeMoneroWorld(
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
const cakeWalletMoneroNodeUriPattern = '.moneroworld.com';
final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
final currentMoneroNode = nodes.values.firstWhere((node) => node.key == currentMoneroNodeId);
final needToReplaceCurrentMoneroNode = currentMoneroNode.uri.toString().contains(cakeWalletMoneroNodeUriPattern);
nodes.values.forEach((node) async {
if (node.type == WalletType.monero &&
node.uri.toString().contains(cakeWalletMoneroNodeUriPattern)) {
await node.delete();
}
});
if (needToReplaceCurrentMoneroNode) {
await changeMoneroCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
}
}

View file

@ -0,0 +1,80 @@
class Erc20TokenInfoExplorers {
String? contractAddress;
String? tokenName;
String? symbol;
String? divisor;
String? tokenType;
String? totalSupply;
String? blueCheckmark;
String? description;
String? website;
String? email;
String? blog;
String? reddit;
String? slack;
String? facebook;
String? twitter;
String? bitcointalk;
String? github;
String? telegram;
String? wechat;
String? linkedin;
String? discord;
String? whitepaper;
String? tokenPriceUSD;
String? image;
Erc20TokenInfoExplorers({
this.contractAddress,
this.tokenName,
this.symbol,
this.divisor,
this.tokenType,
this.totalSupply,
this.blueCheckmark,
this.description,
this.website,
this.email,
this.blog,
this.reddit,
this.slack,
this.facebook,
this.twitter,
this.bitcointalk,
this.github,
this.telegram,
this.wechat,
this.linkedin,
this.discord,
this.whitepaper,
this.tokenPriceUSD,
this.image,
});
Erc20TokenInfoExplorers.fromJson(Map<String, dynamic> json) {
contractAddress = json['contractAddress'] as String?;
tokenName = json['tokenName'] as String?;
symbol = json['symbol'] as String?;
divisor = json['divisor'] as String?;
tokenType = json['tokenType'] as String?;
totalSupply = json['totalSupply'] as String?;
blueCheckmark = json['blueCheckmark'] as String?;
description = json['description'] as String?;
website = json['website'] as String?;
email = json['email'] as String?;
blog = json['blog'] as String?;
reddit = json['reddit'] as String?;
slack = json['slack'] as String?;
facebook = json['facebook'] as String?;
twitter = json['twitter'] as String?;
bitcointalk = json['bitcointalk'] as String?;
github = json['github'] as String?;
telegram = json['telegram'] as String?;
wechat = json['wechat'] as String?;
linkedin = json['linkedin'] as String?;
discord = json['discord'] as String?;
whitepaper = json['whitepaper'] as String?;
tokenPriceUSD = json['tokenPriceUSD'] as String?;
image = json['image'] as String?;
}
}

View file

@ -0,0 +1,85 @@
class Erc20TokenInfoMoralis {
String? address;
String? addressLabel;
String? name;
String? symbol;
String? decimals;
String? logo;
String? logoHash;
String? thumbnail;
String? totalSupply;
String? totalSupplyFormatted;
String? fullyDilutedValuation;
String? blockNumber;
int? validated;
String? createdAt;
bool? possibleSpam;
bool? verifiedContract;
Links? links;
int? securityScore;
Erc20TokenInfoMoralis({
this.address,
this.addressLabel,
this.name,
this.symbol,
this.decimals,
this.logo,
this.logoHash,
this.thumbnail,
this.totalSupply,
this.totalSupplyFormatted,
this.fullyDilutedValuation,
this.blockNumber,
this.validated,
this.createdAt,
this.possibleSpam,
this.verifiedContract,
this.links,
this.securityScore,
});
Erc20TokenInfoMoralis.fromJson(Map<String, dynamic> json) {
address = json['address'] as String?;
addressLabel = json['address_label'] as String?;
name = json['name'] as String?;
symbol = json['symbol'] as String?;
decimals = json['decimals'] as String?;
logo = json['logo'] as String?;
logoHash = json['logo_hash'] as String?;
thumbnail = json['thumbnail'] as String?;
totalSupply = json['total_supply'] as String?;
totalSupplyFormatted = json['total_supply_formatted'] as String?;
fullyDilutedValuation = json['fully_diluted_valuation'] as String?;
blockNumber = json['block_number'] as String?;
validated = json['validated'] as int?;
createdAt = json['created_at'] as String?;
possibleSpam = json['possible_spam'] as bool?;
verifiedContract = json['verified_contract'] as bool;
links =
json['links'] != null ? new Links.fromJson(json['links'] as Map<String, dynamic>) : null;
securityScore = json['security_score'] as int?;
}
}
class Links {
String? twitter;
String? website;
String? facebook;
String? reddit;
String? github;
String? linkedin;
String? telegram;
Links({this.twitter, this.website, this.facebook, this.reddit});
Links.fromJson(Map<String, dynamic> json) {
twitter = json['twitter'] as String?;
website = json['website'] as String?;
facebook = json['facebook'] as String?;
reddit = json['reddit'] as String?;
github = json['github'] as String?;
linkedin = json['linkedin'] as String?;
telegram = json['telegram'] as String?;
}
}

View file

@ -31,6 +31,7 @@ class LanguageService {
'ha': 'Hausa Najeriya (Nigeria)',
'tl': 'Filipino (Tagalog)',
'hy': 'Հայերեն (Armenian)',
'vi': 'Tiếng Việt (Vietnamese)',
};
static const Map<String, String> localeCountryCode = {
@ -61,6 +62,7 @@ class LanguageService {
'ha': 'hau',
'tl': 'phl',
'hy': 'arm',
'vi': 'vnm',
};
static final list = <String, String>{};

View file

@ -27,6 +27,10 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
ExchangeProviderDescription(title: 'ThorChain', raw: 8, image: 'assets/images/thorchain.png');
static const quantex =
ExchangeProviderDescription(title: 'Quantex', raw: 9, image: 'assets/images/quantex.png');
static const letsExchange =
ExchangeProviderDescription(title: 'LetsExchange', raw: 10, image: 'assets/images/letsexchange_icon.svg');
static const stealthEx =
ExchangeProviderDescription(title: 'StealthEx', raw: 11, image: 'assets/images/stealthex.png');
static ExchangeProviderDescription deserialize({required int raw}) {
switch (raw) {
@ -50,6 +54,10 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
return thorChain;
case 9:
return quantex;
case 10:
return letsExchange;
case 11:
return stealthEx;
default:
throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');
}

View file

@ -0,0 +1,292 @@
import 'dart:convert';
import 'dart:developer';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/limits.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:http/http.dart' as http;
class LetsExchangeExchangeProvider extends ExchangeProvider {
LetsExchangeExchangeProvider() : super(pairList: supportedPairs(_notSupported));
static const List<CryptoCurrency> _notSupported = [];
static const apiKey = secrets.letsExchangeBearerToken;
static const _baseUrl = 'api.letsexchange.io';
static const _infoPath = '/api/v1/info';
static const _infoRevertPath = '/api/v1/info-revert';
static const _createTransactionPath = '/api/v1/transaction';
static const _createTransactionRevertPath = '/api/v1/transaction-revert';
static const _getTransactionPath = '/api/v1/transaction';
static const _affiliateId = secrets.letsExchangeAffiliateId;
@override
String get title => 'LetsExchange';
@override
bool get isAvailable => true;
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => true;
@override
ExchangeProviderDescription get description => ExchangeProviderDescription.letsExchange;
@override
Future<bool> checkIsAvailable() async => true;
@override
Future<Limits> fetchLimits(
{required CryptoCurrency from,
required CryptoCurrency to,
required bool isFixedRateMode}) async {
final networkFrom = _getNetworkType(from);
final networkTo = _getNetworkType(to);
try {
final params = {
'from': from.title,
'to': to.title,
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'amount': '1',
'affiliate_id': _affiliateId
};
final responseJSON = await _getInfo(params, isFixedRateMode);
final min = double.tryParse(responseJSON['min_amount'] as String);
final max = double.tryParse(responseJSON['max_amount'] as String);
return Limits(min: min, max: max);
} catch (e) {
log(e.toString());
throw Exception('Failed to fetch limits');
}
}
@override
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
required bool isFixedRateMode,
required bool isReceiveAmount}) async {
final networkFrom = _getNetworkType(from);
final networkTo = _getNetworkType(to);
try {
final params = {
'from': from.title,
'to': to.title,
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'amount': amount.toString(),
'affiliate_id': _affiliateId
};
final responseJSON = await _getInfo(params, isFixedRateMode);
final amountToGet = double.tryParse(responseJSON['amount'] as String) ?? 0.0;
return isFixedRateMode ? amount / amountToGet : amountToGet / amount;
} catch (e) {
log(e.toString());
return 0.0;
}
}
@override
Future<Trade> createTrade(
{required TradeRequest request,
required bool isFixedRateMode,
required bool isSendAll}) async {
final networkFrom = _getNetworkType(request.fromCurrency);
final networkTo = _getNetworkType(request.toCurrency);
try {
final params = {
'from': request.fromCurrency.title,
'to': request.toCurrency.title,
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'amount': isFixedRateMode ? request.toAmount.toString() : request.fromAmount.toString(),
'affiliate_id': _affiliateId
};
final responseInfoJSON = await _getInfo(params, isFixedRateMode);
final rateId = responseInfoJSON['rate_id'] as String;
final withdrawalAddress = _normalizeBchAddress(request.toAddress);
final returnAddress = _normalizeBchAddress(request.refundAddress);
final tradeParams = {
'coin_from': request.fromCurrency.title,
'coin_to': request.toCurrency.title,
if (!isFixedRateMode) 'deposit_amount': request.fromAmount.toString(),
'withdrawal': withdrawalAddress,
if (isFixedRateMode) 'withdrawal_amount': request.toAmount.toString(),
'withdrawal_extra_id': '',
'return': returnAddress,
'rate_id': rateId,
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'affiliate_id': _affiliateId
};
final headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': apiKey
};
final uri = Uri.https(_baseUrl,
isFixedRateMode ? _createTransactionRevertPath : _createTransactionPath, tradeParams);
final response = await http.post(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('LetsExchange create trade failed: ${response.body}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final id = responseJSON['transaction_id'] as String;
final from = responseJSON['coin_from'] as String;
final to = responseJSON['coin_to'] as String;
final payoutAddress = responseJSON['withdrawal'] as String;
final depositAddress = responseJSON['deposit'] as String;
final refundAddress = responseJSON['return'] as String;
final depositAmount = responseJSON['deposit_amount'] as String;
final receiveAmount = responseJSON['withdrawal_amount'] as String;
final status = responseJSON['status'] as String;
final createdAtString = responseJSON['created_at'] as String;
final expiredAtTimestamp = responseJSON['expired_at'] as int;
final createdAt = DateTime.parse(createdAtString);
final expiredAt = DateTime.fromMillisecondsSinceEpoch(expiredAtTimestamp * 1000);
CryptoCurrency fromCurrency;
if (request.fromCurrency.tag != null && request.fromCurrency.title == from) {
fromCurrency = request.fromCurrency;
} else {
fromCurrency = CryptoCurrency.fromString(from);
}
CryptoCurrency toCurrency;
if (request.toCurrency.tag != null && request.toCurrency.title == to) {
toCurrency = request.toCurrency;
} else {
toCurrency = CryptoCurrency.fromString(to);
}
return Trade(
id: id,
from: fromCurrency,
to: toCurrency,
provider: description,
inputAddress: depositAddress,
payoutAddress: payoutAddress,
refundAddress: refundAddress,
amount: depositAmount,
receiveAmount: receiveAmount,
state: TradeState.deserialize(raw: status),
createdAt: createdAt,
expiredAt: expiredAt,
);
} catch (e) {
log(e.toString());
throw TradeNotCreatedException(description);
}
}
@override
Future<Trade> findTradeById({required String id}) async {
final headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': apiKey
};
final url = Uri.https(_baseUrl, '$_getTransactionPath/$id');
final response = await http.get(url, headers: headers);
if (response.statusCode != 200) {
throw Exception('LetsExchange fetch trade failed: ${response.body}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final from = responseJSON['coin_from'] as String;
final to = responseJSON['coin_to'] as String;
final payoutAddress = responseJSON['withdrawal'] as String;
final depositAddress = responseJSON['deposit'] as String;
final refundAddress = responseJSON['return'] as String;
final depositAmount = responseJSON['deposit_amount'] as String;
final receiveAmount = responseJSON['withdrawal_amount'] as String;
final status = responseJSON['status'] as String;
final createdAtString = responseJSON['created_at'] as String;
final expiredAtTimestamp = responseJSON['expired_at'] as int;
final createdAt = DateTime.parse(createdAtString);
final expiredAt = DateTime.fromMillisecondsSinceEpoch(expiredAtTimestamp * 1000);
return Trade(
id: id,
from: CryptoCurrency.fromString(from),
to: CryptoCurrency.fromString(to),
provider: description,
inputAddress: depositAddress,
payoutAddress: payoutAddress,
refundAddress: refundAddress,
amount: depositAmount,
receiveAmount: receiveAmount,
state: TradeState.deserialize(raw: status),
createdAt: createdAt,
expiredAt: expiredAt,
isRefund: status == 'refund',
);
}
Future<Map<String, dynamic>> _getInfo(Map<String, String> params, bool isFixedRateMode) async {
final headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': apiKey
};
try {
final uri = Uri.https(_baseUrl, isFixedRateMode ? _infoRevertPath : _infoPath, params);
final response = await http.post(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('LetsExchange fetch info failed: ${response.body}');
}
return json.decode(response.body) as Map<String, dynamic>;
} catch (e) {
throw Exception('LetsExchange failed to fetch info ${e.toString()}');
}
}
String? _getNetworkType(CryptoCurrency currency) {
if (currency.tag != null && currency.tag!.isNotEmpty) {
switch (currency.tag!) {
case 'TRX':
return 'TRC20';
case 'ETH':
return 'ERC20';
case 'BSC':
return 'BEP20';
case 'POLY':
return 'MATIC';
default:
return currency.tag!;
}
}
return currency.title;
}
String _normalizeBchAddress(String address) =>
address.startsWith('bitcoincash:') ? address.substring(12) : address;
}

View file

@ -0,0 +1,299 @@
import 'dart:convert';
import 'dart:developer';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/limits.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:http/http.dart' as http;
class StealthExExchangeProvider extends ExchangeProvider {
StealthExExchangeProvider() : super(pairList: supportedPairs(_notSupported));
static const List<CryptoCurrency> _notSupported = [];
static final apiKey = secrets.stealthExBearerToken;
static final _additionalFeePercent = double.tryParse(secrets.stealthExAdditionalFeePercent);
static const _baseUrl = 'https://api.stealthex.io';
static const _rangePath = '/v4/rates/range';
static const _amountPath = '/v4/rates/estimated-amount';
static const _exchangesPath = '/v4/exchanges';
@override
String get title => 'StealthEX';
@override
bool get isAvailable => true;
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => true;
@override
ExchangeProviderDescription get description => ExchangeProviderDescription.stealthEx;
@override
Future<bool> checkIsAvailable() async => true;
@override
Future<Limits> fetchLimits(
{required CryptoCurrency from,
required CryptoCurrency to,
required bool isFixedRateMode}) async {
final curFrom = isFixedRateMode ? to : from;
final curTo = isFixedRateMode ? from : to;
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
final body = {
'route': {
'from': {'symbol': _getName(curFrom), 'network': _getNetwork(curFrom)},
'to': {'symbol': _getName(curTo), 'network': _getNetwork(curTo)}
},
'estimation': isFixedRateMode ? 'reversed' : 'direct',
'rate': isFixedRateMode ? 'fixed' : 'floating',
'additional_fee_percent': _additionalFeePercent,
};
try {
final response = await http.post(Uri.parse(_baseUrl + _rangePath),
headers: headers, body: json.encode(body));
if (response.statusCode != 200) {
throw Exception('StealthEx fetch limits failed: ${response.body}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final min = toDouble(responseJSON['min_amount']);
final max = responseJSON['max_amount'] as double?;
return Limits(min: min, max: max);
} catch (e) {
log(e.toString());
throw Exception('StealthEx failed to fetch limits');
}
}
@override
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
required bool isFixedRateMode,
required bool isReceiveAmount}) async {
final response = await getEstimatedExchangeAmount(
from: from, to: to, amount: amount, isFixedRateMode: isFixedRateMode);
final estimatedAmount = response['estimated_amount'] as double? ?? 0.0;
return estimatedAmount > 0.0
? isFixedRateMode
? amount / estimatedAmount
: estimatedAmount / amount
: 0.0;
}
@override
Future<Trade> createTrade(
{required TradeRequest request,
required bool isFixedRateMode,
required bool isSendAll}) async {
String? rateId;
String? validUntil;
try {
if (isFixedRateMode) {
final response = await getEstimatedExchangeAmount(
from: request.fromCurrency,
to: request.toCurrency,
amount: double.parse(request.toAmount),
isFixedRateMode: isFixedRateMode);
rateId = response['rate_id'] as String?;
validUntil = response['valid_until'] as String?;
if (rateId == null) throw TradeNotCreatedException(description);
}
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
final body = {
'route': {
'from': {
'symbol': _getName(request.fromCurrency),
'network': _getNetwork(request.fromCurrency)
},
'to': {'symbol': _getName(request.toCurrency), 'network': _getNetwork(request.toCurrency)}
},
'estimation': isFixedRateMode ? 'reversed' : 'direct',
'rate': isFixedRateMode ? 'fixed' : 'floating',
if (isFixedRateMode) 'rate_id': rateId,
'amount':
isFixedRateMode ? double.parse(request.toAmount) : double.parse(request.fromAmount),
'address': request.toAddress,
'refund_address': request.refundAddress,
'additional_fee_percent': _additionalFeePercent,
};
final response = await http.post(Uri.parse(_baseUrl + _exchangesPath),
headers: headers, body: json.encode(body));
if (response.statusCode != 201) {
throw Exception('StealthEx create trade failed: ${response.body}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final deposit = responseJSON['deposit'] as Map<String, dynamic>;
final withdrawal = responseJSON['withdrawal'] as Map<String, dynamic>;
final id = responseJSON['id'] as String;
final from = deposit['symbol'] as String;
final to = withdrawal['symbol'] as String;
final payoutAddress = withdrawal['address'] as String;
final depositAddress = deposit['address'] as String;
final refundAddress = responseJSON['refund_address'] as String;
final depositAmount = toDouble(deposit['amount']);
final receiveAmount = toDouble(withdrawal['amount']);
final status = responseJSON['status'] as String;
final createdAtString = responseJSON['created_at'] as String;
final createdAt = DateTime.parse(createdAtString);
final expiredAt = validUntil != null
? DateTime.parse(validUntil)
: DateTime.now().add(Duration(minutes: 5));
CryptoCurrency fromCurrency;
if (request.fromCurrency.tag != null && request.fromCurrency.title.toLowerCase() == from) {
fromCurrency = request.fromCurrency;
} else {
fromCurrency = CryptoCurrency.fromString(from);
}
CryptoCurrency toCurrency;
if (request.toCurrency.tag != null && request.toCurrency.title.toLowerCase() == to) {
toCurrency = request.toCurrency;
} else {
toCurrency = CryptoCurrency.fromString(to);
}
return Trade(
id: id,
from: fromCurrency,
to: toCurrency,
provider: description,
inputAddress: depositAddress,
payoutAddress: payoutAddress,
refundAddress: refundAddress,
amount: depositAmount.toString(),
receiveAmount: receiveAmount.toString(),
state: TradeState.deserialize(raw: status),
createdAt: createdAt,
expiredAt: expiredAt,
);
} catch (e) {
log(e.toString());
throw TradeNotCreatedException(description);
}
}
@override
Future<Trade> findTradeById({required String id}) async {
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
final uri = Uri.parse('$_baseUrl$_exchangesPath/$id');
final response = await http.get(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('StealthEx fetch trade failed: ${response.body}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final deposit = responseJSON['deposit'] as Map<String, dynamic>;
final withdrawal = responseJSON['withdrawal'] as Map<String, dynamic>;
final respId = responseJSON['id'] as String;
final from = deposit['symbol'] as String;
final to = withdrawal['symbol'] as String;
final payoutAddress = withdrawal['address'] as String;
final depositAddress = deposit['address'] as String;
final refundAddress = responseJSON['refund_address'] as String;
final depositAmount = toDouble(deposit['amount']);
final receiveAmount = toDouble(withdrawal['amount']);
final status = responseJSON['status'] as String;
final createdAtString = responseJSON['created_at'] as String;
final createdAt = DateTime.parse(createdAtString);
return Trade(
id: respId,
from: CryptoCurrency.fromString(from),
to: CryptoCurrency.fromString(to),
provider: description,
inputAddress: depositAddress,
payoutAddress: payoutAddress,
refundAddress: refundAddress,
amount: depositAmount.toString(),
receiveAmount: receiveAmount.toString(),
state: TradeState.deserialize(raw: status),
createdAt: createdAt,
isRefund: status == 'refunded',
);
}
Future<Map<String, dynamic>> getEstimatedExchangeAmount(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
required bool isFixedRateMode}) async {
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
final body = {
'route': {
'from': {'symbol': _getName(from), 'network': _getNetwork(from)},
'to': {'symbol': _getName(to), 'network': _getNetwork(to)}
},
'estimation': isFixedRateMode ? 'reversed' : 'direct',
'rate': isFixedRateMode ? 'fixed' : 'floating',
'amount': amount,
'additional_fee_percent': _additionalFeePercent,
};
try {
final response = await http.post(Uri.parse(_baseUrl + _amountPath),
headers: headers, body: json.encode(body));
if (response.statusCode != 200) return {};
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final rate = responseJSON['rate'] as Map<String, dynamic>?;
return {
'estimated_amount': responseJSON['estimated_amount'] as double?,
if (rate != null) 'valid_until': rate['valid_until'] as String?,
if (rate != null) 'rate_id': rate['id'] as String?
};
} catch (e) {
log(e.toString());
return {};
}
}
double toDouble(dynamic value) {
if (value is int) {
return value.toDouble();
} else if (value is double) {
return value;
} else {
return 0.0;
}
}
String _getName(CryptoCurrency currency) {
if (currency == CryptoCurrency.usdcEPoly) return 'usdce';
return currency.title.toLowerCase();
}
String _getNetwork(CryptoCurrency currency) {
if (currency.tag == null) return 'mainnet';
if (currency == CryptoCurrency.maticpoly) return 'mainnet';
if (currency.tag == 'POLY') return 'matic';
return currency.tag!.toLowerCase();
}
}

View file

@ -40,7 +40,6 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
static const exchanging = TradeState(raw: 'exchanging', title: 'Exchanging');
static const sending = TradeState(raw: 'sending', title: 'Sending');
static const success = TradeState(raw: 'success', title: 'Success');
static TradeState deserialize({required String raw}) {
switch (raw) {
@ -107,6 +106,7 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
case 'waitingAuthorization':
return waitingAuthorization;
case 'failed':
case 'error':
return failed;
case 'completed':
return completed;
@ -119,12 +119,14 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
case 'refunded':
return refunded;
case 'confirmation':
case 'verifying':
return confirmation;
case 'confirmed':
return confirmed;
case 'exchanging':
return exchanging;
case 'sending':
case 'sending_confirmation':
return sending;
case 'success':
case 'done':

View file

@ -50,11 +50,14 @@ final rootKey = GlobalKey<RootState>();
final RouteObserver<PageRoute<dynamic>> routeObserver = RouteObserver<PageRoute<dynamic>>();
Future<void> main() async {
await runAppWithZone();
}
Future<void> runAppWithZone() async {
bool isAppRunning = false;
await runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = ExceptionHandler.onError;
/// A callback that is invoked when an unhandled error occurs in the root
@ -64,12 +67,7 @@ Future<void> main() async {
return true;
};
await setDefaultMinimumWindowSize();
await CakeHive.close();
await initializeAppConfigs();
await initializeAppAtRoot();
final appDocDir = await getAppDir();
@ -81,34 +79,11 @@ Future<void> main() async {
});
runApp(App());
isAppRunning = true;
}, (error, stackTrace) async {
if (!isAppRunning) {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
scrollBehavior: AppScrollBehavior(),
home: Scaffold(
body: SingleChildScrollView(
child: Container(
margin: EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
child: Column(
children: [
Text(
'Error:\n${error.toString()}',
style: TextStyle(fontSize: 22),
),
Text(
'Stack trace:\n${stackTrace.toString()}',
style: TextStyle(fontSize: 16),
),
],
),
),
),
),
),
TopLevelErrorWidget(error: error, stackTrace: stackTrace),
);
}
@ -116,6 +91,12 @@ Future<void> main() async {
});
}
Future<void> initializeAppAtRoot({bool reInitializing = false}) async {
if (!reInitializing) await setDefaultMinimumWindowSize();
await CakeHive.close();
await initializeAppConfigs();
}
Future<void> initializeAppConfigs() async {
setRootDirFromEnv();
final appDir = await getAppDir();
@ -216,7 +197,7 @@ Future<void> initializeAppConfigs() async {
transactionDescriptions: transactionDescriptions,
secureStorage: secureStorage,
anonpayInvoiceInfo: anonpayInvoiceInfo,
initialMigrationVersion: 39,
initialMigrationVersion: 40,
);
}
@ -349,3 +330,41 @@ class _HomeState extends State<_Home> {
return const SizedBox.shrink();
}
}
class TopLevelErrorWidget extends StatelessWidget {
const TopLevelErrorWidget({
required this.error,
required this.stackTrace,
super.key,
});
final Object error;
final StackTrace stackTrace;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
scrollBehavior: AppScrollBehavior(),
home: Scaffold(
body: SingleChildScrollView(
child: Container(
margin: EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
child: Column(
children: [
Text(
'Error:\n${error.toString()}',
style: TextStyle(fontSize: 22),
),
Text(
'Stack trace:\n${stackTrace.toString()}',
style: TextStyle(fontSize: 16),
),
],
),
),
),
),
);
}
}

View file

@ -2,16 +2,19 @@ import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/address_text_field.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/src/widgets/checkbox_widget.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class EditTokenPage extends BasePage {
EditTokenPage({
@ -79,7 +82,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
if (widget.token != null) {
address = widget.homeSettingsViewModel.getTokenAddressBasedOnWallet(widget.token!);
_contractAddressController.text = address ?? '';
_tokenNameController.text = widget.token!.name;
_tokenSymbolController.text = widget.token!.title;
@ -135,8 +138,8 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
S.of(context).warning,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).dialogTheme.backgroundColor,
fontWeight: FontWeight.bold,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
Padding(
@ -145,7 +148,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
S.of(context).add_token_warning,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.extension<TransactionTradeTheme>()!
.detailsTitlesColor,
@ -167,6 +170,15 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
bottomSection: Column(
children: [
if (_showDisclaimer) ...[
Text(
S.current.do_not_send_funds_to_contract_address_warning,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
SizedBox(height: 20),
CheckboxWidget(
value: _disclaimerChecked,
caption: S.of(context).add_token_disclaimer_check,
@ -176,47 +188,84 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
),
SizedBox(height: 20),
],
Row(
children: <Widget>[
Expanded(
child: PrimaryButton(
onPressed: () async {
if (widget.token != null) {
await widget.homeSettingsViewModel.deleteToken(widget.token!);
}
Navigator.pop(context);
},
text: widget.token != null ? S.of(context).delete : S.of(context).cancel,
color: Colors.red,
textColor: Colors.white,
),
),
SizedBox(width: 20),
Expanded(
child: PrimaryButton(
onPressed: () async {
if (_formKey.currentState!.validate() &&
(!_showDisclaimer || _disclaimerChecked)) {
await widget.homeSettingsViewModel.addToken(
token: CryptoCurrency(
name: _tokenNameController.text,
title: _tokenSymbolController.text.toUpperCase(),
decimals: int.parse(_tokenDecimalController.text),
iconPath: _tokenIconPathController.text,
),
contractAddress: _contractAddressController.text,
);
if (mounted) {
Observer(
builder: (context) {
return Row(
children: <Widget>[
Expanded(
child: LoadingPrimaryButton(
isLoading: widget.homeSettingsViewModel.isDeletingToken,
onPressed: () async {
if (widget.token != null) {
await widget.homeSettingsViewModel.deleteToken(widget.token!);
}
Navigator.pop(context);
}
}
},
text: S.of(context).save,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
),
],
},
text: widget.token != null ? S.of(context).delete : S.of(context).cancel,
color: Colors.red,
textColor: Colors.white,
),
),
SizedBox(width: 20),
Expanded(
child: LoadingPrimaryButton(
isLoading: widget.homeSettingsViewModel.isAddingToken ||
widget.homeSettingsViewModel.isValidatingContractAddress,
onPressed: () async {
if (_formKey.currentState!.validate() &&
(!_showDisclaimer || _disclaimerChecked)) {
final hasPotentialError = await widget.homeSettingsViewModel
.checkIfERC20TokenContractAddressIsAPotentialScamAddress(
_contractAddressController.text,
);
final actionCall = () async {
await widget.homeSettingsViewModel.addToken(
token: CryptoCurrency(
name: _tokenNameController.text,
title: _tokenSymbolController.text.toUpperCase(),
decimals: int.parse(_tokenDecimalController.text),
iconPath: _tokenIconPathController.text,
),
contractAddress: _contractAddressController.text,
);
};
if (hasPotentialError) {
showPopUp<void>(
context: context,
builder: (dialogContext) {
return AlertWithTwoActions(
alertTitle: S.current.warning,
alertContent: S.current.contract_warning,
rightButtonText: S.of(context).continue_text,
leftButtonText: S.of(context).cancel,
actionRightButton: () async {
Navigator.of(dialogContext).pop();
await actionCall();
if (mounted) {
Navigator.pop(context);
}
},
actionLeftButton: () => Navigator.of(dialogContext).pop(),
);
},
);
} else {
await actionCall();
if (mounted) {
Navigator.pop(context);
}
}
}
},
text: S.of(context).save,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
),
],
);
},
),
],
),

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/utils/image_utill.dart';
import 'package:flutter/material.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
@ -36,7 +37,8 @@ class TradeRow extends StatelessWidget {
children: [
ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.asset(provider.image, width: 36, height: 36)),
child: ImageUtil.getImageFromPath(
imagePath: provider.image, height: 36, width: 36)),
SizedBox(width: 12),
Expanded(
child: Column(

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/utils/image_utill.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
@ -101,7 +102,8 @@ class ExchangeConfirmPage extends BasePage {
mainAxisAlignment: MainAxisAlignment.center,
children: [
(trade.provider.image?.isNotEmpty ?? false)
? Image.asset(trade.provider.image, height: 50)
? ImageUtil.getImageFromPath(
imagePath: trade.provider.image, width: 50)
: const SizedBox(),
if (!trade.provider.horizontalLogo)
Padding(

View file

@ -40,13 +40,11 @@ class NanoChangeRepPage extends BasePage {
(node) => node.account == currentRepAccount,
orElse: () => N2Node(
account: currentRepAccount,
alias: currentRepAccount,
score: 0,
uptime: "???",
weight: 0,
),
);
return currentNode;
}
@ -57,9 +55,7 @@ class NanoChangeRepPage extends BasePage {
child: FutureBuilder(
future: nano!.getN2Reps(_wallet),
builder: (context, snapshot) {
if (snapshot.data == null) {
return SizedBox();
}
final reps = snapshot.data ?? [];
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
@ -101,29 +97,35 @@ class NanoChangeRepPage extends BasePage {
),
_buildSingleRepresentative(
context,
getCurrentRepNode(snapshot.data as List<N2Node>),
getCurrentRepNode(reps),
isList: false,
divider: false,
),
Divider(height: 20),
Container(
margin: EdgeInsets.only(top: 12),
child: Text(
S.current.nano_pick_new_rep,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
if (reps.isNotEmpty) ...[
Divider(height: 20),
Container(
margin: EdgeInsets.only(top: 12),
child: Text(
S.current.nano_pick_new_rep,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
),
),
),
),
Divider(height: 20),
],
],
),
],
),
contentPadding: EdgeInsets.only(bottom: 24),
content: Container(
child: Column(
children: _getRepresentativeWidgets(context, snapshot.data as List<N2Node>),
),
child: reps.isNotEmpty
? Column(
children: _getRepresentativeWidgets(context, reps),
)
: SizedBox(),
),
bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer(
@ -207,19 +209,22 @@ class NanoChangeRepPage extends BasePage {
final List<Widget> ret = [];
for (final N2Node node in list) {
if (node.alias != null && node.alias!.trim().isNotEmpty) {
ret.add(_buildSingleRepresentative(context, node));
bool divider = node != list.first;
ret.add(_buildSingleRepresentative(context, node, divider: divider, isList: true));
}
}
return ret;
}
Widget _buildSingleRepresentative(BuildContext context, N2Node rep, {bool isList = true}) {
Widget _buildSingleRepresentative(
BuildContext context,
N2Node rep, {
bool isList = true,
bool divider = false,
}) {
return Column(
children: <Widget>[
if (isList)
Divider(
height: 2,
),
if (divider) Divider(height: 2),
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
@ -244,11 +249,11 @@ class NanoChangeRepPage extends BasePage {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
_sanitizeAlias(rep.alias),
rep.alias ?? rep.account!,
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontWeight: FontWeight.w700,
fontSize: 18,
fontSize: rep.alias == null ? 14 : 18,
),
),
Container(
@ -337,11 +342,4 @@ class NanoChangeRepPage extends BasePage {
],
);
}
String _sanitizeAlias(String? alias) {
if (alias != null) {
return alias.replaceAll(RegExp(r'[^a-zA-Z_.!?_;:-]'), '');
}
return '';
}
}

View file

@ -406,24 +406,16 @@ class WalletRestorePage extends BasePage {
) as DerivationInfo?;
} else if (derivationsWithHistory == 1) {
dInfo = derivations[derivationWithHistoryIndex];
}
// get the default derivation for this wallet type:
if (dInfo == null) {
} else if (derivations.length == 1) {
// we only return 1 derivation if we're pretty sure we know which one to use:
if (derivations.length == 1) {
dInfo = derivations.first;
} else {
// if we have multiple possible derivations, and none have histories
// we just default to the most common one:
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
}
dInfo = derivations.first;
} else {
// if we have multiple possible derivations, and none (or multiple) have histories
// we just default to the most common one:
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
}
this.derivationInfo = dInfo;
if (this.derivationInfo == null) {
this.derivationInfo = walletRestoreViewModel.getDefaultDerivation();
}
await walletRestoreViewModel.create(options: _credentials());
seedSettingsViewModel.setPassphrase(null);

View file

@ -68,11 +68,11 @@ class SendPage extends BasePage {
@override
Function(BuildContext)? get pushToNextWidget => (context) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.focusedChild?.unfocus();
}
};
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.focusedChild?.unfocus();
}
};
@override
Widget? leading(BuildContext context) {
@ -81,9 +81,8 @@ class SendPage extends BasePage {
color: titleColor(context),
size: 16,
);
final _closeButton = currentTheme.type == ThemeType.dark
? closeButtonImageDarkTheme
: closeButtonImage;
final _closeButton =
currentTheme.type == ThemeType.dark ? closeButtonImageDarkTheme : closeButtonImage;
bool isMobileView = responsiveLayoutUtil.shouldRenderMobileUI;
@ -94,9 +93,7 @@ class SendPage extends BasePage {
child: ButtonTheme(
minWidth: double.minPositive,
child: Semantics(
label: !isMobileView
? S.of(context).close
: S.of(context).seed_alert_back,
label: !isMobileView ? S.of(context).close : S.of(context).seed_alert_back,
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith(
@ -281,6 +278,7 @@ class SendPage extends BasePage {
? template.cryptoCurrency
: template.fiatCurrency,
onTap: () async {
sendViewModel.state = IsExecutingState();
if (template.additionalRecipients
?.isNotEmpty ??
false) {
@ -314,6 +312,7 @@ class SendPage extends BasePage {
template: template,
);
}
sendViewModel.state = InitialExecutionState();
},
onRemove: () {
showPopUp<void>(
@ -397,6 +396,7 @@ class SendPage extends BasePage {
builder: (_) {
return LoadingPrimaryButton(
onPressed: () async {
if (sendViewModel.state is IsExecutingState) return;
if (_formKey.currentState != null &&
!_formKey.currentState!.validate()) {
if (sendViewModel.outputs.length > 1) {

View file

@ -113,10 +113,6 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
return GestureDetector(
onTap: () {
if (item.isDisabled) {
return;
}
bool newValue = !item.value;
item.value = newValue;
widget.onChanged(index, newValue);
@ -134,7 +130,7 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
borderColor: Theme.of(context).dividerColor,
iconColor: Colors.white,
onChanged: (bool? value) {
if (value == null || item.isDisabled) {
if (value == null) {
return;
}

View file

@ -16,7 +16,9 @@ abstract class TradeFilterStoreBase with Store {
displaySimpleSwap = true,
displayTrocador = true,
displayExolix = true,
displayThorChain = true;
displayThorChain = true,
displayLetsExchange = true,
displayStealthEx = true;
@observable
bool displayXMRTO;
@ -42,6 +44,12 @@ abstract class TradeFilterStoreBase with Store {
@observable
bool displayThorChain;
@observable
bool displayLetsExchange;
@observable
bool displayStealthEx;
@computed
bool get displayAllTrades =>
displayChangeNow &&
@ -49,7 +57,9 @@ abstract class TradeFilterStoreBase with Store {
displaySimpleSwap &&
displayTrocador &&
displayExolix &&
displayThorChain;
displayThorChain &&
displayLetsExchange &&
displayStealthEx;
@action
void toggleDisplayExchange(ExchangeProviderDescription provider) {
@ -78,6 +88,11 @@ abstract class TradeFilterStoreBase with Store {
case ExchangeProviderDescription.thorChain:
displayThorChain = !displayThorChain;
break;
case ExchangeProviderDescription.letsExchange:
displayLetsExchange = !displayLetsExchange;
case ExchangeProviderDescription.stealthEx:
displayStealthEx = !displayStealthEx;
break;
case ExchangeProviderDescription.all:
if (displayAllTrades) {
displayChangeNow = false;
@ -88,6 +103,8 @@ abstract class TradeFilterStoreBase with Store {
displayTrocador = false;
displayExolix = false;
displayThorChain = false;
displayLetsExchange = false;
displayStealthEx = false;
} else {
displayChangeNow = true;
displaySideShift = true;
@ -97,6 +114,8 @@ abstract class TradeFilterStoreBase with Store {
displayTrocador = true;
displayExolix = true;
displayThorChain = true;
displayLetsExchange = true;
displayStealthEx = true;
}
break;
}
@ -112,13 +131,21 @@ abstract class TradeFilterStoreBase with Store {
? _trades
.where((item) =>
(displayXMRTO && item.trade.provider == ExchangeProviderDescription.xmrto) ||
(displaySideShift && item.trade.provider == ExchangeProviderDescription.sideShift) ||
(displayChangeNow && item.trade.provider == ExchangeProviderDescription.changeNow) ||
(displayMorphToken && item.trade.provider == ExchangeProviderDescription.morphToken) ||
(displaySimpleSwap && item.trade.provider == ExchangeProviderDescription.simpleSwap) ||
(displaySideShift &&
item.trade.provider == ExchangeProviderDescription.sideShift) ||
(displayChangeNow &&
item.trade.provider == ExchangeProviderDescription.changeNow) ||
(displayMorphToken &&
item.trade.provider == ExchangeProviderDescription.morphToken) ||
(displaySimpleSwap &&
item.trade.provider == ExchangeProviderDescription.simpleSwap) ||
(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador) ||
(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix) ||
(displayThorChain && item.trade.provider == ExchangeProviderDescription.thorChain))
(displayThorChain &&
item.trade.provider == ExchangeProviderDescription.thorChain) ||
(displayLetsExchange &&
item.trade.provider == ExchangeProviderDescription.letsExchange) ||
(displayStealthEx && item.trade.provider == ExchangeProviderDescription.stealthEx))
.toList()
: _trades;
}

View file

@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
class ImageUtil {
static Widget getImageFromPath({required String imagePath, double? height, double? width}) {
final bool isNetworkImage = imagePath.startsWith('http') || imagePath.startsWith('https');
final bool isSvg = imagePath.endsWith('.svg');
final double _height = height ?? 35;
final double _width = width ?? 35;
if (isNetworkImage) {
return isSvg
? SvgPicture.network(
imagePath,
height: _height,
width: _width,
placeholderBuilder: (BuildContext context) => Container(
height: _height,
width: _width,
child: Center(
child: CircularProgressIndicator(),
),
),
)
: Image.network(
imagePath,
height: _height,
width: _width,
loadingBuilder:
(BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child;
}
return Container(
height: _height,
width: _width,
child: Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return Container(
height: _height,
width: _width,
);
},
);
} else {
return isSvg
? SvgPicture.asset(imagePath, height: _height, width: _width)
: Image.asset(imagePath, height: _height, width: _width);
}
}
}

View file

@ -1,12 +1,14 @@
import 'dart:convert';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/provider_types.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/service_status.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/generated/i18n.dart';
@ -45,11 +47,9 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:eth_sig_util/util/utils.dart';
import 'package:flutter/services.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:http/http.dart' as http;
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
part 'dashboard_view_model.g.dart';
@ -129,6 +129,16 @@ abstract class DashboardViewModelBase with Store {
caption: ExchangeProviderDescription.thorChain.title,
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)),
FilterItem(
value: () => tradeFilterStore.displayLetsExchange,
caption: ExchangeProviderDescription.letsExchange.title,
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.letsExchange)),
FilterItem(
value: () => tradeFilterStore.displayStealthEx,
caption: ExchangeProviderDescription.stealthEx.title,
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.stealthEx)),
]
},
subname = '',

View file

@ -1,8 +1,14 @@
import 'dart:convert';
import 'dart:developer';
import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/entities/erc20_token_info_explorers.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/erc20_token_info_moralis.dart';
import 'package:cake_wallet/entities/sort_balance_types.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/tron/tron.dart';
@ -11,6 +17,8 @@ import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
import 'package:http/http.dart' as http;
import 'package:cake_wallet/.secrets.g.dart' as secrets;
part 'home_settings_view_model.g.dart';
@ -18,7 +26,10 @@ class HomeSettingsViewModel = HomeSettingsViewModelBase with _$HomeSettingsViewM
abstract class HomeSettingsViewModelBase with Store {
HomeSettingsViewModelBase(this._settingsStore, this._balanceViewModel)
: tokens = ObservableSet<CryptoCurrency>() {
: tokens = ObservableSet<CryptoCurrency>(),
isAddingToken = false,
isDeletingToken = false,
isValidatingContractAddress = false {
_updateTokensList();
}
@ -27,6 +38,15 @@ abstract class HomeSettingsViewModelBase with Store {
final ObservableSet<CryptoCurrency> tokens;
@observable
bool isAddingToken;
@observable
bool isDeletingToken;
@observable
bool isValidatingContractAddress;
@observable
String searchText = '';
@ -45,66 +65,263 @@ abstract class HomeSettingsViewModelBase with Store {
@action
void setPinNativeToken(bool value) => _settingsStore.pinNativeTokenAtTop = value;
@action
Future<void> addToken({
required String contractAddress,
required CryptoCurrency token,
}) async {
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
final erc20token = Erc20Token(
name: token.name,
symbol: token.title,
decimal: token.decimals,
contractAddress: contractAddress,
iconPath: token.iconPath,
);
try {
isAddingToken = true;
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
final erc20token = Erc20Token(
name: token.name,
symbol: token.title,
decimal: token.decimals,
contractAddress: contractAddress,
iconPath: token.iconPath,
);
await ethereum!.addErc20Token(_balanceViewModel.wallet, erc20token);
await ethereum!.addErc20Token(_balanceViewModel.wallet, erc20token);
}
if (_balanceViewModel.wallet.type == WalletType.polygon) {
final polygonToken = Erc20Token(
name: token.name,
symbol: token.title,
decimal: token.decimals,
contractAddress: contractAddress,
iconPath: token.iconPath,
);
await polygon!.addErc20Token(_balanceViewModel.wallet, polygonToken);
}
if (_balanceViewModel.wallet.type == WalletType.solana) {
await solana!.addSPLToken(
_balanceViewModel.wallet,
token,
contractAddress,
);
}
if (_balanceViewModel.wallet.type == WalletType.tron) {
await tron!.addTronToken(_balanceViewModel.wallet, token, contractAddress);
}
_updateTokensList();
_updateFiatPrices(token);
} finally {
isAddingToken = false;
}
if (_balanceViewModel.wallet.type == WalletType.polygon) {
final polygonToken = Erc20Token(
name: token.name,
symbol: token.title,
decimal: token.decimals,
contractAddress: contractAddress,
iconPath: token.iconPath,
);
await polygon!.addErc20Token(_balanceViewModel.wallet, polygonToken);
}
if (_balanceViewModel.wallet.type == WalletType.solana) {
await solana!.addSPLToken(
_balanceViewModel.wallet,
token,
contractAddress,
);
}
if (_balanceViewModel.wallet.type == WalletType.tron) {
await tron!.addTronToken(_balanceViewModel.wallet, token, contractAddress);
}
_updateTokensList();
_updateFiatPrices(token);
}
@action
Future<void> deleteToken(CryptoCurrency token) async {
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
await ethereum!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
}
try {
isDeletingToken = true;
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
await ethereum!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
}
if (_balanceViewModel.wallet.type == WalletType.polygon) {
await polygon!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
}
if (_balanceViewModel.wallet.type == WalletType.polygon) {
await polygon!.deleteErc20Token(_balanceViewModel.wallet, token as Erc20Token);
}
if (_balanceViewModel.wallet.type == WalletType.solana) {
await solana!.deleteSPLToken(_balanceViewModel.wallet, token);
}
if (_balanceViewModel.wallet.type == WalletType.solana) {
await solana!.deleteSPLToken(_balanceViewModel.wallet, token);
}
if (_balanceViewModel.wallet.type == WalletType.tron) {
await tron!.deleteTronToken(_balanceViewModel.wallet, token);
if (_balanceViewModel.wallet.type == WalletType.tron) {
await tron!.deleteTronToken(_balanceViewModel.wallet, token);
}
_updateTokensList();
} finally {
isDeletingToken = false;
}
}
Future<bool> checkIfERC20TokenContractAddressIsAPotentialScamAddress(
String contractAddress,
) async {
try {
isValidatingContractAddress = true;
if (!isEVMCompatibleChain(_balanceViewModel.wallet.type)) {
return false;
}
bool isEthereum = _balanceViewModel.wallet.type == WalletType.ethereum;
bool isPotentialScamViaMoralis = await _isPotentialScamTokenViaMoralis(
contractAddress,
isEthereum ? 'eth' : 'polygon',
);
bool isPotentialScamViaExplorers = await _isPotentialScamTokenViaExplorers(
contractAddress,
isEthereum: isEthereum,
);
bool isUnverifiedContract = await _isContractUnverified(
contractAddress,
isEthereum: isEthereum,
);
final showWarningForContractAddress =
isPotentialScamViaMoralis || isUnverifiedContract || isPotentialScamViaExplorers;
return showWarningForContractAddress;
} finally {
isValidatingContractAddress = false;
}
}
Future<bool> _isPotentialScamTokenViaMoralis(
String contractAddress,
String chainName,
) async {
final uri = Uri.https(
'deep-index.moralis.io',
'/api/v2.2/erc20/metadata',
{
"chain": chainName,
"addresses": contractAddress,
},
);
try {
final response = await http.get(
uri,
headers: {
"Accept": "application/json",
"X-API-Key": secrets.moralisApiKey,
},
);
final decodedResponse = jsonDecode(response.body);
final tokenInfo = Erc20TokenInfoMoralis.fromJson(decodedResponse[0] as Map<String, dynamic>);
// Based on analysis using Moralis internal metrics
if (tokenInfo.possibleSpam == true) {
return true;
}
// Tokens whose contract have not been verified are potentially risky tokens.
if (tokenInfo.verifiedContract == false) {
return true;
}
// Tokens with a security score less than 40 are potentially risky, requiring caution when dealing with them.
if (tokenInfo.securityScore == null || tokenInfo.securityScore! < 40) {
return true;
}
// Absence of a website URL for an ERC-20 token can be a potential red flag. A legitimate ERC-20 projects should have a well-maintained website that provides information about the token, its purpose, team, and roadmap.
if (tokenInfo.links?.website == null || tokenInfo.links!.website!.isEmpty) {
return true;
}
// Having a Fully Diluted Valiuation of 0 is a significant red flag that could signify:
// - An abandoned/unlaunched project
// - Incorrect/missing token data
// - Suspicious manipulation of token data
if (tokenInfo.fullyDilutedValuation == '0') {
return true;
}
// I mean, a logo is the most basic of all the potential causes, but why does your fully functional project not have a logo?
if (tokenInfo.logo == null) {
return true;
}
return false;
} catch (e) {
return true;
}
}
Future<bool> _isPotentialScamTokenViaExplorers(
String contractAddress, {
required bool isEthereum,
}) async {
final uri = Uri.https(
isEthereum ? "api.etherscan.io" : "api.polygonscan.com",
"/api",
{
"module": "token",
"action": "tokeninfo",
"contractaddress": contractAddress,
"apikey": isEthereum ? secrets.etherScanApiKey : secrets.polygonScanApiKey,
},
);
try {
final response = await http.get(uri);
final decodedResponse = jsonDecode(response.body) as Map<String, dynamic>;
if (decodedResponse['status'] != '1') {
log('${decodedResponse['result']}');
return true;
}
final tokenInfo =
Erc20TokenInfoExplorers.fromJson(decodedResponse['result'][0] as Map<String, dynamic>);
// A token without an email to reach its creators is a potential red flag
if (tokenInfo.email?.isEmpty == true) {
return true;
}
// A token without a website is a potential red flag
if (tokenInfo.website?.isEmpty == true) {
return true;
}
// if (tokenInfo.whitepaper == null) {
// return true;
// }
return false;
} catch (e) {
return true;
}
}
Future<bool> _isContractUnverified(
String contractAddress, {
required bool isEthereum,
}) async {
final uri = Uri.https(
isEthereum ? "api.etherscan.io" : "api.polygonscan.com",
"/api",
{
"module": "contract",
"action": "getsourcecode",
"contractaddress": contractAddress,
"apikey": isEthereum ? secrets.etherScanApiKey : secrets.polygonScanApiKey,
},
);
try {
final response = await http.get(uri);
final decodedResponse = jsonDecode(response.body) as Map<String, dynamic>;
if (decodedResponse['status'] == '0') {
log('${decodedResponse['result']}');
return true;
}
if (decodedResponse['status'] == '1' &&
decodedResponse['result'][0]['ABI'] == 'Contract source code not verified') {
return true; // Contract is not verified
} else {
return false; // Contract is verified
}
} catch (e) {
return true;
}
_updateTokensList();
}
Future<CryptoCurrency?> getToken(String contractAddress) async {

View file

@ -69,12 +69,10 @@ class TransactionListItem extends ActionListItem with Keyable {
}
String get formattedStatus {
if (transaction.direction == TransactionDirection.incoming) {
if (balanceViewModel.wallet.type == WalletType.monero ||
balanceViewModel.wallet.type == WalletType.wownero ||
balanceViewModel.wallet.type == WalletType.haven) {
return formattedPendingStatus;
}
if (balanceViewModel.wallet.type == WalletType.monero ||
balanceViewModel.wallet.type == WalletType.wownero ||
balanceViewModel.wallet.type == WalletType.haven) {
return formattedPendingStatus;
}
return transaction.isPending ? S.current.pending : '';
}

View file

@ -7,6 +7,7 @@ import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart';
@ -52,6 +53,8 @@ abstract class ExchangeTradeViewModelBase with Store {
case ExchangeProviderDescription.quantex:
_provider = QuantexExchangeProvider();
break;
case ExchangeProviderDescription.stealthEx:
_provider = StealthExExchangeProvider();
case ExchangeProviderDescription.thorChain:
_provider = ThorChainExchangeProvider(tradesStore: trades);
break;

View file

@ -4,6 +4,8 @@ import 'dart:convert';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cake_wallet/core/create_trade_result.dart';
import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_priority.dart';
@ -160,15 +162,17 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
final SharedPreferences sharedPreferences;
List<ExchangeProvider> get _allProviders => [
ChangeNowExchangeProvider(settingsStore: _settingsStore),
SideShiftExchangeProvider(),
SimpleSwapExchangeProvider(),
ThorChainExchangeProvider(tradesStore: trades),
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
QuantexExchangeProvider(),
TrocadorExchangeProvider(
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
];
ChangeNowExchangeProvider(settingsStore: _settingsStore),
SideShiftExchangeProvider(),
SimpleSwapExchangeProvider(),
ThorChainExchangeProvider(tradesStore: trades),
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
QuantexExchangeProvider(),
LetsExchangeExchangeProvider(),
StealthExExchangeProvider(),
TrocadorExchangeProvider(
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
];
@observable
ExchangeProvider? provider;

View file

@ -62,7 +62,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
derivationInfo = options["derivationInfo"] as DerivationInfo?;
passphrase = options["passphrase"] as String?;
}
derivationInfo ??= getDefaultDerivation();
derivationInfo ??= getDefaultCreateDerivation();
switch (restoreWallet.restoreMode) {
case WalletRestoreMode.keys:

View file

@ -46,7 +46,7 @@ abstract class RestoreFromBackupViewModelBase with Store {
final data = await file.readAsBytes();
await backupService.importBackup(data, password);
await main();
await initializeAppAtRoot(reInitializing: true);
final store = getIt.get<AppStore>();
ReactionDisposer? reaction;

View file

@ -4,9 +4,11 @@ import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart';
@ -59,6 +61,11 @@ abstract class TradeDetailsViewModelBase with Store {
break;
case ExchangeProviderDescription.quantex:
_provider = QuantexExchangeProvider();
case ExchangeProviderDescription.letsExchange:
_provider = LetsExchangeExchangeProvider();
break;
case ExchangeProviderDescription.stealthEx:
_provider = StealthExExchangeProvider();
break;
}
@ -86,6 +93,10 @@ abstract class TradeDetailsViewModelBase with Store {
return 'https://track.ninerealms.com/${trade.id}';
case ExchangeProviderDescription.quantex:
return 'https://myquantex.com/send/${trade.id}';
case ExchangeProviderDescription.letsExchange:
return 'https://letsexchange.io/?transactionId=${trade.id}';
case ExchangeProviderDescription.stealthEx:
return 'https://stealthex.io/exchange/?id=${trade.id}';
}
return null;
}

View file

@ -97,7 +97,7 @@ abstract class WalletCreationVMBase with Store {
dirPath: dirPath,
address: '',
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(),
derivationInfo: credentials.derivationInfo ?? getDefaultCreateDerivation(),
hardwareWalletType: credentials.hardwareWalletType,
);
@ -116,7 +116,7 @@ abstract class WalletCreationVMBase with Store {
}
}
DerivationInfo? getDefaultDerivation() {
DerivationInfo? getDefaultCreateDerivation() {
final useBip39 = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39;
switch (type) {
case WalletType.nano:
@ -147,10 +147,14 @@ abstract class WalletCreationVMBase with Store {
}
DerivationInfo? getCommonRestoreDerivation() {
final useElectrum = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.electrum;
switch (this.type) {
case WalletType.nano:
return DerivationInfo(derivationType: DerivationType.nano);
case WalletType.bitcoin:
if (useElectrum) {
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
}
return DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/84'/0'/0'/0",
@ -158,6 +162,9 @@ abstract class WalletCreationVMBase with Store {
scriptType: "p2wpkh",
);
case WalletType.litecoin:
if (useElectrum) {
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
}
return DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/84'/2'/0'/0",

View file

@ -42,7 +42,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
type == WalletType.tron,
isButtonEnabled = false,
mode = WalletRestoreMode.seed,
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel, type: type, isRecovery: true) {
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
type: type, isRecovery: true) {
switch (type) {
case WalletType.monero:
availableModes = WalletRestoreMode.values;
@ -194,10 +195,11 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
case WalletType.nano:
return nano!.createNanoRestoreWalletFromKeysCredentials(
name: name,
password: password,
seedKey: options['private_key'] as String,
derivationType: options["derivationType"] as DerivationType);
name: name,
password: password,
seedKey: options['private_key'] as String,
derivationType: derivationInfo!.derivationType!,
);
case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromPrivateKey(
name: name,

View file

@ -160,6 +160,7 @@
"contact_name": "اسم جهة الاتصال",
"contact_support": "اتصل بالدعم",
"continue_text": "التالي",
"contract_warning": "تم وضع علامة على عنوان العقد هذا على أنه احتيالي محتمل. يرجى المعالجة بحذر.",
"contractName": "ﺪﻘﻌﻟﺍ ﻢﺳﺍ",
"contractSymbol": "ﺪﻘﻌﻟﺍ ﺰﻣﺭ",
"copied_key_to_clipboard": "تم نسخ ${key} إلى الحافظة",
@ -219,6 +220,7 @@
"displayable": "قابل للعرض",
"do_not_have_enough_gas_asset": "ليس لديك ما يكفي من ${currency} لإجراء معاملة وفقًا لشروط شبكة blockchain الحالية. أنت بحاجة إلى المزيد من ${currency} لدفع رسوم شبكة blockchain، حتى لو كنت ترسل أصلًا مختلفًا.",
"do_not_send": "لا ترسل",
"do_not_send_funds_to_contract_address_warning": "لا ترسل أموالًا إلى هذا العنوان \n\n هذا مجرد معرف للرمز المميز ، فستضيع أي أموال تم إرسالها إلى هذا العنوان. \n\n ملاحظة: لن تطلب منك Cake إضافة عنوان عقد أبدًا",
"do_not_share_warning_text": "لا تشارك هذه مع أي شخص آخر ، بما في ذلك الدعم.\n\nيمكن أن تتم سرقة أموالك!",
"do_not_show_me": "لا ترني هذا مجددا",
"domain_looks_up": "ﻝﺎﺠﻤﻟﺍ ﺚﺤﺑ ﺕﺎﻴﻠﻤﻋ",

View file

@ -160,6 +160,7 @@
"contact_name": "Име на контакт",
"contact_support": "Свържи се с отдел поддръжка",
"continue_text": "Напред",
"contract_warning": "Този адрес на договора е маркиран като потенциално измамник. Моля, обработете с повишено внимание.",
"contractName": "Име на договора",
"contractSymbol": "Договор Символ",
"copied_key_to_clipboard": "Копиран ключ: ${key}",
@ -219,6 +220,7 @@
"displayable": "Възможност за показване",
"do_not_have_enough_gas_asset": "Нямате достатъчно ${currency}, за да извършите транзакция с текущите условия на блокчейн мрежата. Имате нужда от повече ${currency}, за да платите таксите за блокчейн мрежа, дори ако изпращате различен актив.",
"do_not_send": "Не изпращай",
"do_not_send_funds_to_contract_address_warning": "Не изпращайте средства на този адрес \n\n Това е само идентификатор за токена, всички средства, изпратени на този адрес",
"do_not_share_warning_text": "Не споделяйте това с никого, дори и отдел поддръжка.\n\nПарите Ви могат и ще бъдат откраднати!",
"do_not_show_me": "Не показвай повече това",
"domain_looks_up": "Търсене на домейни",

View file

@ -160,6 +160,7 @@
"contact_name": "Jméno kontaktu",
"contact_support": "Kontaktovat podporu",
"continue_text": "Pokračovat",
"contract_warning": "Tato adresa smlouvy byla označena jako potenciálně podvodná. Zpracovejte prosím opatrně.",
"contractName": "Název smlouvy",
"contractSymbol": "Symbol smlouvy",
"copied_key_to_clipboard": "Zkopírován ${key} do schránky",
@ -219,6 +220,7 @@
"displayable": "Zobrazitelné",
"do_not_have_enough_gas_asset": "Nemáte dostatek ${currency} k provedení transakce s aktuálními podmínkami blockchainové sítě. K placení poplatků za blockchainovou síť potřebujete více ${currency}, i když posíláte jiné aktivum.",
"do_not_send": "Neodesílat",
"do_not_send_funds_to_contract_address_warning": "Neposílejte finanční prostředky na tuto adresu \n\n Toto je pouze identifikátor pro token, jakékoli prostředky zaslané na tuto adresu budou ztraceny.",
"do_not_share_warning_text": "Toto nesdílejte s nikým jiným, ani s podporou.\n\nJinak mohou být Vaše prostředky ukradeny!",
"do_not_show_me": "Příště nezobrazovat",
"domain_looks_up": "Vyhledávání domén",

View file

@ -160,6 +160,7 @@
"contact_name": "Name des Kontakts",
"contact_support": "Support kontaktieren",
"continue_text": "Weiter",
"contract_warning": "Diese Vertragsadresse wurde als potenziell betrügerisch gekennzeichnet. Bitte verarbeiten Sie mit Vorsicht.",
"contractName": "Vertragsname",
"contractSymbol": "Vertragssymbol",
"copied_key_to_clipboard": "${key} in Zwischenablage kopiert",
@ -219,6 +220,7 @@
"displayable": "Anzeigebar",
"do_not_have_enough_gas_asset": "Sie verfügen nicht über genügend ${currency}, um eine Transaktion unter den aktuellen Bedingungen des Blockchain-Netzwerks durchzuführen. Sie benötigen mehr ${currency}, um die Gebühren für das Blockchain-Netzwerk zu bezahlen, auch wenn Sie einen anderen Vermögenswert senden.",
"do_not_send": "Nicht senden",
"do_not_send_funds_to_contract_address_warning": "Senden Sie keine Mittel an diese Adresse \n\n Dies ist nur eine Kennung für das Token. Alle an diese Adresse gesendeten Mittel gehen verloren. \n\n Hinweis: Kuchen würde Sie niemals auffordern, eine Vertragsadresse hinzuzufügen",
"do_not_share_warning_text": "Teilen Sie diese nicht mit anderen, einschließlich Support.\n\nIhr Geld kann und wird gestohlen werden!",
"do_not_show_me": "Zeig mir das nicht noch einmal",
"domain_looks_up": "Domain-Suchen",

View file

@ -160,6 +160,7 @@
"contact_name": "Contact Name",
"contact_support": "Contact Support",
"continue_text": "Continue",
"contract_warning": "This contract address has been flagged as potentially fraudulent. Please process with caution.",
"contractName": "Contract Name",
"contractSymbol": "Contract Symbol",
"copied_key_to_clipboard": "Copied ${key} to Clipboard",
@ -219,6 +220,7 @@
"displayable": "Displayable",
"do_not_have_enough_gas_asset": "You do not have enough ${currency} to make a transaction with the current blockchain network conditions. You need more ${currency} to pay blockchain network fees, even if you are sending a different asset.",
"do_not_send": "Don't send",
"do_not_send_funds_to_contract_address_warning": "Do not send funds to this address\n\n This is just an identifier for the token, any funds sent to this address will be lost.\n\n NOTE: Cake would never ask you to add a contract address",
"do_not_share_warning_text": "Do not share these with anyone else, including support.\n\nYour funds can and will be stolen!",
"do_not_show_me": "Do not show me this again",
"domain_looks_up": "Domain lookups",

View file

@ -160,6 +160,7 @@
"contact_name": "Nombre de contacto",
"contact_support": "Contactar con Soporte",
"continue_text": "Continuar",
"contract_warning": "Esta dirección de contrato ha sido marcada como potencialmente fraudulenta. Por favor, procese con precaución.",
"contractName": "Nombre del contrato",
"contractSymbol": "Símbolo de contrato",
"copied_key_to_clipboard": "Copiado ${key} al portapapeles",
@ -219,6 +220,7 @@
"displayable": "Visualizable",
"do_not_have_enough_gas_asset": "No tienes suficiente ${currency} para realizar una transacción con las condiciones actuales de la red blockchain. Necesita más ${currency} para pagar las tarifas de la red blockchain, incluso si envía un activo diferente.",
"do_not_send": "no enviar",
"do_not_send_funds_to_contract_address_warning": "No envíe fondos a esta dirección \n\n Esto es solo un identificador para el token, se perderán los fondos enviados a esta dirección. \n\n Nota: Cake nunca le pediría que agregue una dirección de contrato",
"do_not_share_warning_text": "No comparta estos con nadie más, incluido el soporte.\n\n¡Sus fondos pueden ser y serán robados!",
"do_not_show_me": "no me muestres esto otra vez",
"domain_looks_up": "Búsquedas de dominio",

View file

@ -160,6 +160,7 @@
"contact_name": "Nom de Contact",
"contact_support": "Contacter l'assistance",
"continue_text": "Continuer",
"contract_warning": "Cette adresse contractuelle a été signalée comme potentiellement frauduleuse. Veuillez traiter avec prudence.",
"contractName": "Nom du contrat",
"contractSymbol": "Symbole du contrat",
"copied_key_to_clipboard": "${key} copiée vers le presse-papier",
@ -219,6 +220,7 @@
"displayable": "Visible",
"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.",
"do_not_send": "Ne pas envoyer",
"do_not_send_funds_to_contract_address_warning": "N'envoyez pas de fonds à cette adresse \n\n Ceci est juste un identifiant pour le jeton, tous les fonds envoyés à cette adresse seront perdus. \n\n Remarque: Le gâteau ne vous demanderait jamais d'ajouter une adresse de contrat",
"do_not_share_warning_text": "Ne les partagez avec personne, y compris avec l'assistance.\n\nVos fonds seraient inmanquablement volés !",
"do_not_show_me": "Ne plus me montrer ceci à l'avenir",
"domain_looks_up": "Résolution de nom",

View file

@ -160,6 +160,7 @@
"contact_name": "Sunan Tuntuɓi",
"contact_support": "Tuntuɓi Support",
"continue_text": "Ci gaba",
"contract_warning": "An kafa wannan adireshin kwantaragin kwangilar yayin da yuwuwar zamba. Da fatan za a aiwatar da taka tsantsan.",
"contractName": "Sunan Kwangila",
"contractSymbol": "Alamar Kwangila",
"copied_key_to_clipboard": "An kwafa ${key} a cikin kwafin",
@ -219,6 +220,7 @@
"displayable": "Ana iya nunawa",
"do_not_have_enough_gas_asset": "Ba ku da isassun ${currency} don yin ma'amala tare da yanayin cibiyar sadarwar blockchain na yanzu. Kuna buƙatar ƙarin ${currency} don biyan kuɗaɗen cibiyar sadarwar blockchain, koda kuwa kuna aika wata kadara daban.",
"do_not_send": "Kada ka aika",
"do_not_send_funds_to_contract_address_warning": "Kada ku aika da kudade zuwa wannan adireshin \n\n Wannan kawai mai ganowa ne kawai don token, kowane asusu da aka aiko zuwa wannan adireshin za a rasa. # Lafazin ba zai taba tambayar ka ƙara adireshin kwangila",
"do_not_share_warning_text": "Kada ku raba waɗannan ga kowa, gami da tallafi.\n\nZa a iya sace kuɗin ku kuma za a sace!",
"do_not_show_me": "Kar ka sake nuna min wannan",
"domain_looks_up": "Binciken yanki",

View file

@ -160,6 +160,7 @@
"contact_name": "संपर्क नाम",
"contact_support": "सहायता से संपर्क करें",
"continue_text": "जारी रहना",
"contract_warning": "इस अनुबंध के पते को संभावित रूप से धोखाधड़ी के रूप में चिह्नित किया गया है। कृपया सावधानी के साथ प्रक्रिया करें।",
"contractName": "अनुबंध का नाम",
"contractSymbol": "अनुबंध चिह्न",
"copied_key_to_clipboard": "की नकल की ${key} क्लिपबोर्ड पर",
@ -219,6 +220,7 @@
"displayable": "प्रदर्शन योग्य",
"do_not_have_enough_gas_asset": "वर्तमान ब्लॉकचेन नेटवर्क स्थितियों में लेनदेन करने के लिए आपके पास पर्याप्त ${currency} नहीं है। ब्लॉकचेन नेटवर्क शुल्क का भुगतान करने के लिए आपको अधिक ${currency} की आवश्यकता है, भले ही आप एक अलग संपत्ति भेज रहे हों।",
"do_not_send": "मत भेजो",
"do_not_send_funds_to_contract_address_warning": "इस पते पर धन न भेजें \n\n यह सिर्फ टोकन के लिए एक पहचानकर्ता है, इस पते पर भेजे गए किसी भी धन को खो दिया जाएगा। \n\n नोट: केक आपको एक अनुबंध पता जोड़ने के लिए कभी नहीं कहेगा",
"do_not_share_warning_text": "समर्थन सहित, इन्हें किसी और के साथ साझा न करें।\n\nआपके धन की चोरी हो सकती है और होगी!",
"do_not_show_me": "मुझे यह फिर न दिखाएं",
"domain_looks_up": "डोमेन लुकअप",

View file

@ -160,6 +160,7 @@
"contact_name": "Ime kontakta",
"contact_support": "Kontaktirajte podršku",
"continue_text": "Nastavak",
"contract_warning": "Ova adresa ugovora označena je kao potencijalno lažna. Molimo obradite s oprezom.",
"contractName": "Naziv ugovora",
"contractSymbol": "Simbol ugovora",
"copied_key_to_clipboard": "${key} kopiran u međuspremnik",
@ -219,6 +220,7 @@
"displayable": "Dostupno za prikaz",
"do_not_have_enough_gas_asset": "Nemate dovoljno ${currency} da izvršite transakciju s trenutačnim uvjetima blockchain mreže. Trebate više ${currency} da platite naknade za blockchain mrežu, čak i ako šaljete drugu imovinu.",
"do_not_send": "Ne šalji",
"do_not_send_funds_to_contract_address_warning": "Ne šaljite sredstva na ovu adresu \n\n Ovo je samo identifikator za token, izgubit će se bilo koja sredstva poslana na ovu adresu. \n\n Napomena: Torta nikad ne bi tražila da dodate adresu ugovora",
"do_not_share_warning_text": "Nemojte ih dijeliti ni s kim, uključujući podršku.\n\nVaša sredstva mogu i bit će ukradena!",
"do_not_show_me": "Ne pokazuj mi ovo više",
"domain_looks_up": "Pretraga domena",

View file

@ -160,6 +160,7 @@
"contact_name": "Կոնտակտի անուն",
"contact_support": "Հետադարձ կապ",
"continue_text": "Շարունակել",
"contract_warning": "Պայմանագրի այս հասցեն դրոշմել է որպես հնարավոր կեղծ: Խնդրում ենք զգուշությամբ մշակել:",
"contractName": "Գործարքի անուն",
"contractSymbol": "Գործարքի Նշան",
"copied_key_to_clipboard": "${key} պատճենված է տեքստի բուֆերում",
@ -218,6 +219,7 @@
"displayable": "Ցուցադրվող",
"do_not_have_enough_gas_asset": "Դուք չունեք բավարար ${currency} տրանզակցիան կատարելու համար ընթացիկ բլոկչեյն ցանցի պայմաններում: Դուք պետք է ունենաք ավելի շատ ${currency} blockchain ցանցի միջնորդավճարները վճարելու համար, նույնիսկ եթե դուք այլ ակտիվ եք ուղարկում:",
"do_not_send": "Մի ուղարկեք",
"do_not_send_funds_to_contract_address_warning": "Այս հասցեին գումարներ մի ուղարկեք \n\n Սա պարզապես նույնականացն է նշանի համար, այս հասցեով ուղարկված ցանկացած միջոց կկորչի: \n\n Նշում. Տորթը երբեք չի խնդրի ձեզ ավելացնել պայմանագրի հասցե",
"do_not_share_warning_text": "Մի կիսեք այս տեղեկատվությունը որևէ մեկի հետ, այդ թվում նաև աջակցության հետ: \n\nՁեր միջոցները կարող են գողանալ կորցնել!",
"do_not_show_me": "Մի ցուցադրեք ինձ նորից",
"domain_looks_up": "Դոմեյնի որոնում",

View file

@ -160,6 +160,7 @@
"contact_name": "Nama Kontak",
"contact_support": "Hubungi Dukungan",
"continue_text": "Lanjutkan",
"contract_warning": "Alamat kontrak ini telah ditandai sebagai berpotensi curang. Silakan memproses dengan hati -hati.",
"contractName": "Nama Kontrak",
"contractSymbol": "Simbol Kontrak",
"copied_key_to_clipboard": "Kunci ${key} disalin ke Clipboard",
@ -219,6 +220,7 @@
"displayable": "Dapat ditampilkan",
"do_not_have_enough_gas_asset": "Anda tidak memiliki cukup ${currency} untuk melakukan transaksi dengan kondisi jaringan blockchain saat ini. Anda memerlukan lebih banyak ${currency} untuk membayar biaya jaringan blockchain, meskipun Anda mengirimkan aset yang berbeda.",
"do_not_send": "Jangan kirim",
"do_not_send_funds_to_contract_address_warning": "Jangan mengirim dana ke alamat ini \n\n Ini hanya pengidentifikasi untuk token, dana apa pun yang dikirim ke alamat ini akan hilang. \n\n Catatan: Kue tidak akan pernah meminta Anda untuk menambahkan alamat kontrak",
"do_not_share_warning_text": "Jangan berikan ini pada siapapun, termasuk dukungan.\n\nDana Anda bisa dan akan dicuri!",
"do_not_show_me": "Jangan tampilkan ini lagi",
"domain_looks_up": "Pencarian domain",

View file

@ -161,6 +161,7 @@
"contact_name": "Nome Contatto",
"contact_support": "Contatta l'assistenza",
"continue_text": "Continua",
"contract_warning": "Questo indirizzo del contratto è stato contrassegnato come potenzialmente fraudolento. Si prega di elaborare con cautela.",
"contractName": "Nome del contratto",
"contractSymbol": "Simbolo del contratto",
"copied_key_to_clipboard": " ${key} copiata negli Appunti",
@ -220,6 +221,7 @@
"displayable": "Visualizzabile",
"do_not_have_enough_gas_asset": "Non hai abbastanza ${currency} per effettuare una transazione con le attuali condizioni della rete blockchain. Hai bisogno di più ${currency} per pagare le commissioni della rete blockchain, anche se stai inviando una risorsa diversa.",
"do_not_send": "Non inviare",
"do_not_send_funds_to_contract_address_warning": "Non inviare fondi a questo indirizzo \n\n Questo è solo un identificatore per il token, qualsiasi fondi inviati a questo indirizzo andrà perso. \n\n Nota: la torta non ti chiederebbe mai di aggiungere un indirizzo contrattuale",
"do_not_share_warning_text": "Non condividerli con nessun altro, incluso il supporto.\n\nI tuoi fondi possono e saranno rubati!",
"do_not_show_me": "Non mostrarmelo di nuovo",
"domain_looks_up": "Ricerche di domini",

View file

@ -160,6 +160,7 @@
"contact_name": "連絡先",
"contact_support": "サポートに連絡する",
"continue_text": "持続する",
"contract_warning": "この契約住所は、潜在的に不正としてフラグが立てられています。注意して処理してください。",
"contractName": "契約名",
"contractSymbol": "契約記号",
"copied_key_to_clipboard": "コピー済み ${key} クリップボードへ",
@ -219,6 +220,7 @@
"displayable": "表示可能",
"do_not_have_enough_gas_asset": "現在のブロックチェーン ネットワークの状況では、トランザクションを行うのに十分な ${currency} がありません。別のアセットを送信する場合でも、ブロックチェーン ネットワーク料金を支払うにはさらに ${currency} が必要です。",
"do_not_send": "送信しない",
"do_not_send_funds_to_contract_address_warning": "この住所に資金を送らないでください\n\nこれはトークンの識別子であり、この住所に送られた資金は失われます。",
"do_not_share_warning_text": "サポートを含め、これらを他の誰とも共有しないでください。\n\nあなたの資金は盗まれる可能性があります!",
"do_not_show_me": "また僕にこれを見せないでください",
"domain_looks_up": "ドメイン検索",

View file

@ -160,6 +160,7 @@
"contact_name": "담당자 이름",
"contact_support": "지원팀에 문의",
"continue_text": "잇다",
"contract_warning": "이 계약 주소는 잠재적으로 사기성으로 표시되었습니다. 주의해서 처리하십시오.",
"contractName": "계약명",
"contractSymbol": "계약 기호",
"copied_key_to_clipboard": "복사 ${key} 클립 보드로",
@ -219,6 +220,7 @@
"displayable": "표시 가능",
"do_not_have_enough_gas_asset": "현재 블록체인 네트워크 조건으로 거래를 하기에는 ${currency}이(가) 충분하지 않습니다. 다른 자산을 보내더라도 블록체인 네트워크 수수료를 지불하려면 ${currency}가 더 필요합니다.",
"do_not_send": "보내지 마세요",
"do_not_send_funds_to_contract_address_warning": "이 주소로 자금을 보내지 마십시오 \n\n 이것은 토큰의 식별자 일뿐입니다.이 주소로 전송 된 모든 자금은 손실됩니다. \n\n 참고 : Cake는 계약서 주소를 추가하도록 요구하지 않습니다.",
"do_not_share_warning_text": "지원을 포함하여 다른 사람과 이러한 정보를 공유하지 마십시오.\n\n귀하의 자금은 도난당할 수 있고 도난당할 수 있습니다!",
"do_not_show_me": "나를 다시 표시하지 않음",
"domain_looks_up": "도메인 조회",

View file

@ -160,6 +160,7 @@
"contact_name": "ဆက်သွယ်ရန်အမည်",
"contact_support": "ပံ့ပိုးကူညီမှုထံ ဆက်သွယ်ပါ။",
"continue_text": "ဆက်လက်",
"contract_warning": "ဒီစာချုပ်လိပ်စာအလားအလာအလားအလာအလားအလာအလံများကိုအလံလွှင့်တင်ခဲ့သည်။ ကျေးဇူးပြုပြီးသတိဖြင့်လုပ်ငန်းစဉ်။",
"contractName": "စာချုပ်အမည်",
"contractSymbol": "စာချုပ်သင်္ကေတ",
"copied_key_to_clipboard": "${key} ကို Clipboard သို့ ကူးယူထားသည်။",
@ -219,6 +220,7 @@
"displayable": "ပြသနိုင်သည်။",
"do_not_have_enough_gas_asset": "လက်ရှိ blockchain ကွန်ရက်အခြေအနေများနှင့် အရောင်းအဝယ်ပြုလုပ်ရန် သင့်တွင် ${currency} လုံလောက်မှုမရှိပါ။ သင်သည် မတူညီသော ပိုင်ဆိုင်မှုတစ်ခုကို ပေးပို့နေသော်လည်း blockchain ကွန်ရက်အခကြေးငွေကို ပေးဆောင်ရန် သင်သည် နောက်ထပ် ${currency} လိုအပ်ပါသည်။",
"do_not_send": "မပို့ပါနှင့်",
"do_not_send_funds_to_contract_address_warning": "ဒီလိပ်စာကိုရန်ပုံငွေမပို့ပါနဲ့။ ဒီလိပ်စာကိုအမှတ်အသားမပို့ပါနဲ့, ဒီလိပ်စာကိုပို့တဲ့ရန်ပုံငွေမဆိုဆုံးရှုံးသွားမှာပါ။ \n\n မှတ်ချက်။ ။ \n\n မှတ်ချက်။ ။",
"do_not_share_warning_text": "ပံ့ပိုးကူညီမှုအပါအဝင် ဤအရာများကို အခြားမည်သူနှင့်မျှ မမျှဝေပါနှင့်။\n\nသင့်ငွေများကို ခိုးယူခံရနိုင်သည်!",
"do_not_show_me": "ဒါကို ထပ်မပြနဲ့",
"domain_looks_up": "ဒိုမိန်းရှာဖွေမှုများ",

View file

@ -160,6 +160,7 @@
"contact_name": "Contactnaam",
"contact_support": "Contact opnemen met ondersteuning",
"continue_text": "Doorgaan met",
"contract_warning": "Dit contractadres is gemarkeerd als mogelijk frauduleus. Verwerk met voorzichtigheid.",
"contractName": "Contractnaam",
"contractSymbol": "Contractsymbool",
"copied_key_to_clipboard": "Gekopieerd ${key} naar het klembord",
@ -219,6 +220,7 @@
"displayable": "Weer te geven",
"do_not_have_enough_gas_asset": "U heeft niet genoeg ${currency} om een transactie uit te voeren met de huidige blockchain-netwerkomstandigheden. U heeft meer ${currency} nodig om blockchain-netwerkkosten te betalen, zelfs als u een ander item verzendt.",
"do_not_send": "Niet sturen",
"do_not_send_funds_to_contract_address_warning": "Stuur geen fondsen naar dit adres \n\n Dit is slechts een identificatie voor het token, alle fondsen die naar dit adres zijn verzonden, gaan verloren. \n\n Opmerking: cake zou u nooit vragen om een contractadres toe te voegen",
"do_not_share_warning_text": "Deel deze met niemand anders, ook niet met support.\n\nUw geld kan en zal worden gestolen!",
"do_not_show_me": "laat me dit niet opnieuw zien",
"domain_looks_up": "Domein opzoeken",

View file

@ -160,6 +160,7 @@
"contact_name": "Nazwa Kontaktu",
"contact_support": "Skontaktuj się z pomocą techniczną",
"continue_text": "Dalej",
"contract_warning": "Ten adres umowy został oznaczony jako potencjalnie nieuczciwy. Prosimy o ostrożność.",
"contractName": "Nazwa umowy",
"contractSymbol": "Symbol kontraktu",
"copied_key_to_clipboard": "Skopiowaneo ${key} do schowka",
@ -219,6 +220,7 @@
"displayable": "Wyświetlane",
"do_not_have_enough_gas_asset": "Nie masz wystarczającej ilości ${currency}, aby dokonać transakcji przy bieżących warunkach sieci blockchain. Potrzebujesz więcej ${currency}, aby uiścić opłaty za sieć blockchain, nawet jeśli wysyłasz inny zasób.",
"do_not_send": "Nie wysyłaj",
"do_not_send_funds_to_contract_address_warning": "Nie wysyłaj środków na ten adres \n\n To jest tylko identyfikator tokena, wszelkie środki wysłane na ten adres zostaną utracone. \n\n",
"do_not_share_warning_text": "NIE udostępniaj ich nikomu innemu, w tym pomocy technicznej.\n\nTwoje środki wtedy prawdopodobnie zostaną skradzione!",
"do_not_show_me": "Nie pokazuj mi tego ponownie",
"domain_looks_up": "Wyszukiwanie domen",

View file

@ -160,6 +160,7 @@
"contact_name": "Nome do contato",
"contact_support": "Contatar Suporte",
"continue_text": "Continuar",
"contract_warning": "Este endereço do contrato foi sinalizado como potencialmente fraudulento. Por favor, processe com cautela.",
"contractName": "Nome do contrato",
"contractSymbol": "Símbolo do Contrato",
"copied_key_to_clipboard": "${key} copiada para a área de transferência",
@ -219,6 +220,7 @@
"displayable": "Exibível",
"do_not_have_enough_gas_asset": "Você não tem ${currency} suficiente para fazer uma transação com as condições atuais da rede blockchain. Você precisa de mais ${currency} para pagar as taxas da rede blockchain, mesmo se estiver enviando um ativo diferente.",
"do_not_send": "não envie",
"do_not_send_funds_to_contract_address_warning": "Não envie fundos para este endereço \n\n Este é apenas um identificador para o token, quaisquer fundos enviados para este endereço serão perdidos. \n\n NOTA: O bolo nunca solicitaria que você adicione um endereço de contrato",
"do_not_share_warning_text": "Não os compartilhe com mais ninguém, incluindo suporte.\n\nSeus fundos podem e serão roubados!",
"do_not_show_me": "não me mostre isso novamente",
"domain_looks_up": "Pesquisas de domínio",

View file

@ -160,6 +160,7 @@
"contact_name": "Имя контакта",
"contact_support": "Связаться со службой поддержки",
"continue_text": "Продолжить",
"contract_warning": "Этот адрес контракта был отмечен как потенциально мошеннический. Пожалуйста, обработайтесь с осторожностью.",
"contractName": "Название контракта",
"contractSymbol": "Символ контракта",
"copied_key_to_clipboard": "Скопировано ${key} в буфер обмена",
@ -219,6 +220,7 @@
"displayable": "Отображаемый",
"do_not_have_enough_gas_asset": "У вас недостаточно ${currency} для совершения транзакции при текущих условиях сети блокчейн. Вам нужно больше ${currency} для оплаты комиссий за сеть блокчейна, даже если вы отправляете другой актив.",
"do_not_send": "Не отправлять",
"do_not_send_funds_to_contract_address_warning": "Не отправляйте средства на этот адрес \n\n Это просто идентификатор для токена, любые средства, отправленные на этот адрес, будут потеряны. \n\n Примечание: торт никогда не попросит вас добавить адрес контракта",
"do_not_share_warning_text": "Не сообщайте их никому, включая техподдержку.\n\nВаши средства могут и будут украдены!",
"do_not_show_me": "Не показывай мне это больше",
"domain_looks_up": "Поиск доменов",

View file

@ -160,6 +160,7 @@
"contact_name": "ชื่อผู้ติดต่อ",
"contact_support": "ติดต่อฝ่ายสนับสนุน",
"continue_text": "ดำเนินการต่อ",
"contract_warning": "ที่อยู่สัญญานี้ได้รับการตั้งค่าสถานะว่าเป็นการฉ้อโกง กรุณาดำเนินการด้วยความระมัดระวัง",
"contractName": "ชื่อสัญญา",
"contractSymbol": "สัญลักษณ์สัญญา",
"copied_key_to_clipboard": "คัดลอก ${key} ไปยัง Clipboard แล้ว",
@ -219,6 +220,7 @@
"displayable": "สามารถแสดงได้",
"do_not_have_enough_gas_asset": "คุณมี ${currency} ไม่เพียงพอที่จะทำธุรกรรมกับเงื่อนไขเครือข่ายบล็อคเชนในปัจจุบัน คุณต้องมี ${currency} เพิ่มขึ้นเพื่อชำระค่าธรรมเนียมเครือข่ายบล็อคเชน แม้ว่าคุณจะส่งสินทรัพย์อื่นก็ตาม",
"do_not_send": "อย่าส่ง",
"do_not_send_funds_to_contract_address_warning": "อย่าส่งเงินไปยังที่อยู่นี้ \n\n นี่เป็นเพียงตัวระบุสำหรับโทเค็นเงินทุนใด ๆ ที่ส่งไปยังที่อยู่นี้จะหายไป \n\n หมายเหตุ: เค้กจะไม่ขอให้คุณเพิ่มที่อยู่สัญญา",
"do_not_share_warning_text": "อย่าแชร์ข้อมูลนี้กับใครอื่น รวมถึงฝ่ายสนับสนุนด้วย\n\nการเงินของคุณอาจถูกขโมยโดยไม่หวังดี!",
"do_not_show_me": "อย่าแสดงข้อความนี้อีก",
"domain_looks_up": "การค้นหาโดเมน",

View file

@ -160,6 +160,7 @@
"contact_name": "Pangalan ng Contact",
"contact_support": "Makipag-ugnay sa Suporta",
"continue_text": "Magpatuloy",
"contract_warning": "Ang address ng kontrata na ito ay na -flag bilang potensyal na mapanlinlang. Mangyaring iproseso nang may pag -iingat.",
"contractName": "Pangalan ng Kontrata",
"contractSymbol": "Simbolo ng Kontrata",
"copied_key_to_clipboard": "Kinopya ang ${key} sa Clipboard",
@ -219,6 +220,7 @@
"displayable": "Maipapakita",
"do_not_have_enough_gas_asset": "Wala kang sapat na ${currency} para gumawa ng transaksyon sa kasalukuyang kundisyon ng network ng blockchain. Kailangan mo ng higit pang ${currency} upang magbayad ng mga fee sa network ng blockchain, kahit na nagpapadala ka ng ibang asset.",
"do_not_send": "Huwag ipadala",
"do_not_send_funds_to_contract_address_warning": "Huwag magpadala ng pondo sa address na ito\n\n Ito ay isang identifier lamang para sa token, ang anumang pondo na ipinadala sa address na ito ay mawawala.\n\n Tandaan: Ang cake ay hindi hihilingin sa iyo na magdagdag ng isang address ng kontrata",
"do_not_share_warning_text": "Huwag ibahagi ang mga ito sa sinuman kasama ang tagatustos.\n\nMaaaring manakaw ang iyong mga pondo!",
"do_not_show_me": "Huwag mo itong ipakita muli",
"domain_looks_up": "Mga paghahanap ng domain",

View file

@ -160,6 +160,7 @@
"contact_name": "Kişi ismi",
"contact_support": "Destek ile İletişime Geç",
"continue_text": "Devam et",
"contract_warning": "Bu sözleşme adresi potansiyel olarak hileli olarak işaretlenmiştir. Lütfen dikkatle işleyin.",
"contractName": "Sözleşme Adı",
"contractSymbol": "Sözleşme Sembolü",
"copied_key_to_clipboard": "${key} panoya kopyalandı",
@ -219,6 +220,7 @@
"displayable": "Gösterilebilir",
"do_not_have_enough_gas_asset": "Mevcut blockchain ağ koşullarıyla işlem yapmak için yeterli ${currency} paranız yok. Farklı bir varlık gönderiyor olsanız bile blockchain ağ ücretlerini ödemek için daha fazla ${currency} miktarına ihtiyacınız var.",
"do_not_send": "Gönderme",
"do_not_send_funds_to_contract_address_warning": "Bu adrese fon göndermeyin \n\n Bu sadece jeton için bir tanımlayıcıdır, bu adrese gönderilen fonlar kaybolacaktır. \n\n Not: Kekten asla bir sözleşme adresi eklemenizi istemez",
"do_not_share_warning_text": "Bunları destek de dahil olmak üzere başka kimseyle paylaşma.\n\nParan çalınabilir ve çalınacaktır!",
"do_not_show_me": "Bana bunu bir daha gösterme",
"domain_looks_up": "Etki alanı aramaları",

View file

@ -160,6 +160,7 @@
"contact_name": "Ім'я контакту",
"contact_support": "Звернутися до служби підтримки",
"continue_text": "Продовжити",
"contract_warning": "Ця адреса контракту була позначена як потенційно шахрайська. Будь ласка, обробляйте обережно.",
"contractName": "Назва контракту",
"contractSymbol": "Контракт символ",
"copied_key_to_clipboard": "Скопійовано ${key} в буфер обміну",
@ -219,6 +220,7 @@
"displayable": "Відображуваний",
"do_not_have_enough_gas_asset": "У вас недостатньо ${currency}, щоб здійснити трансакцію з поточними умовами мережі блокчейн. Вам потрібно більше ${currency}, щоб сплатити комісію мережі блокчейн, навіть якщо ви надсилаєте інший актив.",
"do_not_send": "Не надсилайте",
"do_not_send_funds_to_contract_address_warning": "Не надсилайте кошти на цю адресу \n\n Це лише ідентифікатор для маркера, будь -які кошти, надіслані на цю адресу",
"do_not_share_warning_text": "Не діліться цим нікому, включно зі службою підтримки.\n\nВаші кошти можуть і будуть вкрадені!",
"do_not_show_me": "Не показуй мені це знову",
"domain_looks_up": "Пошук доменів",

View file

@ -160,6 +160,7 @@
"contact_name": "رابطے کا نام",
"contact_support": "سپورٹ سے رابطہ کریں۔",
"continue_text": "جاری رہے",
"contract_warning": "اس معاہدے کے پتے کو ممکنہ طور پر جعلی قرار دیا گیا ہے۔ براہ کرم احتیاط کے ساتھ کارروائی کریں۔",
"contractName": "ﻡﺎﻧ ﺎﮐ ﮦﺪﮨﺎﻌﻣ",
"contractSymbol": "ﺖﻣﻼﻋ ﯽﮐ ﮦﺪﮨﺎﻌﻣ",
"copied_key_to_clipboard": "${key} کو کلپ بورڈ پر کاپی کیا گیا۔",
@ -219,6 +220,7 @@
"displayable": "قابل نمائش",
"do_not_have_enough_gas_asset": "آپ کے پاس موجودہ بلاکچین نیٹ ورک کی شرائط کے ساتھ لین دین کرنے کے لیے کافی ${currency} نہیں ہے۔ آپ کو بلاکچین نیٹ ورک کی فیس ادا کرنے کے لیے مزید ${currency} کی ضرورت ہے، چاہے آپ کوئی مختلف اثاثہ بھیج رہے ہوں۔",
"do_not_send": "مت بھیجیں۔",
"do_not_send_funds_to_contract_address_warning": "اس پتے پر فنڈز نہ بھیجیں \n\n یہ ٹوکن کے لئے صرف ایک شناخت کنندہ ہے ، اس پتے پر بھیجے گئے کوئی بھی فنڈز ضائع ہوجائیں گے۔ \n\n نوٹ: کیک آپ کو کبھی بھی معاہدہ کا پتہ شامل کرنے کے لئے نہیں کہے گا۔",
"do_not_share_warning_text": "سپورٹ سمیت کسی اور کے ساتھ ان کا اشتراک نہ کریں۔\\n\\nآپ کے فنڈز چوری ہو سکتے ہیں اور ہو جائیں گے!",
"do_not_show_me": "مجھے یہ دوبارہ مت دکھانا",
"domain_looks_up": "ڈومین تلاش کرنا",

900
res/values/strings_vi.arb Normal file
View file

@ -0,0 +1,900 @@
{
"about_cake_pay": "Cake Pay cho phép bạn dễ dàng mua thẻ quà tặng bằng tài sản ảo, có thể sử dụng ngay lập tức tại hơn 150.000 cửa hàng tại Hoa Kỳ.",
"account": "Tài khoản",
"accounts": "Tài khoản",
"accounts_subaddresses": "Tài khoản và địa chỉ phụ",
"activate": "Kích hoạt",
"active": "Đang hoạt động",
"active_cards": "Thẻ đang hoạt động",
"activeConnectionsPrompt": "Các kết nối đang hoạt động sẽ xuất hiện ở đây",
"add": "Thêm",
"add_contact": "Thêm liên hệ",
"add_contact_to_address_book": "Bạn có muốn thêm liên hệ này vào sổ địa chỉ không?",
"add_custom_node": "Thêm nút tùy chỉnh mới",
"add_custom_redemption": "Thêm đổi thưởng tùy chỉnh",
"add_fund_to_card": "Thêm tiền trả trước vào thẻ (tối đa ${value})",
"add_new_node": "Thêm nút mới",
"add_new_word": "Thêm từ mới",
"add_receiver": "Thêm người nhận khác (tùy chọn)",
"add_secret_code": "Hoặc, thêm mã bí mật này vào ứng dụng xác thực",
"add_tip": "Thêm tiền boa",
"add_token_disclaimer_check": "Tôi đã xác nhận địa chỉ hợp đồng token và thông tin bằng nguồn đáng tin cậy. Thêm thông tin sai hoặc độc hại có thể dẫn đến mất tiền.",
"add_token_warning": "Không chỉnh sửa hoặc thêm token theo yêu cầu của kẻ lừa đảo.\nLuôn xác nhận địa chỉ token từ các nguồn đáng tin cậy!",
"add_value": "Thêm giá trị",
"address": "Địa chỉ",
"address_book": "Sổ địa chỉ",
"address_book_menu": "Sổ địa chỉ",
"address_detected": "Địa chỉ được phát hiện",
"address_from_domain": "Địa chỉ này đến từ ${domain} trên Unstoppable Domains",
"address_from_yat": "Địa chỉ này đến từ ${emoji} trên Yat",
"address_label": "Nhãn địa chỉ",
"address_remove_contact": "Xóa liên hệ",
"address_remove_content": "Bạn có chắc chắn muốn xóa liên hệ đã chọn không?",
"addresses": "Địa chỉ",
"advanced_settings": "Cài đặt nâng cao",
"aggressive": "Tích cực",
"agree": "Đồng ý",
"agree_and_continue": "Đồng ý & Tiếp tục",
"agree_to": "Bằng cách tạo tài khoản, bạn đồng ý với ",
"all": "TẤT CẢ",
"all_trades": "Tất cả giao dịch",
"all_transactions": "Tất cả giao dịch",
"alphabetical": "Theo thứ tự chữ cái",
"already_have_account": "Bạn đã có tài khoản?",
"always": "Luôn luôn",
"amount": "Số tiền: ",
"amount_is_below_minimum_limit": "Số dư của bạn sau khi trừ phí sẽ thấp hơn số tiền tối thiểu cần thiết cho giao dịch (${min})",
"amount_is_estimate": "Số tiền nhận được là ước tính",
"amount_is_guaranteed": "Số tiền nhận được được đảm bảo",
"and": "và",
"anonpay_description": "Tạo ${type}. Người nhận có thể ${method} với bất kỳ loại tiền điện tử nào được hỗ trợ, và bạn sẽ nhận được tiền trong ví này.",
"apk_update": "Cập nhật APK",
"approve": "Phê duyệt",
"arrive_in_this_address": "${currency} ${tag} sẽ đến địa chỉ này",
"ascending": "Tăng dần",
"ask_each_time": "Hỏi mỗi lần",
"auth_store_ban_timeout": "thời gian cấm",
"auth_store_banned_for": "Bị cấm trong ",
"auth_store_banned_minutes": " phút",
"auth_store_incorrect_password": "Sai PIN",
"authenticated": "Đã xác thực",
"authentication": "Xác thực",
"auto_generate_addresses": "Tự động tạo địa chỉ",
"auto_generate_subaddresses": "Tự động tạo địa chỉ phụ",
"automatic": "Tự động",
"available_balance": "Số dư khả dụng",
"available_balance_description": "Số dư khả dụng hoặc số dư đã xác nhận là số tiền có thể chi tiêu ngay lập tức. Nếu tiền xuất hiện ở số dư thấp hơn nhưng không phải ở số dư cao hơn, bạn phải đợi một vài phút để các khoản tiền đến có thêm xác nhận từ mạng lưới. Sau khi có thêm xác nhận, chúng sẽ có thể chi tiêu.",
"avg_savings": "Tiết kiệm trung bình",
"awaitDAppProcessing": "Vui lòng đợi ứng dụng phi tập trung hoàn thành xử lý.",
"awaiting_payment_confirmation": "Đang chờ xác nhận thanh toán",
"background_sync_mode": "Chế độ đồng bộ nền",
"backup": "Sao lưu",
"backup_file": "Tập tin sao lưu",
"backup_password": "Mật khẩu sao lưu",
"balance": "Số dư",
"balance_page": "Trang số dư",
"bill_amount": "Số tiền hóa đơn",
"billing_address_info": "Nếu được yêu cầu địa chỉ thanh toán, hãy cung cấp địa chỉ giao hàng của bạn",
"biometric_auth_reason": "Quét vân tay để xác thực",
"bitcoin_dark_theme": "Chủ đề Bitcoin tối",
"bitcoin_light_theme": "Chủ đề Bitcoin sáng",
"bitcoin_payments_require_1_confirmation": "Các khoản thanh toán Bitcoin yêu cầu 1 xác nhận, có thể mất 20 phút hoặc lâu hơn. Cảm ơn bạn đã kiên nhẫn! Bạn sẽ nhận được email khi thanh toán được xác nhận.",
"block_remaining": "1 khối còn lại",
"Blocks_remaining": "${status} khối còn lại",
"bluetooth": "Bluetooth",
"bright_theme": "Sáng",
"bump_fee": "Tăng phí",
"buy": "Mua",
"buy_alert_content": "Hiện tại chúng tôi chỉ hỗ trợ mua Bitcoin, Ethereum, Litecoin, và Monero. Vui lòng tạo hoặc chuyển sang ví Bitcoin, Ethereum, Litecoin, hoặc Monero của bạn.",
"buy_bitcoin": "Mua Bitcoin",
"buy_now": "Mua ngay",
"buy_provider_unavailable": "Nhà cung cấp hiện không khả dụng.",
"buy_with": "Mua bằng",
"by_cake_pay": "bởi Cake Pay",
"cake_2fa_preset": "Thiết lập sẵn Cake 2FA",
"cake_dark_theme": "Chủ đề Cake tối",
"cake_pay_account_note": "Đăng ký chỉ với một địa chỉ email để xem và mua thẻ. Một số thẻ còn được giảm giá!",
"cake_pay_learn_more": "Mua và đổi thẻ quà tặng ngay trong ứng dụng!\nVuốt từ trái sang phải để tìm hiểu thêm.",
"cake_pay_save_order": "Thẻ sẽ được gửi đến email của bạn trong vòng 1 ngày làm việc \n Lưu mã Đơn hàng của bạn:",
"cake_pay_subtitle": "Mua thẻ trả trước toàn cầu và thẻ quà tặng",
"cake_pay_web_cards_subtitle": "Mua thẻ trả trước toàn cầu và thẻ quà tặng",
"cake_pay_web_cards_title": "Thẻ Cake Pay Web",
"cake_wallet": "Ví Cake",
"cakepay_prepaid_card": "Thẻ Ghi Nợ Trả Trước CakePay",
"camera_consent": "Máy ảnh của bạn sẽ được sử dụng để chụp hình nhằm mục đích xác minh danh tính bởi ${provider}. Vui lòng kiểm tra Chính sách quyền riêng tư của họ để biết thêm chi tiết.",
"camera_permission_is_required": "Cần có quyền truy cập máy ảnh. \nVui lòng bật nó từ cài đặt ứng dụng.",
"cancel": "Hủy",
"card_address": "Địa chỉ:",
"cardholder_agreement": "Thỏa thuận Chủ Thẻ",
"cards": "Thẻ",
"chains": "Chuỗi",
"change": "Thay đổi",
"change_backup_password_alert": "Các tệp sao lưu trước đây sẽ không còn khả dụng để nhập với mật khẩu sao lưu mới. Mật khẩu sao lưu mới chỉ được sử dụng cho các tệp sao lưu mới. Bạn có chắc chắn muốn thay đổi mật khẩu sao lưu không?",
"change_currency": "Thay đổi Tiền tệ",
"change_current_node": "Bạn có chắc chắn muốn thay đổi nút hiện tại sang ${node} không?",
"change_current_node_title": "Thay đổi nút hiện tại",
"change_exchange_provider": "Thay đổi Nhà cung cấp Trao đổi",
"change_language": "Thay đổi ngôn ngữ",
"change_language_to": "Thay đổi ngôn ngữ sang ${language}?",
"change_password": "Thay đổi mật khẩu",
"change_rep": "Thay đổi Đại diện",
"change_rep_message": "Bạn có chắc chắn muốn thay đổi đại diện không?",
"change_rep_successful": "Thay đổi đại diện thành công",
"change_wallet_alert_content": "Bạn có muốn thay đổi ví hiện tại thành ${wallet_name} không?",
"change_wallet_alert_title": "Thay đổi ví hiện tại",
"choose_account": "Chọn tài khoản",
"choose_address": "\n\nVui lòng chọn địa chỉ:",
"choose_card_value": "Chọn giá trị thẻ",
"choose_derivation": "Chọn Đường dẫn Ví",
"choose_from_available_options": "Chọn từ các tùy chọn có sẵn:",
"choose_one": "Chọn một",
"choose_relay": "Vui lòng chọn một relay để sử dụng",
"choose_wallet_currency": "Vui lòng chọn tiền tệ của ví:",
"clear": "Xóa",
"clearnet_link": "Liên kết Clearnet",
"close": "Đóng",
"coin_control": "Kiểm soát đồng xu (tùy chọn)",
"cold_or_recover_wallet": "Thêm ví lạnh hoặc khôi phục ví giấy",
"color_theme": "Chủ đề màu sắc",
"commit_transaction_amount_fee": "Cam kết giao dịch\nSố tiền: ${amount}\nPhí: ${fee}",
"confirm": "Xác nhận",
"confirm_delete_template": "Thao tác này sẽ xóa mẫu này. Bạn có muốn tiếp tục không?",
"confirm_delete_wallet": "Thao tác này sẽ xóa ví này. Bạn có muốn tiếp tục không?",
"confirm_fee_deduction": "Xác nhận Khấu trừ Phí",
"confirm_fee_dedction_content": "Bạn có đồng ý trừ phí từ đầu ra không?",
"confirm_sending": "Xác nhận gửi",
"confirm_silent_payments_switch_node": "Nút hiện tại của bạn không hỗ trợ thanh toán im lặng\\nCake Wallet sẽ chuyển sang một nút tương thích chỉ để quét",
"confirmations": "Xác nhận",
"confirmed": "Số dư đã xác nhận",
"confirmed_tx": "Đã xác nhận",
"congratulations": "Chúc mừng!",
"connect_an_existing_yat": "Kết nối Yat hiện có",
"connect_yats": "Kết nối Yats",
"connect_your_hardware_wallet": "Kết nối ví phần cứng của bạn bằng Bluetooth hoặc USB",
"connect_your_hardware_wallet_ios": "Kết nối ví phần cứng của bạn bằng Bluetooth",
"connection_sync": "Kết nối và đồng bộ hóa",
"connectWalletPrompt": "Kết nối ví của bạn với WalletConnect để thực hiện giao dịch",
"contact": "Liên hệ",
"contact_list_contacts": "Danh bạ",
"contact_list_wallets": "Ví của tôi",
"contact_name": "Tên liên hệ",
"contact_support": "Liên hệ Hỗ trợ",
"continue_text": "Tiếp tục",
"contractName": "Tên Hợp đồng",
"contractSymbol": "Ký hiệu Hợp đồng",
"copied_key_to_clipboard": "Đã sao chép ${key} vào khay nhớ tạm",
"copied_to_clipboard": "Đã sao chép vào khay nhớ tạm",
"copy": "Sao chép",
"copy_address": "Sao chép Địa chỉ",
"copy_id": "Sao chép ID",
"copyWalletConnectLink": "Sao chép liên kết WalletConnect từ dApp và dán vào đây",
"countries": "Quốc gia",
"create_account": "Tạo tài khoản",
"create_backup": "Tạo sao lưu",
"create_donation_link": "Tạo liên kết quyên góp",
"create_invoice": "Tạo hóa đơn",
"create_new": "Tạo Ví Mới",
"create_new_account": "Tạo tài khoản mới",
"creating_new_wallet": "Đang tạo ví mới",
"creating_new_wallet_error": "Lỗi: ${description}",
"creation_date": "Ngày Tạo",
"custom": "Tùy chỉnh",
"custom_drag": "Tùy chỉnh (Giữ và Kéo)",
"custom_redeem_amount": "Số tiền Chuộc Tùy chỉnh",
"custom_value": "Giá trị Tùy chỉnh",
"dark_theme": "Tối",
"debit_card": "Thẻ Ghi Nợ",
"debit_card_terms": "Việc lưu trữ và sử dụng số thẻ thanh toán của bạn (và thông tin xác thực tương ứng với số thẻ thanh toán của bạn) trong ví điện tử này phải tuân theo Điều khoản và Điều kiện của thỏa thuận chủ thẻ hiện hành với tổ chức phát hành thẻ thanh toán, theo thời gian.",
"decimal_places_error": "Quá nhiều chữ số thập phân",
"decimals_cannot_be_zero": "Chữ số thập phân không thể là số không.",
"default_buy_provider": "Nhà cung cấp Mua mặc định",
"default_sell_provider": "Nhà cung cấp Bán mặc định",
"delete": "Xóa",
"delete_account": "Xóa Tài khoản",
"delete_wallet": "Xóa ví",
"delete_wallet_confirm_message": "Bạn có chắc chắn muốn xóa ví ${wallet_name} không?",
"deleteConnectionConfirmationPrompt": "Bạn có chắc chắn muốn xóa kết nối với",
"denominations": "Mệnh giá",
"derivationpath": "Đường dẫn Derivation",
"descending": "Giảm dần",
"description": "Mô tả",
"destination_tag": "Thẻ đích:",
"dfx_option_description": "Mua tiền điện tử bằng EUR & CHF. Dành cho khách hàng bán lẻ và doanh nghiệp tại Châu Âu",
"didnt_get_code": "Không nhận được mã?",
"digit_pin": "Mã PIN - số",
"digital_and_physical_card": "thẻ ghi nợ trả trước kỹ thuật số và vật lý",
"disable": "Vô hiệu hóa",
"disable_bulletin": "Vô hiệu hóa bản tin tình trạng dịch vụ",
"disable_buy": "Vô hiệu hóa chức năng mua",
"disable_cake_2fa": "Vô hiệu hóa 2FA Cake",
"disable_exchange": "Vô hiệu hóa chức năng trao đổi",
"disable_fee_api_warning": "Khi tắt chức năng này, tỉ lệ phí có thể không chính xác trong một số trường hợp, dẫn đến bạn trả quá hoặc không đủ phí cho giao dịch của mình.",
"disable_fiat": "Vô hiệu hóa tiền tệ fiat",
"disable_sell": "Vô hiệu hóa chức năng bán",
"disableBatteryOptimization": "Vô hiệu hóa Tối ưu hóa Pin",
"disableBatteryOptimizationDescription": "Bạn có muốn vô hiệu hóa tối ưu hóa pin để đồng bộ hóa nền hoạt động mượt mà hơn không?",
"disabled": "Đã vô hiệu hóa",
"discount": "Tiết kiệm ${value}%",
"display_settings": "Cài đặt hiển thị",
"displayable": "Có thể hiển thị",
"do_not_have_enough_gas_asset": "Bạn không có đủ ${currency} để thực hiện giao dịch với điều kiện mạng blockchain hiện tại. Bạn cần thêm ${currency} để trả phí mạng blockchain, ngay cả khi bạn đang gửi tài sản khác.",
"do_not_send": "Đừng gửi",
"do_not_share_warning_text": "Không chia sẻ điều này với bất kỳ ai, bao gồm cả bộ phận hỗ trợ.\n\nTài sản của bạn có thể và sẽ bị đánh cắp!",
"do_not_show_me": "Không hiển thị lại cho tôi",
"domain_looks_up": "Tra cứu tên miền",
"donation_link_details": "Chi tiết liên kết quyên góp",
"e_sign_consent": "Đồng ý Ký Điện tử",
"edit": "Chỉnh sửa",
"edit_backup_password": "Chỉnh sửa mật khẩu sao lưu",
"edit_node": "Chỉnh sửa nút",
"edit_token": "Chỉnh sửa token",
"electrum_address_disclaimer": "Chúng tôi tạo địa chỉ mới mỗi khi bạn sử dụng, nhưng các địa chỉ cũ vẫn tiếp tục hoạt động",
"email_address": "Địa chỉ Email",
"enable_replace_by_fee": "Bật Thay thế Bằng Phí",
"enable_silent_payments_scanning": "Bật quét thanh toán im lặng",
"enabled": "Đã bật",
"enter_amount": "Nhập số tiền",
"enter_backup_password": "Nhập mật khẩu sao lưu tại đây",
"enter_code": "Nhập mã",
"enter_seed_phrase": "Nhập cụm từ hạt giống của bạn",
"enter_totp_code": "Vui lòng nhập mã TOTP.",
"enter_wallet_password": "Nhập mật khẩu ví",
"enter_your_note": "Nhập ghi chú của bạn...",
"enter_your_pin": "Nhập mã PIN của bạn",
"enter_your_pin_again": "Nhập lại mã PIN của bạn",
"enterTokenID": "Nhập ID token",
"enterWalletConnectURI": "Nhập URI WalletConnect",
"error": "Lỗi",
"error_dialog_content": "Oops, chúng tôi gặp một số lỗi.\n\nVui lòng gửi báo cáo sự cố cho nhóm hỗ trợ của chúng tôi để cải thiện ứng dụng.",
"error_text_account_name": "Tên tài khoản chỉ được chứa chữ cái, số\nvà phải từ 1 đến 15 ký tự",
"error_text_address": "Địa chỉ ví phải tương ứng với loại tiền điện tử",
"error_text_amount": "Số tiền chỉ được chứa số",
"error_text_contact_name": "Tên liên hệ không được chứa ký tự ` , ' \"\nvà phải từ 1 đến 32 ký tự",
"error_text_crypto_currency": "Số chữ số thập phân phải ít hơn hoặc bằng 12",
"error_text_fiat": "Giá trị số tiền không được vượt quá số dư khả dụng.\nSố chữ số thập phân phải ít hơn hoặc bằng 2",
"error_text_input_above_maximum_limit": "Số tiền vượt quá giới hạn tối đa",
"error_text_input_below_minimum_limit": "Số tiền dưới mức tối thiểu",
"error_text_keys": "Khóa ví chỉ được chứa 64 ký tự dạng hex",
"error_text_limits_loading_failed": "Giao dịch cho ${provider} không được tạo. Tải giới hạn không thành công",
"error_text_maximum_limit": "Giao dịch cho ${provider} không được tạo. Số tiền lớn hơn mức tối đa: ${max} ${currency}",
"error_text_minimal_limit": "Giao dịch cho ${provider} không được tạo. Số tiền nhỏ hơn mức tối thiểu: ${min} ${currency}",
"error_text_node_address": "Vui lòng nhập địa chỉ iPv4",
"error_text_node_port": "Cổng nút chỉ được chứa các số từ 0 đến 65535",
"error_text_node_proxy_address": "Vui lòng nhập <địa chỉ IPv4>:<cổng>, ví dụ 127.0.0.1:9050",
"error_text_payment_id": "ID thanh toán chỉ được chứa từ 16 đến 64 ký tự dạng hex",
"error_text_subaddress_name": "Tên địa chỉ phụ không được chứa ký tự ` , ' \"\nvà phải từ 1 đến 20 ký tự",
"error_text_template": "Tên và địa chỉ mẫu không được chứa ký tự ` , ' \"\nvà phải từ 1 đến 106 ký tự",
"error_text_wallet_name": "Tên ví chỉ được chứa chữ cái, số, ký hiệu _ -\nvà phải từ 1 đến 33 ký tự",
"error_text_xmr": "Giá trị XMR không được vượt quá số dư khả dụng.\nSố chữ số thập phân phải ít hơn hoặc bằng 12",
"errorGettingCredentials": "Không thành công: Lỗi khi nhận thông tin xác thực",
"errorSigningTransaction": "Đã xảy ra lỗi khi ký giao dịch",
"estimated": "Ước tính",
"estimated_new_fee": "Phí mới ước tính",
"estimated_receive_amount": "Số tiền nhận ước tính",
"etherscan_history": "Lịch sử Etherscan",
"event": "Sự kiện",
"events": "Các sự kiện",
"exchange": "Trao đổi",
"exchange_incorrect_current_wallet_for_xmr": "Nếu bạn muốn trao đổi XMR từ số dư Monero của Ví Cake, vui lòng chuyển sang ví Monero của bạn trước.",
"exchange_new_template": "Mẫu mới",
"exchange_provider_unsupported": "${providerName} không còn được hỗ trợ nữa!",
"exchange_result_confirm": "Bằng cách nhấn xác nhận, bạn sẽ gửi ${fetchingLabel} ${from} từ ví có tên ${walletName} của mình đến địa chỉ dưới đây. Hoặc bạn có thể gửi từ ví bên ngoài của mình đến địa chỉ/mã QR bên dưới.\n\nVui lòng nhấn xác nhận để tiếp tục hoặc quay lại để thay đổi số tiền.",
"exchange_result_description": "Bạn phải gửi tối thiểu ${fetchingLabel} ${from} đến địa chỉ hiển thị trên trang tiếp theo. Nếu bạn gửi số tiền thấp hơn ${fetchingLabel} ${from} thì có thể không được chuyển đổi và không được hoàn trả.",
"exchange_result_write_down_ID": "*Vui lòng sao chép hoặc ghi lại ID hiển thị ở trên.",
"exchange_result_write_down_trade_id": "Vui lòng sao chép hoặc ghi lại ID giao dịch để tiếp tục.",
"exchange_sync_alert_content": "Vui lòng chờ cho đến khi ví của bạn được đồng bộ hóa",
"expired": "Đã hết hạn",
"expires": "Hết hạn",
"expiresOn": "Hết hạn vào",
"expiry_and_validity": "Hạn và hiệu lực",
"export_backup": "Xuất sao lưu",
"extra_id": "ID bổ sung:",
"extracted_address_content": "Bạn sẽ gửi tiền cho\n${recipient_name}",
"failed_authentication": "Xác thực không thành công. ${state_error}",
"faq": "FAQ",
"features": "Tính năng",
"fetching": "Đang tải",
"fiat_api": "API Fiat",
"fiat_balance": "Số dư Fiat",
"field_required": "Trường này là bắt buộc",
"fill_code": "Vui lòng điền mã xác minh được gửi đến email của bạn",
"filter_by": "Lọc theo",
"first_wallet_text": "Ví tuyệt vời cho Monero, Bitcoin, Ethereum, Litecoin, và Haven",
"fixed_pair_not_supported": "Cặp tỷ giá cố định này không được hỗ trợ với các sàn giao dịch đã chọn",
"fixed_rate": "Tỷ giá cố định",
"fixed_rate_alert": "Bạn sẽ có thể nhập số lượng nhận được khi chế độ tỷ giá cố định được chọn. Bạn có muốn chuyển sang chế độ tỷ giá cố định không?",
"forgot_password": "Quên mật khẩu",
"freeze": "Đóng băng",
"frequently_asked_questions": "Các câu hỏi thường gặp",
"frozen": "Đã đóng băng",
"full_balance": "Số dư đầy đủ",
"generate_name": "Tạo tên",
"generating_gift_card": "Đang tạo thẻ quà tặng",
"get_a": "Nhận một ",
"get_card_note": " mà bạn có thể nạp lại bằng tiền điện tử. Không cần thêm thông tin!",
"get_your_yat": "Nhận Yat của bạn",
"gift_card_amount": "Số tiền thẻ quà tặng",
"gift_card_balance_note": "Các thẻ quà tặng còn số dư sẽ xuất hiện ở đây",
"gift_card_is_generated": "Thẻ quà tặng đã được tạo",
"gift_card_number": "Số thẻ quà tặng",
"gift_card_redeemed_note": "Thẻ quà tặng bạn đã quy đổi sẽ xuất hiện ở đây",
"gift_cards": "Thẻ quà tặng",
"gift_cards_unavailable": "Thẻ quà tặng chỉ có thể mua bằng Monero, Bitcoin, và Litecoin vào lúc này",
"got_it": "Đã hiểu",
"gross_balance": "Số dư gộp",
"group_by_type": "Nhóm theo loại",
"haven_app": "Haven bởi Cake Wallet",
"haven_app_wallet_text": "Ví tuyệt vời cho Haven",
"help": "Trợ giúp",
"hidden_balance": "Số dư ẩn",
"hide_details": "Ẩn chi tiết",
"high_contrast_theme": "Chủ đề độ tương phản cao",
"home_screen_settings": "Cài đặt màn hình chính",
"how_to_use": "Cách sử dụng",
"how_to_use_card": "Cách sử dụng thẻ này",
"id": "ID: ",
"ignor": "Bỏ qua",
"import": "Nhập",
"importNFTs": "Nhập NFT",
"in_store": "Tại cửa hàng",
"incoming": "Đang nhận",
"incorrect_seed": "Văn bản nhập không hợp lệ.",
"inputs": "Đầu vào",
"insufficient_lamport_for_tx": "Bạn không có đủ SOL để thanh toán giao dịch và phí giao dịch. Vui lòng thêm SOL vào ví của bạn hoặc giảm số lượng SOL bạn đang gửi.",
"insufficient_lamports": "Bạn không có đủ SOL để thanh toán giao dịch và phí giao dịch. Bạn cần ít nhất ${solValueNeeded} SOL. Vui lòng thêm SOL vào ví của bạn hoặc giảm số lượng SOL bạn đang gửi",
"insufficientFundsForRentError": "Bạn không có đủ SOL để thanh toán phí giao dịch và phí thuê cho tài khoản. Vui lòng thêm SOL vào ví của bạn hoặc giảm số lượng SOL bạn đang gửi",
"introducing_cake_pay": "Giới thiệu Cake Pay!",
"invalid_input": "Nhập không hợp lệ",
"invalid_password": "Mật khẩu không hợp lệ",
"invoice_details": "Chi tiết hóa đơn",
"is_percentage": "là",
"last_30_days": "30 ngày gần nhất",
"learn_more": "Tìm hiểu thêm",
"ledger_connection_error": "Không thể kết nối với Ledger của bạn. Vui lòng thử lại.",
"ledger_error_device_locked": "Ledger đã bị khóa",
"ledger_error_tx_rejected_by_user": "Giao dịch bị từ chối trên thiết bị",
"ledger_error_wrong_app": "Vui lòng đảm bảo bạn đã mở đúng ứng dụng trên Ledger của mình",
"ledger_please_enable_bluetooth": "Vui lòng bật Bluetooth để phát hiện Ledger của bạn",
"light_theme": "Chủ đề sáng",
"live_fee_rates": "Tỷ lệ phí hiện tại qua API",
"load_more": "Tải thêm",
"loading_your_wallet": "Đang tải ví của bạn",
"login": "Đăng nhập",
"logout": "Đăng xuất",
"low_fee": "Phí thấp",
"low_fee_alert": "Bạn hiện đang sử dụng mức ưu tiên phí mạng thấp. Điều này có thể dẫn đến thời gian chờ lâu, tỷ giá khác nhau, hoặc giao dịch bị hủy bỏ. Chúng tôi khuyến nghị bạn đặt mức phí cao hơn để có trải nghiệm tốt hơn.",
"manage_nodes": "Quản lý các nút",
"manage_pow_nodes": "Quản lý các nút PoW",
"manage_yats": "Quản lý Yats",
"mark_as_redeemed": "Đánh dấu là đã đổi",
"market_place": "Thị trường",
"matrix_green_dark_theme": "Chủ đề tối Matrix Green",
"max_amount": "Tối đa: ${value}",
"max_value": "Tối đa: ${value} ${currency}",
"memo": "Ghi chú:",
"message": "Tin nhắn",
"message_verified": "Tin nhắn đã được xác minh thành công",
"methods": "Phương pháp",
"min_amount": "Tối thiểu: ${value}",
"min_value": "Tối thiểu: ${value} ${currency}",
"minutes_to_pin_code": "${minute} phút",
"mm": "MM",
"modify_2fa": "Chỉnh sửa Cake 2FA",
"monero_com": "Monero.com bởi Cake Wallet",
"monero_com_wallet_text": "Ví tuyệt vời cho Monero",
"monero_dark_theme": "Chủ đề tối Monero",
"monero_light_theme": "Chủ đề sáng Monero",
"moonpay_alert_text": "Giá trị số tiền phải lớn hơn hoặc bằng ${minAmount} ${fiatCurrency}",
"more_options": "Thêm tùy chọn",
"name": "Tên",
"nano_current_rep": "Đại diện hiện tại",
"nano_gpt_thanks_message": "Cảm ơn bạn đã sử dụng NanoGPT! Hãy nhớ quay lại trình duyệt sau khi giao dịch của bạn hoàn tất!",
"nano_pick_new_rep": "Chọn đại diện mới",
"nanogpt_subtitle": "Tất cả các mẫu mới nhất (GPT-4, Claude).\\nKhông cần đăng ký, thanh toán bằng tiền điện tử.",
"narrow": "Hẹp",
"new_first_wallet_text": "Giữ an toàn cho tiền điện tử của bạn, dễ dàng như ăn bánh",
"new_node_testing": "Đang thử nghiệm nút mới",
"new_subaddress_create": "Tạo",
"new_subaddress_label_name": "Tên nhãn",
"new_subaddress_title": "Địa chỉ mới",
"new_template": "Mẫu mới",
"new_wallet": "Ví mới",
"newConnection": "Kết nối mới",
"no_cards_found": "Không tìm thấy thẻ",
"no_id_needed": "Không cần ID!",
"no_id_required": "Không yêu cầu ID. Nạp tiền và chi tiêu ở bất kỳ đâu",
"no_relay_on_domain": "Không có relay cho miền của người dùng hoặc relay không khả dụng. Vui lòng chọn một relay để sử dụng.",
"no_relays": "Không có relay",
"no_relays_message": "Chúng tôi đã tìm thấy một bản ghi Nostr NIP-05 cho người dùng này, nhưng nó không chứa bất kỳ relay nào. Vui lòng hướng dẫn người nhận thêm relay vào bản ghi Nostr của họ.",
"node_address": "Địa chỉ nút",
"node_connection_failed": "Kết nối không thành công",
"node_connection_successful": "Kết nối thành công",
"node_new": "Nút mới",
"node_port": "Cổng nút",
"node_reset_settings_title": "Đặt lại cài đặt",
"node_test": "Kiểm tra",
"nodes": "Các nút",
"nodes_list_reset_to_default_message": "Bạn có chắc chắn muốn đặt lại cài đặt về mặc định không?",
"none_of_selected_providers_can_exchange": "Không có nhà cung cấp nào đã chọn có thể thực hiện giao dịch này",
"noNFTYet": "Chưa có NFT",
"normal": "Bình thường",
"note_optional": "Ghi chú (tùy chọn)",
"note_tap_to_change": "Ghi chú (nhấn để thay đổi)",
"nullURIError": "URI là null",
"offer_expires_in": "Ưu đãi hết hạn trong: ",
"offline": "Ngoại tuyến",
"ok": "OK",
"old_fee": "Phí cũ",
"onion_link": "Liên kết Onion",
"online": "Trực tuyến",
"onramper_option_description": "Mua tiền điện tử nhanh chóng với nhiều phương thức thanh toán. Có sẵn ở hầu hết các quốc gia. Chênh lệch và phí thay đổi.",
"open_gift_card": "Mở thẻ quà tặng",
"optional_description": "Mô tả tùy chọn",
"optional_email_hint": "Email thông báo cho người nhận (tùy chọn)",
"optional_name": "Tên người nhận (tùy chọn)",
"optionally_order_card": "Có thể đặt thẻ vật lý.",
"orbot_running_alert": "Vui lòng đảm bảo Orbot đang chạy trước khi kết nối với nút này.",
"order_by": "Sắp xếp theo",
"order_id": "ID đơn hàng",
"order_physical_card": "Đặt thẻ vật lý",
"other_settings": "Cài đặt khác",
"outdated_electrum_wallet_description": "Ví Bitcoin mới được tạo trong Cake hiện có hạt giống 24 từ. Bạn cần tạo một ví Bitcoin mới và chuyển tất cả số tiền của bạn vào ví 24 từ mới, và ngừng sử dụng ví có hạt giống 12 từ. Vui lòng thực hiện ngay để bảo vệ tài sản của bạn.",
"outdated_electrum_wallet_receive_warning": "Nếu ví này có hạt giống 12 từ và được tạo trong Cake, ĐỪNG gửi Bitcoin vào ví này. Bất kỳ BTC nào chuyển vào ví này có thể bị mất. Tạo ví 24 từ mới (nhấn menu ở góc trên bên phải, chọn Ví, chọn Tạo Ví Mới, sau đó chọn Bitcoin) và NGAY LẬP TỨC chuyển BTC của bạn vào đó. Ví BTC mới (24 từ) từ Cake là an toàn",
"outgoing": "Đang gửi",
"outputs": "Đầu ra",
"overwrite_amount": "Ghi đè số tiền",
"pairingInvalidEvent": "Sự kiện ghép nối không hợp lệ",
"passphrase": "Cụm từ bảo mật (Tùy chọn)",
"password": "Mật khẩu",
"paste": "Dán",
"pause_wallet_creation": "Khả năng tạo ví Haven hiện đang bị tạm dừng.",
"payment_id": "ID thanh toán: ",
"payment_was_received": "Thanh toán của bạn đã được nhận.",
"pending": " (đang chờ)",
"percentageOf": "của ${amount}",
"pin_at_top": "Ghim ${token} ở trên cùng",
"pin_is_incorrect": "PIN không chính xác",
"pin_number": "Số PIN",
"placeholder_contacts": "Danh bạ của bạn sẽ được hiển thị ở đây",
"placeholder_transactions": "Giao dịch của bạn sẽ được hiển thị ở đây",
"please_fill_totp": "Vui lòng điền mã 8 chữ số trên thiết bị khác của bạn",
"please_make_selection": "Vui lòng chọn một tùy chọn dưới đây để tạo hoặc khôi phục ví của bạn.",
"please_reference_document": "Vui lòng tham khảo các tài liệu dưới đây để biết thêm thông tin.",
"please_select": "Vui lòng chọn:",
"please_select_backup_file": "Vui lòng chọn tệp sao lưu và nhập mật khẩu sao lưu.",
"please_try_to_connect_to_another_node": "Vui lòng thử kết nối với một nút khác",
"please_wait": "Vui lòng chờ",
"polygonscan_history": "Lịch sử PolygonScan",
"powered_by": "Được cung cấp bởi ${title}",
"pre_seed_button_text": "Tôi hiểu. Hiển thị hạt giống của tôi",
"pre_seed_description": "Trên trang tiếp theo, bạn sẽ thấy một chuỗi ${words} từ. Đây là hạt giống riêng tư và duy nhất của bạn và là CÁCH DUY NHẤT để khôi phục ví của bạn trong trường hợp mất hoặc hỏng hóc. Đây là TRÁCH NHIỆM của bạn để ghi lại và lưu trữ nó ở một nơi an toàn ngoài ứng dụng Cake Wallet.",
"pre_seed_title": "QUAN TRỌNG",
"prepaid_cards": "Thẻ trả trước",
"prevent_screenshots": "Ngăn chặn ảnh chụp màn hình và ghi hình màn hình",
"privacy": "Quyền riêng tư",
"privacy_policy": "Chính sách quyền riêng tư",
"privacy_settings": "Cài đặt quyền riêng tư",
"private_key": "Khóa riêng",
"proceed_after_one_minute": "Nếu màn hình không tiếp tục sau 1 phút, hãy kiểm tra email của bạn.",
"proceed_on_device": "Tiếp tục trên thiết bị của bạn",
"proceed_on_device_description": "Vui lòng làm theo các hướng dẫn được nhắc trên ví phần cứng của bạn",
"profile": "Hồ sơ",
"provider_error": "Lỗi ${provider}",
"public_key": "Khóa công khai",
"purchase_gift_card": "Mua thẻ quà tặng",
"purple_dark_theme": "Chủ đề tối tím",
"qr_fullscreen": "Nhấn để mở mã QR toàn màn hình",
"qr_payment_amount": "Mã QR này chứa số tiền thanh toán. Bạn có muốn ghi đè giá trị hiện tại không?",
"quantity": "Số lượng",
"question_to_disable_2fa": "Bạn có chắc chắn muốn tắt Cake 2FA không? Mã 2FA sẽ không còn cần thiết để truy cập ví và một số chức năng nhất định.",
"receivable_balance": "Số dư có thể nhận",
"receive": "Nhận",
"receive_amount": "Số lượng",
"received": "Đã nhận",
"recipient_address": "Địa chỉ người nhận",
"reconnect": "Kết nối lại",
"reconnect_alert_text": "Bạn có chắc chắn muốn kết nối lại không?",
"reconnection": "Kết nối lại",
"red_dark_theme": "Chủ đề tối đỏ",
"red_light_theme": "Chủ đề sáng đỏ",
"redeemed": "Đã đổi",
"refund_address": "Địa chỉ hoàn tiền",
"reject": "Từ chối",
"remaining": "còn lại",
"remove": "Gỡ bỏ",
"remove_node": "Gỡ bỏ nút",
"remove_node_message": "Bạn có chắc chắn muốn gỡ bỏ nút đã chọn không?",
"rename": "Đổi tên",
"rep_warning": "Cảnh báo đại diện",
"rep_warning_sub": "Đại diện của bạn dường như không còn trong tình trạng tốt. Nhấn vào đây để chọn một cái mới",
"repeat_wallet_password": "Nhập lại mật khẩu ví",
"repeated_password_is_incorrect": "Mật khẩu nhập lại không chính xác. Vui lòng nhập lại mật khẩu ví.",
"require_for_adding_contacts": "Yêu cầu khi thêm danh bạ",
"require_for_all_security_and_backup_settings": "Yêu cầu cho tất cả các cài đặt bảo mật và sao lưu",
"require_for_assessing_wallet": "Yêu cầu khi truy cập ví",
"require_for_creating_new_wallets": "Yêu cầu khi tạo ví mới",
"require_for_exchanges_to_external_wallets": "Yêu cầu khi đổi sang ví ngoài",
"require_for_exchanges_to_internal_wallets": "Yêu cầu khi đổi sang ví nội bộ",
"require_for_sends_to_contacts": "Yêu cầu khi gửi đến danh bạ",
"require_for_sends_to_internal_wallets": "Yêu cầu khi gửi đến ví nội bộ",
"require_for_sends_to_non_contacts": "Yêu cầu khi gửi đến người không phải danh bạ",
"require_pin_after": "Yêu cầu PIN sau",
"rescan": "Quét lại",
"resend_code": "Vui lòng gửi lại",
"reset": "Đặt lại",
"reset_password": "Đặt lại mật khẩu",
"restore_active_seed": "Hạt giống hoạt động",
"restore_address": "Địa chỉ",
"restore_bitcoin_description_from_keys": "Khôi phục ví của bạn từ chuỗi WIF được tạo từ khóa riêng của bạn",
"restore_bitcoin_description_from_seed": "Khôi phục ví của bạn từ mã kết hợp 24 từ",
"restore_bitcoin_title_from_keys": "Khôi phục từ WIF",
"restore_description_from_backup": "Bạn có thể khôi phục toàn bộ ứng dụng Cake Wallet từ tệp sao lưu của bạn",
"restore_description_from_hardware_wallet": "Khôi phục từ ví phần cứng Ledger",
"restore_description_from_keys": "Khôi phục ví của bạn từ các thao tác nhập được lưu từ khóa riêng của bạn",
"restore_description_from_seed": "Khôi phục ví của bạn từ mã kết hợp 25 từ hoặc 13 từ",
"restore_description_from_seed_keys": "Khôi phục ví của bạn từ hạt giống/khóa mà bạn đã lưu ở nơi an toàn",
"restore_from_date_or_blockheight": "Vui lòng nhập một ngày vài ngày trước khi bạn tạo ví này. Hoặc nếu bạn biết chiều cao khối, hãy nhập nó thay thế",
"restore_from_seed_placeholder": "Vui lòng nhập hoặc dán hạt giống của bạn vào đây",
"restore_new_seed": "Hạt giống mới",
"restore_next": "Tiếp theo",
"restore_recover": "Khôi phục",
"restore_restore_wallet": "Khôi phục Ví",
"restore_seed_keys_restore": "Khôi phục hạt giống/khóa",
"restore_spend_key_private": "Khóa chi tiêu (riêng tư)",
"restore_title_from_backup": "Khôi phục từ sao lưu",
"restore_title_from_hardware_wallet": "Khôi phục từ ví phần cứng",
"restore_title_from_keys": "Khôi phục từ khóa",
"restore_title_from_seed": "Khôi phục từ hạt giống",
"restore_title_from_seed_keys": "Khôi phục từ hạt giống/khóa",
"restore_view_key_private": "Khóa xem (riêng tư)",
"restore_wallet": "Khôi phục Ví",
"restore_wallet_name": "Tên ví",
"restore_wallet_restore_description": "Mô tả khôi phục ví",
"robinhood_option_description": "Mua và chuyển ngay lập tức bằng thẻ ghi nợ, tài khoản ngân hàng hoặc số dư Robinhood của bạn. Chỉ có ở Mỹ.",
"router_no_route": "Không có tuyến đường nào được định nghĩa cho ${name}",
"save": "Lưu",
"save_backup_password": "Vui lòng đảm bảo rằng bạn đã lưu mật khẩu sao lưu của mình. Bạn sẽ không thể nhập tệp sao lưu của mình nếu không có nó.",
"save_backup_password_alert": "Lưu mật khẩu sao lưu",
"save_to_downloads": "Lưu vào Tải xuống",
"saved_the_trade_id": "Tôi đã lưu ID giao dịch",
"scan_one_block": "Quét một khối",
"scan_qr_code": "Quét mã QR",
"scan_qr_code_to_get_address": "Quét mã QR để nhận địa chỉ",
"scan_qr_on_device": "Quét mã QR này trên thiết bị khác",
"search": "Tìm kiếm",
"search_add_token": "Tìm kiếm / Thêm token",
"search_category": "Tìm kiếm danh mục",
"search_currency": "Tìm kiếm tiền tệ",
"search_language": "Tìm kiếm ngôn ngữ",
"second_intro_content": "Yat của bạn là một địa chỉ emoji duy nhất thay thế tất cả các địa chỉ hex dài của bạn cho tất cả các loại tiền tệ của bạn.",
"second_intro_title": "Một địa chỉ emoji để cai trị tất cả",
"security_and_backup": "Bảo mật và sao lưu",
"seed_alert_back": "Quay lại",
"seed_alert_content": "Hạt giống là cách duy nhất để khôi phục ví của bạn. Bạn đã ghi lại nó chưa?",
"seed_alert_title": "Chú ý",
"seed_alert_yes": "Có, tôi đã ghi lại",
"seed_choose": "Chọn ngôn ngữ hạt giống",
"seed_hex_form": "Hạt giống ví (dạng hex)",
"seed_key": "Khóa hạt giống",
"seed_language": "Ngôn ngữ hạt giống",
"seed_language_chinese": "Tiếng Trung",
"seed_language_chinese_traditional": "Tiếng Trung (Truyền thống)",
"seed_language_czech": "Tiếng Séc",
"seed_language_dutch": "Tiếng Hà Lan",
"seed_language_english": "Tiếng Anh",
"seed_language_french": "Tiếng Pháp",
"seed_language_german": "Tiếng Đức",
"seed_language_italian": "Tiếng Ý",
"seed_language_japanese": "Tiếng Nhật",
"seed_language_korean": "Tiếng Hàn",
"seed_language_next": "Tiếp theo",
"seed_language_portuguese": "Tiếng Bồ Đào Nha",
"seed_language_russian": "Tiếng Nga",
"seed_language_spanish": "Tiếng Tây Ban Nha",
"seed_phrase_length": "Độ dài cụm từ hạt giống",
"seed_reminder": "Vui lòng ghi lại những điều này phòng khi bạn mất hoặc xóa điện thoại của mình",
"seed_share": "Chia sẻ hạt giống",
"seed_title": "Hạt giống",
"seedtype": "Loại hạt giống",
"seedtype_legacy": "Di sản (25 từ)",
"seedtype_polyseed": "Polyseed (16 từ)",
"seedtype_wownero": "Wownero (14 từ)",
"select_backup_file": "Chọn tệp sao lưu",
"select_buy_provider_notice": "Chọn nhà cung cấp mua ở trên. Bạn có thể bỏ qua màn hình này bằng cách thiết lập nhà cung cấp mua mặc định trong cài đặt ứng dụng.",
"select_destination": "Vui lòng chọn đích cho tệp sao lưu.",
"select_sell_provider_notice": "Chọn nhà cung cấp bán ở trên. Bạn có thể bỏ qua màn hình này bằng cách thiết lập nhà cung cấp bán mặc định trong cài đặt ứng dụng.",
"sell": "Bán",
"sell_alert_content": "Hiện tại chúng tôi chỉ hỗ trợ bán Bitcoin, Ethereum và Litecoin. Vui lòng tạo hoặc chuyển sang ví Bitcoin, Ethereum hoặc Litecoin của bạn.",
"sell_monero_com_alert_content": "Bán Monero chưa được hỗ trợ",
"send": "Gửi",
"send_address": "Địa chỉ ${cryptoCurrency}",
"send_amount": "Số tiền:",
"send_creating_transaction": "Tạo giao dịch",
"send_error_currency": "Tiền tệ chỉ có thể chứa số",
"send_error_minimum_value": "Giá trị tối thiểu của số tiền là 0.01",
"send_estimated_fee": "Phí ước lượng:",
"send_fee": "Phí:",
"send_name": "Tên",
"send_new": "Mới",
"send_payment_id": "ID thanh toán (tùy chọn)",
"send_priority": "Hiện tại phí được đặt ở mức ưu tiên ${transactionPriority}.\nƯu tiên giao dịch có thể được điều chỉnh trong cài đặt",
"send_sending": "Đang gửi...",
"send_success": "Đã gửi ${crypto} của bạn thành công",
"send_templates": "Mẫu",
"send_title": "Gửi",
"send_to_this_address": "Gửi ${currency} ${tag} đến địa chỉ này",
"send_xmr": "Gửi XMR",
"send_your_wallet": "Ví của bạn",
"sending": "Đang gửi",
"sent": "Đã gửi",
"service_health_disabled": "Thông báo sức khỏe dịch vụ bị vô hiệu hóa",
"service_health_disabled_message": "Đây là trang thông báo sức khỏe dịch vụ, bạn có thể kích hoạt trang này trong Cài đặt -> Quyền riêng tư",
"settings": "Cài đặt",
"settings_all": "TẤT CẢ",
"settings_allow_biometrical_authentication": "Cho phép xác thực sinh trắc học",
"settings_can_be_changed_later": "Các cài đặt này có thể được thay đổi sau trong cài đặt ứng dụng",
"settings_change_language": "Thay đổi ngôn ngữ",
"settings_change_pin": "Thay đổi PIN",
"settings_currency": "Tiền tệ",
"settings_current_node": "Nút hiện tại",
"settings_dark_mode": "Chế độ tối",
"settings_display_balance": "Hiển thị số dư",
"settings_display_on_dashboard_list": "Hiển thị trên danh sách bảng điều khiển",
"settings_fee_priority": "Ưu tiên phí",
"settings_nodes": "Các nút",
"settings_none": "Không có",
"settings_only_trades": "Chỉ giao dịch",
"settings_only_transactions": "Chỉ giao dịch",
"settings_personal": "Cá nhân",
"settings_save_recipient_address": "Lưu địa chỉ người nhận",
"settings_support": "Hỗ trợ",
"settings_terms_and_conditions": "Điều khoản và Điều kiện",
"settings_title": "Cài đặt",
"settings_trades": "Giao dịch",
"settings_transactions": "Giao dịch",
"settings_wallets": "Ví",
"setup_2fa": "Thiết lập Cake 2FA",
"setup_2fa_text": "Cake 2FA hoạt động bằng cách sử dụng TOTP làm yếu tố xác thực thứ hai.\n\nTOTP của Cake 2FA yêu cầu hỗ trợ SHA-512 và 8 chữ số; điều này cung cấp bảo mật cao hơn. Thông tin thêm và các ứng dụng hỗ trợ có thể được tìm thấy trong hướng dẫn.",
"setup_pin": "Thiết lập PIN",
"setup_successful": "PIN của bạn đã được thiết lập thành công!",
"setup_totp_recommended": "Thiết lập TOTP",
"setup_warning_2fa_text": "Cake 2FA là xác thực thứ hai cho một số hành động trong ví. Nó KHÔNG an toàn như lưu trữ lạnh.\n\nNếu bạn mất quyền truy cập vào ứng dụng 2FA hoặc các khóa TOTP, bạn SẼ mất quyền truy cập vào ví này. Bạn sẽ cần phải khôi phục ví của bạn từ hạt giống nhớ.\n\nHỗ trợ Cake sẽ không thể hỗ trợ bạn nếu bạn mất quyền truy cập vào 2FA hoặc hạt giống nhớ của bạn.\nTrước khi sử dụng Cake 2FA, chúng tôi khuyến nghị đọc kỹ hướng dẫn.",
"setup_your_debit_card": "Thiết lập thẻ ghi nợ của bạn",
"share": "Chia sẻ",
"share_address": "Chia sẻ địa chỉ",
"show_details": "Hiển thị chi tiết",
"show_keys": "Hiển thị hạt giống/khóa",
"show_market_place": "Hiển thị Thị trường",
"show_seed": "Hiển thị hạt giống",
"sign_message": "Ký tin nhắn",
"sign_up": "Đăng ký",
"sign_verify_message": "Ký hoặc xác minh tin nhắn",
"sign_verify_message_sub": "Ký hoặc xác minh một tin nhắn bằng khóa riêng của bạn",
"sign_verify_title": "Ký / Xác minh",
"signature": "Chữ ký",
"signature_invalid_error": "Chữ ký không hợp lệ cho tin nhắn đã cho",
"signTransaction": "Ký giao dịch",
"signup_for_card_accept_terms": "Đăng ký thẻ và chấp nhận các điều khoản.",
"silent_payments": "Thanh toán im lặng",
"silent_payments_always_scan": "Đặt Thanh toán im lặng luôn quét",
"silent_payments_disclaimer": "Địa chỉ mới không phải là danh tính mới. Đây là việc tái sử dụng một danh tính hiện có với nhãn khác.",
"silent_payments_display_card": "Hiển thị thẻ Thanh toán im lặng",
"silent_payments_scan_from_date": "Quét từ ngày",
"silent_payments_scan_from_date_or_blockheight": "Vui lòng nhập chiều cao khối bạn muốn bắt đầu quét cho các thanh toán im lặng đến, hoặc, sử dụng ngày thay thế. Bạn có thể chọn nếu ví tiếp tục quét mỗi khối, hoặc chỉ kiểm tra chiều cao đã chỉ định.",
"silent_payments_scan_from_height": "Quét từ chiều cao khối",
"silent_payments_scanned_tip": "ĐÃ QUÉT ĐỂ TIP! (${tip})",
"silent_payments_scanning": "Đang quét thanh toán im lặng",
"silent_payments_settings": "Cài đặt thanh toán im lặng",
"slidable": "Có thể kéo",
"sort_by": "Sắp xếp theo",
"spend_key_private": "Khóa chi tiêu (riêng tư)",
"spend_key_public": "Khóa chi tiêu (công khai)",
"status": "Trạng thái: ",
"string_default": "Mặc định",
"subaddress_title": "Danh sách địa chỉ phụ",
"subaddresses": "Địa chỉ phụ",
"submit_request": "gửi yêu cầu",
"successful": "Thành công",
"support_description_guides": "Tài liệu và hỗ trợ cho các vấn đề phổ biến",
"support_description_live_chat": "Miễn phí và nhanh chóng! Các đại diện hỗ trợ được đào tạo sẵn sàng hỗ trợ",
"support_description_other_links": "Tham gia cộng đồng của chúng tôi hoặc liên hệ với chúng tôi hoặc các đối tác của chúng tôi qua các phương pháp khác",
"support_title_guides": "Hướng dẫn Cake Wallet",
"support_title_live_chat": "Hỗ trợ trực tiếp",
"support_title_other_links": "Liên kết hỗ trợ khác",
"sweeping_wallet": "Quét ví",
"sweeping_wallet_alert": "Việc này không nên mất nhiều thời gian. KHÔNG RỜI KHỎI MÀN HÌNH NÀY HOẶC CÁC KHOẢN TIỀN ĐƯỢC QUÉT CÓ THỂ BỊ MẤT.",
"switchToETHWallet": "Vui lòng chuyển sang ví Ethereum và thử lại",
"switchToEVMCompatibleWallet": "Vui lòng chuyển sang ví tương thích EVM và thử lại (Ethereum, Polygon)",
"symbol": "Ký hiệu",
"sync_all_wallets": "Đồng bộ tất cả các ví",
"sync_status_attempting_sync": "ĐANG THỬ ĐỒNG BỘ",
"sync_status_connected": "ĐÃ KẾT NỐI",
"sync_status_connecting": "ĐANG KẾT NỐI",
"sync_status_failed_connect": "ĐÃ NGẮT KẾT NỐI",
"sync_status_not_connected": "CHƯA KẾT NỐI",
"sync_status_starting_scan": "ĐANG BẮT ĐẦU QUÉT",
"sync_status_starting_sync": "ĐANG BẮT ĐẦU ĐỒNG BỘ",
"sync_status_syncronized": "ĐÃ ĐỒNG BỘ",
"sync_status_syncronizing": "ĐANG ĐỒNG BỘ",
"sync_status_timed_out": "HẾT THỜI GIAN",
"sync_status_unsupported": "NÓT KHÔNG ĐƯỢC HỖ TRỢ",
"syncing_wallet_alert_content": "Số dư và danh sách giao dịch của bạn có thể không đầy đủ cho đến khi nó hiển thị “ĐÃ ĐỒNG BỘ” ở trên cùng. Nhấn vào đây để tìm hiểu thêm.",
"syncing_wallet_alert_title": "Ví của bạn đang đồng bộ",
"template": "Mẫu",
"template_name": "Tên mẫu",
"testnet_coins_no_value": "Tiền tệ testnet không có giá trị",
"third_intro_content": "Yats cũng tồn tại ngoài Cake Wallet. Bất kỳ địa chỉ ví nào trên thế giới đều có thể được thay thế bằng một Yat!",
"third_intro_title": "Yat tương thích tốt với các đối tượng khác",
"thorchain_contract_address_not_supported": "THORChain không hỗ trợ gửi đến địa chỉ hợp đồng",
"thorchain_taproot_address_not_supported": "Nhà cung cấp ThorChain không hỗ trợ địa chỉ Taproot. Vui lòng thay đổi địa chỉ hoặc chọn nhà cung cấp khác.",
"time": "${minutes} phút ${seconds} giây",
"tip": "Mẹo:",
"today": "Hôm nay",
"token_contract_address": "Địa chỉ hợp đồng token",
"token_decimal": "Số thập phân của token",
"token_name": "Tên token ví dụ: Tether",
"token_symbol": "Ký hiệu token ví dụ: USDT",
"tokenID": "ID",
"tor_connection": "Kết nối Tor",
"tor_only": "Chỉ Tor",
"total": "Tổng cộng",
"total_saving": "Tiết kiệm tổng cộng",
"totp_2fa_failure": "Mã không chính xác. Vui lòng thử mã khác hoặc tạo khóa bí mật mới. Sử dụng ứng dụng 2FA tương thích hỗ trợ mã 8 chữ số và SHA512.",
"totp_2fa_success": "Thành công! Cake 2FA đã được kích hoạt cho ví này. Hãy nhớ lưu hạt giống nhớ của bạn phòng trường hợp bạn mất quyền truy cập vào ví.",
"totp_auth_url": "URL XÁC THỰC TOTP",
"totp_code": "Mã TOTP",
"totp_secret_code": "Mã bí mật TOTP",
"totp_verification_success": "Xác minh thành công!",
"track": "Theo dõi",
"trade_details_copied": "${title} đã được sao chép vào clipboard",
"trade_details_created_at": "Tạo lúc",
"trade_details_fetching": "Đang lấy dữ liệu",
"trade_details_id": "ID",
"trade_details_pair": "Cặp",
"trade_details_provider": "Nhà cung cấp",
"trade_details_state": "Trạng thái",
"trade_details_title": "Chi tiết giao dịch",
"trade_for_not_created": "Giao dịch cho ${title} chưa được tạo.",
"trade_history_title": "Lịch sử giao dịch",
"trade_id": "ID giao dịch:",
"trade_id_not_found": "Giao dịch ${tradeId} của ${title} không tìm thấy.",
"trade_is_powered_by": "Giao dịch này được cung cấp bởi ${provider}",
"trade_not_created": "Giao dịch chưa được tạo",
"trade_not_found": "Giao dịch không tìm thấy.",
"trade_state_btc_sent": "BTC đã gửi",
"trade_state_complete": "Hoàn thành",
"trade_state_confirming": "Đang xác nhận",
"trade_state_created": "Đã tạo",
"trade_state_finished": "Đã hoàn tất",
"trade_state_paid": "Đã thanh toán",
"trade_state_paid_unconfirmed": "Đã thanh toán chưa xác nhận",
"trade_state_pending": "Đang chờ xử lý",
"trade_state_timeout": "Hết thời gian",
"trade_state_to_be_created": "Sẽ được tạo",
"trade_state_traded": "Đã giao dịch",
"trade_state_trading": "Đang giao dịch",
"trade_state_underpaid": "Thanh toán chưa đủ",
"trade_state_unpaid": "Chưa thanh toán",
"trades": "Giao dịch",
"transaction_details_amount": "Số tiền",
"transaction_details_copied": "${title} đã được sao chép vào clipboard",
"transaction_details_date": "Ngày",
"transaction_details_fee": "Phí",
"transaction_details_height": "Chiều cao",
"transaction_details_recipient_address": "Địa chỉ người nhận",
"transaction_details_source_address": "Địa chỉ nguồn",
"transaction_details_title": "Chi tiết giao dịch",
"transaction_details_transaction_id": "ID giao dịch",
"transaction_key": "Khóa giao dịch",
"transaction_priority_fast": "Nhanh",
"transaction_priority_fastest": "Nhanh nhất",
"transaction_priority_medium": "Trung bình",
"transaction_priority_regular": "Thông thường",
"transaction_priority_slow": "Chậm",
"transaction_sent": "Giao dịch đã được gửi!",
"transaction_sent_notice": "Nếu màn hình không tiếp tục sau 1 phút, hãy kiểm tra trình khám phá khối và email của bạn.",
"transactions": "Giao dịch",
"transactions_by_date": "Giao dịch theo ngày",
"trongrid_history": "Lịch sử TronGrid",
"trusted": "Đã tin cậy",
"tx_commit_exception_no_dust_on_change": "Giao dịch bị từ chối với số tiền này. Với số tiền này bạn có thể gửi ${min} mà không cần đổi tiền lẻ hoặc ${max} trả lại tiền lẻ.",
"tx_commit_failed": "Giao dịch không thành công. Vui lòng liên hệ với hỗ trợ.",
"tx_invalid_input": "Bạn đang sử dụng loại đầu vào sai cho loại thanh toán này",
"tx_no_dust_exception": "Giao dịch bị từ chối vì gửi một số tiền quá nhỏ. Vui lòng thử tăng số tiền.",
"tx_not_enough_inputs_exception": "Không đủ đầu vào có sẵn. Vui lòng chọn thêm dưới Coin Control",
"tx_rejected_bip68_final": "Giao dịch có đầu vào chưa xác nhận và không thể thay thế bằng phí.",
"tx_rejected_dust_change": "Giao dịch bị từ chối bởi quy tắc mạng, số tiền thay đổi thấp (dust). Thử gửi TOÀN BỘ hoặc giảm số tiền.",
"tx_rejected_dust_output": "Giao dịch bị từ chối bởi quy tắc mạng, số tiền đầu ra thấp (dust). Vui lòng tăng số tiền.",
"tx_rejected_dust_output_send_all": "Giao dịch bị từ chối bởi quy tắc mạng, số tiền đầu ra thấp (dust). Vui lòng kiểm tra số dư của các đồng tiền được chọn dưới Coin Control.",
"tx_rejected_vout_negative": "Không đủ số dư để thanh toán phí giao dịch này. Vui lòng kiểm tra số dư của các đồng tiền dưới Coin Control.",
"tx_wrong_balance_exception": "Bạn không có đủ ${currency} để gửi số tiền này.",
"tx_wrong_balance_with_amount_exception": "Bạn không có đủ ${currency} để gửi tổng số tiền ${amount}",
"tx_zero_fee_exception": "Không thể gửi giao dịch với phí bằng 0. Thử tăng tỷ lệ phí hoặc kiểm tra kết nối của bạn để biết ước lượng mới nhất.",
"unavailable_balance": "Số dư không khả dụng",
"unavailable_balance_description": "Số dư không khả dụng: Tổng số này bao gồm các khoản tiền bị khóa trong các giao dịch chờ xử lý và những khoản bạn đã chủ động đóng băng trong cài đặt điều khiển tiền của bạn. Các số dư bị khóa sẽ trở nên khả dụng khi các giao dịch tương ứng của chúng hoàn tất, trong khi các số dư bị đóng băng vẫn không thể truy cập cho các giao dịch cho đến khi bạn quyết định mở khóa chúng.",
"unconfirmed": "Số dư chưa xác nhận",
"understand": "Tôi hiểu",
"unlock": "Mở khóa",
"unmatched_currencies": "Tiền tệ của ví hiện tại của bạn không khớp với QR đã quét",
"unspent_change": "Tiền thối",
"unspent_coins_details_title": "Chi tiết các đồng tiền chưa chi tiêu",
"unspent_coins_title": "Các đồng tiền chưa chi tiêu",
"unsupported_asset": "Chúng tôi không hỗ trợ hành động này cho tài sản này. Vui lòng tạo hoặc chuyển sang ví của loại tài sản được hỗ trợ.",
"uptime": "Thời gian hoạt động",
"upto": "lên đến ${value}",
"usb": "USB",
"use": "Chuyển sang",
"use_card_info_three": "Sử dụng thẻ kỹ thuật số trực tuyến hoặc với các phương thức thanh toán không tiếp xúc.",
"use_card_info_two": "Các khoản tiền được chuyển đổi thành USD khi chúng được giữ trong tài khoản trả trước, không phải trong các loại tiền kỹ thuật số.",
"use_ssl": "Sử dụng SSL",
"use_suggested": "Sử dụng đề xuất",
"use_testnet": "Sử dụng Testnet",
"value": "Giá trị",
"value_type": "Loại giá trị",
"variable_pair_not_supported": "Cặp biến này không được hỗ trợ với các sàn giao dịch đã chọn",
"verification": "Xác minh",
"verify_message": "Xác minh tin nhắn",
"verify_with_2fa": "Xác minh với Cake 2FA",
"version": "Phiên bản ${currentVersion}",
"view_all": "Xem tất cả",
"view_in_block_explorer": "Xem trong Block Explorer",
"view_key_private": "Xem khóa (riêng tư)",
"view_key_public": "Xem khóa (công khai)",
"view_transaction_on": "Xem giao dịch trên",
"voting_weight": "Trọng số bỏ phiếu",
"waitFewSecondForTxUpdate": "Vui lòng đợi vài giây để giao dịch được phản ánh trong lịch sử giao dịch",
"wallet_keys": "Hạt giống/khóa ví",
"wallet_list_create_new_wallet": "Tạo ví mới",
"wallet_list_edit_wallet": "Chỉnh sửa ví",
"wallet_list_failed_to_load": "Tải ví ${wallet_name} không thành công. ${error}",
"wallet_list_failed_to_remove": "Xóa ví ${wallet_name} không thành công. ${error}",
"wallet_list_load_wallet": "Tải ví",
"wallet_list_loading_wallet": "Đang tải ví ${wallet_name}",
"wallet_list_removing_wallet": "Đang xóa ví ${wallet_name}",
"wallet_list_restore_wallet": "Khôi phục ví",
"wallet_list_title": "Ví Monero",
"wallet_list_wallet_name": "Tên ví",
"wallet_menu": "Menu",
"wallet_name": "Tên ví",
"wallet_name_exists": "Một ví với tên đó đã tồn tại. Vui lòng chọn tên khác hoặc đổi tên ví khác trước.",
"wallet_password_is_empty": "Mật khẩu ví bị bỏ trống. Mật khẩu ví không được để trống",
"wallet_recovery_height": "Chiều cao khôi phục",
"wallet_restoration_store_incorrect_seed_length": "Độ dài hạt giống không chính xác",
"wallet_seed": "Hạt giống ví",
"wallet_seed_legacy": "Hạt giống ví cũ",
"wallet_store_monero_wallet": "Ví Monero",
"walletConnect": "WalletConnect",
"wallets": "Các ví",
"warning": "Cảnh báo",
"welcome": "Chào mừng đến với",
"welcome_to_cakepay": "Chào mừng đến với Cake Pay!",
"what_is_silent_payments": "Thanh toán im lặng là gì?",
"widgets_address": "Địa chỉ",
"widgets_or": "hoặc",
"widgets_restore_from_blockheight": "Khôi phục từ chiều cao khối",
"widgets_restore_from_date": "Khôi phục từ ngày",
"widgets_seed": "Hạt giống",
"wouoldLikeToConnect": "muốn kết nối",
"write_down_backup_password": "Vui lòng ghi lại mật khẩu sao lưu của bạn, được sử dụng để nhập các tệp sao lưu của bạn.",
"xlm_extra_info": "Vui lòng đừng quên chỉ định Memo ID khi gửi giao dịch XLM để trao đổi",
"xmr_available_balance": "Số dư khả dụng",
"xmr_full_balance": "Số dư đầy đủ",
"xmr_hidden": "Bị ẩn",
"xmr_to_error": "Lỗi XMR.TO",
"xmr_to_error_description": "Số tiền không hợp lệ. Giới hạn tối đa 8 chữ số sau dấu thập phân",
"xrp_extra_info": "Vui lòng đừng quên chỉ định Destination Tag khi gửi giao dịch XRP để trao đổi",
"yat": "Yat",
"yat_address": "Địa chỉ Yat",
"yat_alert_content": "Người dùng Cake Wallet hiện có thể gửi và nhận tất cả các loại tiền yêu thích của họ với tên người dùng dựa trên emoji độc đáo.",
"yat_alert_title": "Gửi và nhận crypto dễ dàng hơn với Yat",
"yat_error": "Lỗi Yat",
"yat_error_content": "Không có địa chỉ liên kết với Yat này. Thử Yat khác",
"yat_popup_content": "Bây giờ bạn có thể gửi và nhận crypto trong Cake Wallet với Yat của bạn - một tên người dùng ngắn gọn dựa trên emoji. Quản lý Yats bất cứ lúc nào trên màn hình cài đặt",
"yat_popup_title": "Địa chỉ ví của bạn có thể được chuyển thành emoji.",
"yesterday": "Hôm qua",
"you_now_have_debit_card": "Bạn hiện có một thẻ ghi nợ",
"you_pay": "Bạn thanh toán",
"you_will_get": "Chuyển đổi thành",
"you_will_send": "Chuyển đổi từ",
"yy": "YY"
}

View file

@ -160,6 +160,7 @@
"contact_name": "Orúkọ olùbásọ̀rọ̀",
"contact_support": "Bá ìranlọ́wọ́ sọ̀rọ̀",
"continue_text": "Tẹ̀síwájú",
"contract_warning": "Adirẹsi adehun adehun yii ti samisi bi arekereke. Jọwọ ṣe ilana pẹlu iṣọra.",
"contractName": "Orukọ adehun",
"contractSymbol": "Aami adehun",
"copied_key_to_clipboard": "Ti ṣeda ${key} sí àtẹ àkọsílẹ̀",
@ -219,6 +220,7 @@
"displayable": "A lè ṣàfihàn ẹ̀",
"do_not_have_enough_gas_asset": "O ko ni to ${currency} lati ṣe idunadura kan pẹlu awọn ipo nẹtiwọki blockchain lọwọlọwọ. O nilo diẹ sii ${currency} lati san awọn owo nẹtiwọọki blockchain, paapaa ti o ba nfi dukia miiran ranṣẹ.",
"do_not_send": "Ẹ kò ránṣ",
"do_not_send_funds_to_contract_address_warning": "Maṣe fi owo ranṣẹ si adirẹsi yii \n\n Eyi jẹ idamọ fun àmi, eyikeyi awọn owo ti a firanṣẹ si adirẹsi yii yoo ko beere lọwọ rẹ lati ṣafikun adirẹsi adehun kan",
"do_not_share_warning_text": "Ẹ kò pín wọnyìí sí ẹnikẹ́ni. Ẹ sì kò pin wọnyìí sí ìranlọ́wọ́. Ẹnikẹ́ni lè jí owó yín! Wọ́n máa jí owó yín!",
"do_not_show_me": "Kò fi eléyìí hàn mi mọ́",
"domain_looks_up": "Awọn wiwa agbegbe",

View file

@ -160,6 +160,7 @@
"contact_name": "联系人姓名",
"contact_support": "联系支持",
"continue_text": "继续",
"contract_warning": "该合同地址已被标记为潜在的欺诈性。请谨慎处理。",
"contractName": "合约名称",
"contractSymbol": "合约符号",
"copied_key_to_clipboard": "复制 ${key} 到剪贴板",
@ -219,6 +220,7 @@
"displayable": "可显示",
"do_not_have_enough_gas_asset": "您没有足够的 ${currency} 来在当前的区块链网络条件下进行交易。即使您发送的是不同的资产,您也需要更多的 ${currency} 来支付区块链网络费用。",
"do_not_send": "不要发送",
"do_not_send_funds_to_contract_address_warning": "请勿将资金发送到此地址\n\n这只是令牌的标识符任何发送到此地址的资金都将丢失。\n\n注意蛋糕永远不会要求您添加合同地址",
"do_not_share_warning_text": "请勿与其他任何人分享这些信息,包括支持人员。\n\n您的资金可能而且将会被盗",
"do_not_show_me": "不再提示",
"domain_looks_up": "域名查找",

View file

@ -8,7 +8,7 @@ if [[ ! -d "monero_c" ]];
then
git clone https://github.com/mrcyjanek/monero_c --branch rewrite-wip
cd monero_c
git checkout 5de323b1ba7387cf73973042f06383d4dbe619f5
git checkout 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
git reset --hard
git submodule update --init --force --recursive
./apply_patches.sh monero

View file

@ -43,6 +43,12 @@ class SecretKey {
SecretKey('cakePayApiKey', () => ''),
SecretKey('CSRFToken', () => ''),
SecretKey('authorization', () => ''),
SecretKey('etherScanApiKey', () => ''),
SecretKey('polygonScanApiKey', () => ''),
SecretKey('letsExchangeBearerToken', () => ''),
SecretKey('letsExchangeAffiliateId', () => ''),
SecretKey('stealthExBearerToken', () => ''),
SecretKey('stealthExAdditionalFeePercent', () => ''),
];
static final evmChainsSecrets = [

View file

@ -2,5 +2,5 @@ const defaultLang = "en";
const langs = [
"ar", "bg", "cs", "de", "en", "es", "fr", "ha", "hi", "hr", "hy", "id", "it",
"ja", "ko", "my", "nl", "pl", "pt", "ru", "th", "tl", "tr", "uk", "ur", "yo",
"zh-cn" // zh, but Google Translate uses zh-cn for Chinese (Simplified)
"vi", "zh-cn" // zh, but Google Translate uses zh-cn for Chinese (Simplified)
];