diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index b67a4d937..c4b006ec0 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -128,6 +128,7 @@ jobs: echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_ethereum/lib/.secrets.g.dart echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart + echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart echo "const robinhoodCIdApiSecret = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart diff --git a/assets/images/exolix.png b/assets/images/exolix.png new file mode 100644 index 000000000..29e5f2db1 Binary files /dev/null and b/assets/images/exolix.png differ diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt index cbe201cf8..9393f7768 100644 --- a/assets/text/Monerocom_Release_Notes.txt +++ b/assets/text/Monerocom_Release_Notes.txt @@ -1,3 +1,3 @@ -Enhance Monero coin control -Add Filipino localization -Bug Fixes \ No newline at end of file +Fix 2FA code issue +Bug fixes +Minor enhancements \ No newline at end of file diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index 263e7ccfe..1fd86c9ca 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1,5 +1,4 @@ -New Buy Provider Robinhood -Fix sending Ethereum issue -Enhance Monero coin control -Add Filipino localization -Bug Fixes \ No newline at end of file +Ethereum enhancements and bug fixes +Fix 2FA code issue +Bug fixes +Minor enhancements \ No newline at end of file diff --git a/cw_ethereum/lib/ethereum_client.dart b/cw_ethereum/lib/ethereum_client.dart index e10e79f1e..c2639827f 100644 --- a/cw_ethereum/lib/ethereum_client.dart +++ b/cw_ethereum/lib/ethereum_client.dart @@ -65,13 +65,11 @@ class EthereumClient { bool _isEthereum = currency == CryptoCurrency.eth; - final price = await _client!.getGasPrice(); + final price = _client!.getGasPrice(); final Transaction transaction = Transaction( from: privateKey.address, to: EthereumAddress.fromHex(toAddress), - maxGas: gas, - gasPrice: price, maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip), value: _isEthereum ? EtherAmount.inWei(BigInt.parse(amount)) : EtherAmount.zero(), ); @@ -101,7 +99,7 @@ class EthereumClient { return PendingEthereumTransaction( signedTransaction: signedTransaction, amount: amount, - fee: BigInt.from(gas) * price.getInWei, + fee: BigInt.from(gas) * (await price).getInWei, sendTransaction: _sendTransaction, exponent: exponent, ); diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 7df95c022..f5f4a9983 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -121,12 +121,12 @@ abstract class MoneroWalletBase extends WalletBase init() async { await walletAddresses.init(); - balance = ObservableMap.of( - { - currency: MoneroBalance( - fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id), - unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id)) - }); + balance = ObservableMap.of({ + currency: MoneroBalance( + fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id), + unlockedBalance: + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id)) + }); _setListeners(); await updateTransactions(); @@ -134,15 +134,14 @@ abstract class MoneroWalletBase extends WalletBase await save()); + _autoSaveTimer = + Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save()); } + @override Future? updateBalance() => null; @@ -162,7 +161,8 @@ abstract class MoneroWalletBase extends WalletBase 1; final unlockedBalance = - monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); var allInputsAmount = 0; PendingTransactionDescription pendingTransactionDescription; @@ -217,56 +217,42 @@ abstract class MoneroWalletBase extends WalletBase item.sendAll - || (item.formattedCryptoAmount ?? 0) <= 0)) { + if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) { throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.'); } - final int totalAmount = outputs.fold(0, (acc, value) => - acc + (value.formattedCryptoAmount ?? 0)); + final int totalAmount = + outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)); final estimatedFee = calculateEstimatedFee(_credentials.priority, totalAmount); if (unlockedBalance < totalAmount) { throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.'); } - if (allInputsAmount < totalAmount + estimatedFee) { + if (!spendAllCoins && (allInputsAmount < totalAmount + estimatedFee)) { throw MoneroTransactionNoInputsException(inputs.length); } final moneroOutputs = outputs.map((output) { - final outputAddress = output.isParsedAddress - ? output.extractedAddress - : output.address; + final outputAddress = output.isParsedAddress ? output.extractedAddress : output.address; - return MoneroOutput( - address: outputAddress!, - amount: output.cryptoAmount!.replaceAll(',', '.')); + return MoneroOutput( + address: outputAddress!, amount: output.cryptoAmount!.replaceAll(',', '.')); }).toList(); - pendingTransactionDescription = - await transaction_history.createTransactionMultDest( + pendingTransactionDescription = await transaction_history.createTransactionMultDest( outputs: moneroOutputs, priorityRaw: _credentials.priority.serialize(), accountIndex: walletAddresses.account!.id, preferredInputs: inputs); } else { final output = outputs.first; - final address = output.isParsedAddress - ? output.extractedAddress - : output.address; - final amount = output.sendAll - ? null - : output.cryptoAmount!.replaceAll(',', '.'); - final formattedAmount = output.sendAll - ? null - : output.formattedCryptoAmount; + final address = output.isParsedAddress ? output.extractedAddress : output.address; + final amount = output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.'); + final formattedAmount = output.sendAll ? null : output.formattedCryptoAmount; if ((formattedAmount != null && unlockedBalance < formattedAmount) || (formattedAmount == null && unlockedBalance <= 0)) { @@ -277,8 +263,9 @@ abstract class MoneroWalletBase extends WalletBase - element.walletId.contains(id) && element.hash.contains(coin.hash)); + final coinInfoList = unspentCoinsInfo.values + .where((element) => element.walletId.contains(id) && element.hash.contains(coin.hash)); if (coinInfoList.isNotEmpty) { final coinInfo = coinInfoList.first; @@ -457,16 +440,15 @@ abstract class MoneroWalletBase extends WalletBase _addCoinInfo(MoneroUnspent coin) async { final newInfo = UnspentCoinsInfo( - walletId: id, - hash: coin.hash, - isFrozen: coin.isFrozen, - isSending: coin.isSending, - noteRaw: coin.note, - address: coin.address, - value: coin.value, - vout: 0, - keyImage: coin.keyImage - ); + walletId: id, + hash: coin.hash, + isFrozen: coin.isFrozen, + isSending: coin.isSending, + noteRaw: coin.note, + address: coin.address, + value: coin.value, + vout: 0, + keyImage: coin.keyImage); await unspentCoinsInfo.add(newInfo); } @@ -474,8 +456,8 @@ abstract class MoneroWalletBase extends WalletBase _refreshUnspentCoinsInfo() async { try { final List keys = []; - final currentWalletUnspentCoins = unspentCoinsInfo.values - .where((element) => element.walletId.contains(id)); + final currentWalletUnspentCoins = + unspentCoinsInfo.values.where((element) => element.walletId.contains(id)); if (currentWalletUnspentCoins.isNotEmpty) { currentWalletUnspentCoins.forEach((element) { @@ -496,16 +478,14 @@ abstract class MoneroWalletBase extends WalletBase - monero_wallet.getAddress( - accountIndex: accountIndex, - addressIndex: addressIndex); + monero_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex); @override Future> fetchTransactions() async { transaction_history.refreshTransactions(); - return _getAllTransactionsOfAccount(walletAddresses.account?.id).fold>( - {}, - (Map acc, MoneroTransactionInfo tx) { + return _getAllTransactionsOfAccount(walletAddresses.account?.id) + .fold>({}, + (Map acc, MoneroTransactionInfo tx) { acc[tx.id] = tx; return acc; }); @@ -533,12 +513,11 @@ abstract class MoneroWalletBase extends WalletBase _getAllTransactionsOfAccount(int? accountIndex) => - transaction_history - .getAllTransactions() - .map((row) => MoneroTransactionInfo.fromRow(row)) - .where((element) => element.accountIndex == (accountIndex ?? 0)) - .toList(); + List _getAllTransactionsOfAccount(int? accountIndex) => transaction_history + .getAllTransactions() + .map((row) => MoneroTransactionInfo.fromRow(row)) + .where((element) => element.accountIndex == (accountIndex ?? 0)) + .toList(); void _setListeners() { _listener?.stop(); @@ -560,8 +539,7 @@ abstract class MoneroWalletBase extends WalletBase _askForUpdateTransactionHistory() async => - await updateTransactions(); + Future _askForUpdateTransactionHistory() async => await updateTransactions(); - int _getFullBalance() => - monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); + int _getFullBalance() => monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); int _getUnlockedBalance() => monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); @@ -605,8 +581,7 @@ abstract class MoneroWalletBase extends WalletBase setup({ (onAuthFinished, closable) => AuthPage(getIt.get(), onAuthenticationFinished: onAuthFinished, closable: closable)); - getIt.registerFactory( + getIt.registerLazySingleton( () => Setup2FAViewModel( getIt.get(), getIt.get(), diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index f699f89f8..d32dcbca4 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -19,7 +19,6 @@ class PreferencesKey { 'allow_biometrical_authentication'; static const useTOTP2FA = 'use_totp_2fa'; static const failedTotpTokenTrials = 'failed_token_trials'; - static const totpSecretKey = 'totp_qr_secret_key'; static const disableExchangeKey = 'disable_exchange'; static const exchangeStatusKey = 'exchange_status'; static const currentTheme = 'current_theme'; @@ -75,4 +74,5 @@ class PreferencesKey { static const shouldRequireTOTP2FAForAllSecurityAndBackupSettings = 'should_require_totp_2fa_for_all_security_and_backup_settings'; static const selectedCake2FAPreset = 'selected_cake_2fa_preset'; + static const totpSecretKey = 'totp_secret_key'; } diff --git a/lib/exchange/exchange_provider_description.dart b/lib/exchange/exchange_provider_description.dart index e545f69ce..151d018e0 100644 --- a/lib/exchange/exchange_provider_description.dart +++ b/lib/exchange/exchange_provider_description.dart @@ -24,7 +24,10 @@ class ExchangeProviderDescription extends EnumerableItem with Serializable< static const trocador = ExchangeProviderDescription(title: 'Trocador', raw: 5, image: 'assets/images/trocador.png'); - static const all = ExchangeProviderDescription(title: 'All trades', raw: 6, image: ''); + static const exolix = + ExchangeProviderDescription(title: 'Exolix', raw: 6, image: 'assets/images/exolix.png'); + + static const all = ExchangeProviderDescription(title: 'All trades', raw: 7, image: ''); static ExchangeProviderDescription deserialize({required int raw}) { switch (raw) { @@ -41,6 +44,8 @@ class ExchangeProviderDescription extends EnumerableItem with Serializable< case 5: return trocador; case 6: + return exolix; + case 7: return all; default: throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize'); diff --git a/lib/exchange/exolix/exolix_exchange_provider.dart b/lib/exchange/exolix/exolix_exchange_provider.dart new file mode 100644 index 000000000..0768f1160 --- /dev/null +++ b/lib/exchange/exolix/exolix_exchange_provider.dart @@ -0,0 +1,294 @@ +import 'dart:convert'; +import 'package:cake_wallet/exchange/trade_not_found_exeption.dart'; +import 'package:http/http.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/exchange/exchange_pair.dart'; +import 'package:cake_wallet/exchange/exchange_provider.dart'; +import 'package:cake_wallet/exchange/limits.dart'; +import 'package:cake_wallet/exchange/trade.dart'; +import 'package:cake_wallet/exchange/trade_request.dart'; +import 'package:cake_wallet/exchange/trade_state.dart'; +import 'package:cake_wallet/exchange/exolix/exolix_request.dart'; +import 'package:cake_wallet/exchange/exchange_provider_description.dart'; + +class ExolixExchangeProvider extends ExchangeProvider { + ExolixExchangeProvider() : super(pairList: _supportedPairs()); + + static final apiKey = secrets.exolixApiKey; + static const apiBaseUrl = 'exolix.com'; + static const transactionsPath = '/api/v2/transactions'; + static const ratePath = '/api/v2/rate'; + + static const List _notSupported = [ + CryptoCurrency.usdt, + CryptoCurrency.xhv, + CryptoCurrency.btt, + CryptoCurrency.firo, + CryptoCurrency.zaddr, + CryptoCurrency.xvg, + CryptoCurrency.kmd, + CryptoCurrency.paxg, + CryptoCurrency.rune, + CryptoCurrency.scrt, + CryptoCurrency.btcln, + CryptoCurrency.cro, + CryptoCurrency.ftm, + CryptoCurrency.frax, + CryptoCurrency.gusd, + CryptoCurrency.gtc, + CryptoCurrency.weth, + ]; + + static List _supportedPairs() { + final supportedCurrencies = + CryptoCurrency.all.where((element) => !_notSupported.contains(element)).toList(); + + return supportedCurrencies + .map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true))) + .expand((i) => i) + .toList(); + } + + @override + String get title => 'Exolix'; + + @override + bool get isAvailable => true; + + @override + bool get isEnabled => true; + + @override + bool get supportsFixedRate => true; + + @override + ExchangeProviderDescription get description => ExchangeProviderDescription.exolix; + + @override + Future checkIsAvailable() async => true; + + static String getRateType(bool isFixedRate) => isFixedRate ? 'fixed' : 'float'; + + @override + Future fetchLimits( + {required CryptoCurrency from, + required CryptoCurrency to, + required bool isFixedRateMode}) async { + final params = { + 'rateType': getRateType(isFixedRateMode), + 'amount': '1', + }; + if (isFixedRateMode) { + params['coinFrom'] = _normalizeCurrency(to); + params['coinTo'] = _normalizeCurrency(from); + params['networkFrom'] = _networkFor(to); + params['networkTo'] = _networkFor(from); + } else { + params['coinFrom'] = _normalizeCurrency(from); + params['coinTo'] = _normalizeCurrency(to); + params['networkFrom'] = _networkFor(from); + params['networkTo'] = _networkFor(to); + } + final uri = Uri.https(apiBaseUrl, ratePath, params); + final response = await get(uri); + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as Map; + return Limits(min: responseJSON['minAmount'] as double?); + } + + @override + Future createTrade({required TradeRequest request, required bool isFixedRateMode}) async { + final _request = request as ExolixRequest; + + final headers = {'Content-Type': 'application/json'}; + final body = { + 'coinFrom': _normalizeCurrency(_request.from), + 'coinTo': _normalizeCurrency(_request.to), + 'networkFrom': _networkFor(_request.from), + 'networkTo': _networkFor(_request.to), + 'withdrawalAddress': _request.address, + 'refundAddress': _request.refundAddress, + 'rateType': getRateType(isFixedRateMode), + 'apiToken': apiKey, + }; + + if (isFixedRateMode) { + body['withdrawalAmount'] = _request.toAmount; + } else { + body['amount'] = _request.fromAmount; + } + + final uri = Uri.https(apiBaseUrl, transactionsPath); + final response = await post(uri, headers: headers, body: json.encode(body)); + + if (response.statusCode == 400) { + final responseJSON = json.decode(response.body) as Map; + final errors = responseJSON['errors'] as Map; + final errorMessage = errors.values.join(', '); + throw Exception(errorMessage); + } + + if (response.statusCode != 200 && response.statusCode != 201) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as Map; + final id = responseJSON['id'] as String; + final inputAddress = responseJSON['depositAddress'] as String; + final refundAddress = responseJSON['refundAddress'] as String?; + final extraId = responseJSON['depositExtraId'] as String?; + final payoutAddress = responseJSON['withdrawalAddress'] as String; + final amount = responseJSON['amount'].toString(); + + return Trade( + id: id, + from: _request.from, + to: _request.to, + provider: description, + inputAddress: inputAddress, + refundAddress: refundAddress, + extraId: extraId, + createdAt: DateTime.now(), + amount: amount, + state: TradeState.created, + payoutAddress: payoutAddress); + } + + @override + Future findTradeById({required String id}) async { + final findTradeByIdPath = transactionsPath + '/$id'; + final uri = Uri.https(apiBaseUrl, findTradeByIdPath); + final response = await get(uri); + + if (response.statusCode == 404) { + throw TradeNotFoundException(id, provider: description); + } + + if (response.statusCode == 400) { + final responseJSON = json.decode(response.body) as Map; + final errors = responseJSON['errors'] as Map; + final errorMessage = errors.values.join(', '); + + throw TradeNotFoundException(id, provider: description, description: errorMessage); + } + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as Map; + final coinFrom = responseJSON['coinFrom']['coinCode'] as String; + final from = CryptoCurrency.fromString(coinFrom); + final coinTo = responseJSON['coinTo']['coinCode'] as String; + final to = CryptoCurrency.fromString(coinTo); + final inputAddress = responseJSON['depositAddress'] as String; + final amount = responseJSON['amount'].toString(); + final status = responseJSON['status'] as String; + final state = TradeState.deserialize(raw: _prepareStatus(status)); + final extraId = responseJSON['depositExtraId'] as String?; + final outputTransaction = responseJSON['hashOut']['hash'] as String?; + final payoutAddress = responseJSON['withdrawalAddress'] as String; + + return Trade( + id: id, + from: from, + to: to, + provider: description, + inputAddress: inputAddress, + amount: amount, + state: state, + extraId: extraId, + outputTransaction: outputTransaction, + payoutAddress: payoutAddress); + } + + @override + Future fetchRate( + {required CryptoCurrency from, + required CryptoCurrency to, + required double amount, + required bool isFixedRateMode, + required bool isReceiveAmount}) async { + try { + if (amount == 0) { + return 0.0; + } + + final params = { + 'coinFrom': _normalizeCurrency(from), + 'coinTo': _normalizeCurrency(to), + 'networkFrom': _networkFor(from), + 'networkTo': _networkFor(to), + 'rateType': getRateType(isFixedRateMode), + }; + + if (isReceiveAmount) { + params['withdrawalAmount'] = amount.toString(); + } else { + params['amount'] = amount.toString(); + } + + final uri = Uri.https(apiBaseUrl, ratePath, params); + final response = await get(uri); + final responseJSON = json.decode(response.body) as Map; + + if (response.statusCode != 200) { + final message = responseJSON['message'] as String?; + throw Exception(message); + } + + final rate = responseJSON['rate'] as double; + + return rate; + } catch (e) { + print(e.toString()); + return 0.0; + } + } + + String _prepareStatus(String status) { + switch (status) { + case 'deleted': + case 'error': + return 'overdue'; + default: + return status; + } + } + + String _networkFor(CryptoCurrency currency) { + switch (currency) { + case CryptoCurrency.arb: + return 'ARBITRUM'; + default: + return currency.tag != null ? _normalizeTag(currency.tag!) : currency.title; + } + } + + String _normalizeCurrency(CryptoCurrency currency) { + switch (currency) { + case CryptoCurrency.nano: + return 'XNO'; + case CryptoCurrency.bttc: + return 'BTT'; + case CryptoCurrency.zec: + return 'ZEC'; + default: + return currency.title; + } + } + + String _normalizeTag(String tag) { + switch (tag) { + case 'POLY': + return 'Polygon'; + default: + return tag; + } + } +} diff --git a/lib/exchange/exolix/exolix_request.dart b/lib/exchange/exolix/exolix_request.dart new file mode 100644 index 000000000..e97ffa386 --- /dev/null +++ b/lib/exchange/exolix/exolix_request.dart @@ -0,0 +1,20 @@ +import 'package:flutter/foundation.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/exchange/trade_request.dart'; + +class ExolixRequest extends TradeRequest { + ExolixRequest( + {required this.from, + required this.to, + required this.address, + required this.fromAmount, + required this.toAmount, + required this.refundAddress}); + + CryptoCurrency from; + CryptoCurrency to; + String address; + String fromAmount; + String toAmount; + String refundAddress; +} diff --git a/lib/exchange/trade_state.dart b/lib/exchange/trade_state.dart index 98737339c..ebf74ce7a 100644 --- a/lib/exchange/trade_state.dart +++ b/lib/exchange/trade_state.dart @@ -35,6 +35,15 @@ class TradeState extends EnumerableItem with Serializable { static const completed = TradeState(raw: 'completed', title: 'Completed'); static const settling = TradeState(raw: 'settling', title: 'Settlement in progress'); static const settled = TradeState(raw: 'settled', title: 'Settlement completed'); + static const wait = TradeState(raw: 'wait', title: 'Waiting'); + static const overdue = TradeState(raw: 'overdue', title: 'Overdue'); + static const refund = TradeState(raw: 'refund', title: 'Refund'); + static const refunded = TradeState(raw: 'refunded', title: 'Refunded'); + static const confirmation = TradeState(raw: 'confirmation', title: 'Confirmation'); + static const confirmed = TradeState(raw: 'confirmed', title: 'Confirmed'); + 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) { case 'pending': @@ -77,6 +86,24 @@ class TradeState extends EnumerableItem with Serializable { return failed; case 'completed': return completed; + case 'wait': + return wait; + case 'overdue': + return overdue; + case 'refund': + return refund; + case 'refunded': + return refunded; + case 'confirmation': + return confirmation; + case 'confirmed': + return confirmed; + case 'exchanging': + return exchanging; + case 'sending': + return sending; + case 'success': + return success; default: throw Exception('Unexpected token: $raw in TradeState deserialize'); } diff --git a/lib/src/screens/dashboard/widgets/trade_row.dart b/lib/src/screens/dashboard/widgets/trade_row.dart index a42593f24..7f570b98e 100644 --- a/lib/src/screens/dashboard/widgets/trade_row.dart +++ b/lib/src/screens/dashboard/widgets/trade_row.dart @@ -94,6 +94,9 @@ class TradeRow extends StatelessWidget { borderRadius: BorderRadius.circular(50), child: Image.asset('assets/images/trocador.png', width: 36, height: 36)); break; + case ExchangeProviderDescription.exolix: + image = Image.asset('assets/images/exolix.png', width: 36, height: 36); + break; default: image = null; } diff --git a/lib/src/screens/setup_2fa/setup_2fa.dart b/lib/src/screens/setup_2fa/setup_2fa.dart index a74152e4f..895fbb9c0 100644 --- a/lib/src/screens/setup_2fa/setup_2fa.dart +++ b/lib/src/screens/setup_2fa/setup_2fa.dart @@ -53,8 +53,10 @@ class Setup2FAPage extends BasePage { SizedBox(height: 86), SettingsCellWithArrow( title: S.current.setup_totp_recommended, - handler: (_) => Navigator.of(context) - .pushReplacementNamed(Routes.setup_2faQRPage), + handler: (_) { + setup2FAViewModel.generateSecretKey(); + return Navigator.of(context).pushReplacementNamed(Routes.setup_2faQRPage); + }, ), StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), ], diff --git a/lib/src/screens/setup_2fa/setup_2fa_qr_page.dart b/lib/src/screens/setup_2fa/setup_2fa_qr_page.dart index 82a079b39..43dbab05f 100644 --- a/lib/src/screens/setup_2fa/setup_2fa_qr_page.dart +++ b/lib/src/screens/setup_2fa/setup_2fa_qr_page.dart @@ -14,7 +14,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:qr_flutter/qr_flutter.dart' as qr; - class Setup2FAQRPage extends BasePage { Setup2FAQRPage({required this.setup2FAViewModel}); @@ -25,7 +24,6 @@ class Setup2FAQRPage extends BasePage { @override Widget body(BuildContext context) { - final copyImage = Image.asset( 'assets/images/copy_content.png', height: 12, @@ -91,7 +89,7 @@ class Setup2FAQRPage extends BasePage { ), SizedBox(height: 8), Text( - '${setup2FAViewModel.secretKey}', + '${setup2FAViewModel.totpSecretKey}', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, @@ -110,7 +108,63 @@ class Setup2FAQRPage extends BasePage { child: InkWell( onTap: () { ClipboardUtil.setSensitiveDataToClipboard( - ClipboardData(text: '${setup2FAViewModel.secretKey}')); + ClipboardData(text: '${setup2FAViewModel.totpSecretKey}')); + showBar(context, S.of(context).copied_to_clipboard); + }, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: Color(0xFFF2F0FA), + ), + child: copyImage, + ), + ), + ) + ], + ), + SizedBox(height: 8), + StandardListSeparator(), + SizedBox(height: 13), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + S.current.totp_auth_url, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Palette.darkGray, + height: 1.8333, + ), + ), + SizedBox(height: 8), + Text( + '${setup2FAViewModel.totpVersionOneLink}', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + height: 1.375, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + SizedBox(width: 8), + Container( + width: 32, + height: 32, + child: InkWell( + onTap: () { + ClipboardUtil.setSensitiveDataToClipboard( + ClipboardData(text: '${setup2FAViewModel.totpVersionOneLink}')); showBar(context, S.of(context).copied_to_clipboard); }, child: Container( @@ -129,13 +183,10 @@ class Setup2FAQRPage extends BasePage { Spacer(), PrimaryButton( onPressed: () { - Navigator.of(context).pushReplacementNamed( - Routes.totpAuthCodePage, + Navigator.of(context).pushReplacementNamed(Routes.totpAuthCodePage, arguments: TotpAuthArgumentsModel( isForSetup: true, - ) - - ); + )); }, text: S.current.continue_text, color: Theme.of(context).primaryColor, diff --git a/lib/src/screens/trade_details/trade_details_page.dart b/lib/src/screens/trade_details/trade_details_page.dart index 17683c600..1bb8872e8 100644 --- a/lib/src/screens/trade_details/trade_details_page.dart +++ b/lib/src/screens/trade_details/trade_details_page.dart @@ -51,10 +51,11 @@ class TradeDetailsPageBodyState extends State { @override Widget build(BuildContext context) { return Observer(builder: (_) { - // FIX-ME: Added `context` it was not used here before, maby bug ? + int itemsCount = tradeDetailsViewModel.items.length; + return SectionStandardList( sectionCount: 1, - itemCounter: (int _) => tradeDetailsViewModel.items.length, + itemCounter: (int _) => itemsCount, itemBuilder: (__, index) { final item = tradeDetailsViewModel.items[index]; diff --git a/lib/store/dashboard/trade_filter_store.dart b/lib/store/dashboard/trade_filter_store.dart index c772a35d6..799e8b951 100644 --- a/lib/store/dashboard/trade_filter_store.dart +++ b/lib/store/dashboard/trade_filter_store.dart @@ -13,7 +13,8 @@ abstract class TradeFilterStoreBase with Store { displaySideShift = true, displayMorphToken = true, displaySimpleSwap = true, - displayTrocador = true; + displayTrocador = true, + displayExolix = true; @observable bool displayXMRTO; @@ -33,8 +34,11 @@ abstract class TradeFilterStoreBase with Store { @observable bool displayTrocador; + @observable + bool displayExolix; + @computed - bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap && displayTrocador; + bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap && displayTrocador && displayExolix; @action void toggleDisplayExchange(ExchangeProviderDescription provider) { @@ -56,7 +60,10 @@ abstract class TradeFilterStoreBase with Store { break; case ExchangeProviderDescription.trocador: displayTrocador = !displayTrocador; - break; + break; + case ExchangeProviderDescription.exolix: + displayExolix = !displayExolix; + break; case ExchangeProviderDescription.all: if (displayAllTrades) { displayChangeNow = false; @@ -65,6 +72,7 @@ abstract class TradeFilterStoreBase with Store { displayMorphToken = false; displaySimpleSwap = false; displayTrocador = false; + displayExolix = false; } else { displayChangeNow = true; displaySideShift = true; @@ -72,6 +80,7 @@ abstract class TradeFilterStoreBase with Store { displayMorphToken = true; displaySimpleSwap = true; displayTrocador = true; + displayExolix = true; } break; } @@ -98,7 +107,8 @@ abstract class TradeFilterStoreBase with Store { ||(displaySimpleSwap && item.trade.provider == ExchangeProviderDescription.simpleSwap) - ||(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador)) + ||(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador) + ||(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix)) .toList() : _trades; } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 1df1f113d..ec1e15acc 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -281,14 +281,13 @@ abstract class SettingsStoreBase with Store { reaction( (_) => useTOTP2FA, (bool use) => sharedPreferences.setBool(PreferencesKey.useTOTP2FA, use)); + reaction((_) => totpSecretKey, + (String totpKey) => sharedPreferences.setString(PreferencesKey.totpSecretKey, totpKey)); reaction( (_) => numberOfFailedTokenTrials, (int failedTokenTrail) => sharedPreferences.setInt(PreferencesKey.failedTotpTokenTrials, failedTokenTrail)); - reaction((_) => totpSecretKey, - (String totpKey) => sharedPreferences.setString(PreferencesKey.totpSecretKey, totpKey)); - reaction( (_) => shouldShowMarketPlaceInDashboard, (bool value) => @@ -424,15 +423,10 @@ abstract class SettingsStoreBase with Store { bool shouldRequireTOTP2FAForAllSecurityAndBackupSettings; @observable - String totpSecretKey; - - @computed - String get totpVersionOneLink { - return 'otpauth://totp/Cake%20Wallet:$deviceName?secret=$totpSecretKey&issuer=Cake%20Wallet&algorithm=SHA512&digits=8&period=30'; - } + bool useTOTP2FA; @observable - bool useTOTP2FA; + String totpSecretKey; @observable int numberOfFailedTokenTrials; @@ -577,8 +571,8 @@ abstract class SettingsStoreBase with Store { final shouldRequireTOTP2FAForAllSecurityAndBackupSettings = sharedPreferences .getBool(PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings) ?? false; - final totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? ''; final useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? false; + final totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? ''; final tokenTrialNumber = sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? 0; final shouldShowMarketPlaceInDashboard = sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ?? true; @@ -680,8 +674,8 @@ abstract class SettingsStoreBase with Store { initialFiatMode: currentFiatApiMode, initialAllowBiometricalAuthentication: allowBiometricalAuthentication, initialCake2FAPresetOptions: selectedCake2FAPreset, - initialTotpSecretKey: totpSecretKey, initialUseTOTP2FA: useTOTP2FA, + initialTotpSecretKey: totpSecretKey, initialFailedTokenTrial: tokenTrialNumber, initialExchangeStatus: exchangeStatus, initialTheme: savedTheme, @@ -755,9 +749,8 @@ abstract class SettingsStoreBase with Store { shouldSaveRecipientAddress = sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? shouldSaveRecipientAddress; - totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? totpSecretKey; useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? useTOTP2FA; - + totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? totpSecretKey; numberOfFailedTokenTrials = sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials; isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure; diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index de8195101..0ec39bc18 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -162,6 +162,7 @@ class ExceptionHandler { "Handshake error in client", "Error while launching http", "OS Error: Network is unreachable", + "ClientException: Write failed, uri=https:", ]; static Future _addDeviceInfo(File file) async { diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 9998eb8be..777db8f8d 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -99,6 +99,11 @@ abstract class DashboardViewModelBase with Store { caption: ExchangeProviderDescription.trocador.title, onChanged: () => tradeFilterStore .toggleDisplayExchange(ExchangeProviderDescription.trocador)), + FilterItem( + value: () => tradeFilterStore.displayExolix, + caption: ExchangeProviderDescription.exolix.title, + onChanged: () => tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.exolix)), ] }, subname = '', diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index cfabd994f..346844171 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart'; @@ -53,6 +54,9 @@ abstract class ExchangeTradeViewModelBase with Store { case ExchangeProviderDescription.trocador: _provider = TrocadorExchangeProvider(); break; + case ExchangeProviderDescription.exolix: + _provider = ExolixExchangeProvider(); + break; } _updateItems(); diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 2e90a3a33..bd370cc54 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -6,6 +6,8 @@ import 'package:cake_wallet/core/wallet_change_listener_view_model.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/wallet_contact.dart'; +import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart'; +import 'package:cake_wallet/exchange/exolix/exolix_request.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; @@ -151,6 +153,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), TrocadorExchangeProvider(useTorOnly: _useTorOnly), + ExolixExchangeProvider(), ]; @observable @@ -547,6 +550,17 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with amount = isFixedRateMode ? receiveAmount : depositAmount; } + if (provider is ExolixExchangeProvider) { + request = ExolixRequest( + from: depositCurrency, + to: receiveCurrency, + fromAmount: depositAmount.replaceAll(',', '.'), + toAmount: receiveAmount.replaceAll(',', '.'), + refundAddress: depositAddress, + address: receiveAddress); + amount = isFixedRateMode ? receiveAmount : depositAmount; + } + amount = amount.replaceAll(',', '.'); if (limitsState is LimitsLoadedSuccessfully) { diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 329b3c4ad..72ce9fdb2 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -30,6 +30,7 @@ import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/generated/i18n.dart'; part 'send_view_model.g.dart'; @@ -325,7 +326,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor state = TransactionCommitted(); } catch (e) { - state = FailureState(e.toString()); + String translatedError = translateErrorMessage(e.toString(), wallet.type, wallet.currency); + state = FailureState(translatedError); } } @@ -412,4 +414,14 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor selectedCryptoCurrency = wallet.currency; } } + + String translateErrorMessage(String error, WalletType walletType, CryptoCurrency currency,) { + if (walletType == WalletType.ethereum || walletType == WalletType.haven) { + if (error.contains('gas required exceeds allowance (0)') || error.contains('insufficient funds for gas')) { + return S.current.do_not_have_enough_gas_asset(currency.toString()); + } + } + + return error; + } } diff --git a/lib/view_model/set_up_2fa_viewmodel.dart b/lib/view_model/set_up_2fa_viewmodel.dart index 0b4b614ab..eacd3128d 100644 --- a/lib/view_model/set_up_2fa_viewmodel.dart +++ b/lib/view_model/set_up_2fa_viewmodel.dart @@ -27,7 +27,6 @@ abstract class Setup2FAViewModelBase with Store { unhighlightTabs = false, selected2FASettings = ObservableList(), state = InitialExecutionState() { - _getRandomBase32SecretKey(); selectCakePreset(selectedCake2FAPreset); reaction((_) => state, _saveLastAuthTime); } @@ -36,9 +35,12 @@ abstract class Setup2FAViewModelBase with Store { static const banTimeout = 180; // 3 minutes final banTimeoutKey = S.current.auth_store_ban_timeout; - String get secretKey => _settingsStore.totpSecretKey; String get deviceName => _settingsStore.deviceName; - String get totpVersionOneLink => _settingsStore.totpVersionOneLink; + + @computed + String get totpSecretKey => _settingsStore.totpSecretKey; + + String totpVersionOneLink = ''; @observable ExecutionState state; @@ -84,9 +86,14 @@ abstract class Setup2FAViewModelBase with Store { bool get shouldRequireTOTP2FAForAllSecurityAndBackupSettings => _settingsStore.shouldRequireTOTP2FAForAllSecurityAndBackupSettings; - void _getRandomBase32SecretKey() { - final randomBase32Key = Utils.generateRandomBase32SecretKey(16); - _setBase32SecretKey(randomBase32Key); + @action + void generateSecretKey() { + final _totpSecretKey = Utils.generateRandomBase32SecretKey(16); + + totpVersionOneLink = + 'otpauth://totp/Cake%20Wallet:$deviceName?secret=$_totpSecretKey&issuer=Cake%20Wallet&algorithm=SHA512&digits=8&period=30'; + + setTOTPSecretKey(_totpSecretKey); } @action @@ -95,15 +102,10 @@ abstract class Setup2FAViewModelBase with Store { } @action - void _setBase32SecretKey(String value) { + void setTOTPSecretKey(String value) { _settingsStore.totpSecretKey = value; } - @action - void clearBase32SecretKey() { - _settingsStore.totpSecretKey = ''; - } - Duration? banDuration() { final unbanTimestamp = _sharedPreferences.getInt(banTimeoutKey); @@ -145,7 +147,7 @@ abstract class Setup2FAViewModelBase with Store { } final result = Utils.verify( - secretKey: secretKey, + secretKey: totpSecretKey, otp: otpText, ); @@ -156,7 +158,6 @@ abstract class Setup2FAViewModelBase with Store { } else { final value = _settingsStore.numberOfFailedTokenTrials + 1; adjustTokenTrialNumber(value); - print(value); if (_failureCounter >= maxFailedTrials) { final banDuration = await ban(); state = AuthenticationBanned( diff --git a/lib/view_model/support_view_model.dart b/lib/view_model/support_view_model.dart index d3b14c59b..ccef76154 100644 --- a/lib/view_model/support_view_model.dart +++ b/lib/view_model/support_view_model.dart @@ -53,6 +53,11 @@ abstract class SupportViewModelBase with Store { icon: 'assets/images/simpleSwap.png', linkTitle: 'support@simpleswap.io', link: 'mailto:support@simpleswap.io'), + LinkListItem( + title: 'Exolix', + icon: 'assets/images/exolix.png', + linkTitle: 'support@exolix.com', + link: 'mailto:support@exolix.com'), if (!isMoneroOnly) ... [ LinkListItem( title: 'Wyre', diff --git a/lib/view_model/trade_details_view_model.dart b/lib/view_model/trade_details_view_model.dart index c0b1ac461..393629237 100644 --- a/lib/view_model/trade_details_view_model.dart +++ b/lib/view_model/trade_details_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart'; import 'package:cake_wallet/exchange/exchange_provider.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; +import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart'; import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; @@ -54,6 +55,9 @@ abstract class TradeDetailsViewModelBase with Store { case ExchangeProviderDescription.trocador: _provider = TrocadorExchangeProvider(); break; + case ExchangeProviderDescription.exolix: + _provider = ExolixExchangeProvider(); + break; } _updateItems(); @@ -157,6 +161,12 @@ abstract class TradeDetailsViewModelBase with Store { items.add(StandartListItem( title: '${trade.providerName} ${S.current.password}', value: trade.password ?? '')); } + + if (trade.provider == ExchangeProviderDescription.exolix) { + final buildURL = 'https://exolix.com/transaction/${trade.id.toString()}'; + items.add( + TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => _launchUrl(buildURL))); + } } void _launchUrl(String url) { diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 24ba6a0a3..b77d48034 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -694,5 +694,9 @@ "onramper_option_description": "شراء بسرعة التشفير مع العديد من طرق الدفع. متوفر في معظم البلدان. ينتشر وتختلف الرسوم.", "default_buy_provider": "مزود شراء الافتراضي", "ask_each_time": "اسأل في كل مرة", - "buy_provider_unavailable": "مزود حاليا غير متوفر." -} \ No newline at end of file + "buy_provider_unavailable": "مزود حاليا غير متوفر.", + + "do_not_have_enough_gas_asset": "ليس لديك ما يكفي من ${currency} لإجراء معاملة وفقًا لشروط شبكة blockchain الحالية. أنت بحاجة إلى المزيد من ${currency} لدفع رسوم شبكة blockchain، حتى لو كنت ترسل أصلًا مختلفًا.", + "totp_auth_url": " TOTP ﺔﻗﺩﺎﺼﻤﻟ URL ﻥﺍﻮﻨﻋ" +} + diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 15d65558d..0186a66ed 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -690,5 +690,7 @@ "onramper_option_description": "Бързо купувайте криптовалута с много методи за плащане. Предлага се в повечето страни. Разпространенията и таксите варират.", "default_buy_provider": "Доставчик по подразбиране купува", "ask_each_time": "Питайте всеки път", - "buy_provider_unavailable": "Понастоящем доставчик не е наличен." -} \ No newline at end of file + "buy_provider_unavailable": "Понастоящем доставчик не е наличен.", + "do_not_have_enough_gas_asset": "Нямате достатъчно ${currency}, за да извършите транзакция с текущите условия на блокчейн мрежата. Имате нужда от повече ${currency}, за да платите таксите за блокчейн мрежа, дори ако изпращате различен актив.", + "totp_auth_url": "TOTP AUTH URL" +} diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 9c212657b..13c16c878 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -690,5 +690,7 @@ "onramper_option_description": "Rychle si koupte krypto s mnoha metodami plateb. K dispozici ve většině zemí. Rozpětí a poplatky se liší.", "default_buy_provider": "Výchozí poskytovatel nákupu", "ask_each_time": "Zeptejte se pokaždé", - "buy_provider_unavailable": "Poskytovatel aktuálně nedostupný." -} \ No newline at end of file + "buy_provider_unavailable": "Poskytovatel aktuálně nedostupný.", + "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.", + "totp_auth_url": "URL AUTH TOTP" +} diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 086276804..4bc3dc527 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -698,5 +698,7 @@ "onramper_option_description": "Kaufen Sie schnell Krypto mit vielen Zahlungsmethoden. In den meisten Ländern erhältlich. Spreads und Gebühren variieren.", "default_buy_provider": "Standard-Kaufanbieter", "ask_each_time": "Jedes Mal fragen", - "buy_provider_unavailable": "Anbieter derzeit nicht verfügbar." -} \ No newline at end of file + "buy_provider_unavailable": "Anbieter derzeit nicht verfügbar.", + "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.", + "totp_auth_url": "TOTP-Auth-URL" +} diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 9baa30e22..31b8152db 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -699,5 +699,7 @@ "default_buy_provider": "Default Buy Provider", "ask_each_time": "Ask each time", "robinhood_option_description": "Buy and transfer instantly using your debit card, bank account, or Robinhood balance. USA only.", - "buy_provider_unavailable": "Provider currently unavailable." -} \ No newline at end of file + "buy_provider_unavailable": "Provider currently unavailable.", + "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.", + "totp_auth_url": "TOTP AUTH URL" +} diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 5deccfbc6..4d4ac2d80 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -698,5 +698,7 @@ "onramper_option_description": "Compre rápidamente cripto con muchos métodos de pago. Disponible en la mayoría de los países. Los diferenciales y las tarifas varían.", "default_buy_provider": "Proveedor de compra predeterminado", "ask_each_time": "Pregunta cada vez", - "buy_provider_unavailable": "Proveedor actualmente no disponible." -} \ No newline at end of file + "buy_provider_unavailable": "Proveedor actualmente no disponible.", + "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.", + "totp_auth_url": "URL de autenticación TOTP" +} diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 516e9490e..32d31f706 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -698,5 +698,7 @@ "onramper_option_description": "Achetez rapidement la crypto avec de nombreux méthodes de paiement. Disponible dans la plupart des pays. Les écarts et les frais varient.", "default_buy_provider": "Fournisseur d'achat par défaut", "ask_each_time": "Demandez à chaque fois", - "buy_provider_unavailable": "Fournisseur actuellement indisponible." -} \ No newline at end of file + "buy_provider_unavailable": "Fournisseur actuellement indisponible.", + "do_not_have_enough_gas_asset": "Vous n'avez pas assez de ${currency} pour effectuer une transaction avec les conditions actuelles du réseau blockchain. Vous avez besoin de plus de ${currency} pour payer les frais du réseau blockchain, même si vous envoyez un actif différent.", + "totp_auth_url": "URL D'AUTORISATION TOTP" +} diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 0f4d0ab7d..afdcaf66d 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -676,5 +676,7 @@ "onramper_option_description": "Da sauri sayi Crypto tare da hanyoyin biyan kuɗi da yawa. Akwai a yawancin ƙasashe. Yaduwa da kudade sun bambanta.", "default_buy_provider": "Tsohuwar Siyarwa", "ask_each_time": "Tambaya kowane lokaci", - "buy_provider_unavailable": "Mai ba da kyauta a halin yanzu babu." -} \ No newline at end of file + "buy_provider_unavailable": "Mai ba da kyauta a halin yanzu babu.", + "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.", + "totp_auth_url": "TOTP AUTH URL" +} diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 826a9cca3..fd0c0c1f5 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -698,5 +698,7 @@ "onramper_option_description": "जल्दी से कई भुगतान विधियों के साथ क्रिप्टो खरीदें। अधिकांश देशों में उपलब्ध है। फैलता है और फीस अलग -अलग होती है।", "default_buy_provider": "डिफ़ॉल्ट खरीद प्रदाता", "ask_each_time": "हर बार पूछें", - "buy_provider_unavailable": "वर्तमान में प्रदाता अनुपलब्ध है।" -} \ No newline at end of file + "buy_provider_unavailable": "वर्तमान में प्रदाता अनुपलब्ध है।", + "do_not_have_enough_gas_asset": "वर्तमान ब्लॉकचेन नेटवर्क स्थितियों में लेनदेन करने के लिए आपके पास पर्याप्त ${currency} नहीं है। ब्लॉकचेन नेटवर्क शुल्क का भुगतान करने के लिए आपको अधिक ${currency} की आवश्यकता है, भले ही आप एक अलग संपत्ति भेज रहे हों।", + "totp_auth_url": "TOTP प्रामाणिक यूआरएल" +} diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 4fb5d6147..2d337f434 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -698,5 +698,7 @@ "onramper_option_description": "Brzo kupite kriptovalute s mnogim načinima plaćanja. Dostupno u većini zemalja. Širenja i naknade variraju.", "default_buy_provider": "Zadani davatelj kupnje", "ask_each_time": "Pitajte svaki put", - "buy_provider_unavailable": "Davatelj trenutno nije dostupan." + "buy_provider_unavailable": "Davatelj trenutno nije dostupan.", + "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.", + "totp_auth_url": "TOTP AUTH URL" } \ No newline at end of file diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index e3f947ed3..cc23a004c 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -686,5 +686,7 @@ "onramper_option_description": "Beli crypto dengan cepat dengan banyak metode pembayaran. Tersedia di sebagian besar negara. Spread dan biaya bervariasi.", "default_buy_provider": "Penyedia beli default", "ask_each_time": "Tanyakan setiap kali", - "buy_provider_unavailable": "Penyedia saat ini tidak tersedia." + "buy_provider_unavailable": "Penyedia saat ini tidak tersedia.", + "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.", + "totp_auth_url": "URL Otentikasi TOTP" } \ No newline at end of file diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 92567423e..0435a6e70 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -698,5 +698,7 @@ "onramper_option_description": "Acquista rapidamente la criptovaluta con molti metodi di pagamento. Disponibile nella maggior parte dei paesi. Gli spread e le commissioni variano.", "default_buy_provider": "Provider di acquisto predefinito", "ask_each_time": "Chiedi ogni volta", - "buy_provider_unavailable": "Provider attualmente non disponibile." -} \ No newline at end of file + "buy_provider_unavailable": "Provider attualmente non disponibile.", + "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.", + "totp_auth_url": "URL DI AUT. TOTP" +} diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 8822abcc8..7ad6ddf6c 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -698,5 +698,7 @@ "onramper_option_description": "多くの支払い方法で暗号をすばやく購入してください。ほとんどの国で利用可能です。スプレッドと料金は異なります。", "default_buy_provider": "デフォルトの購入プロバイダー", "ask_each_time": "毎回尋ねてください", - "buy_provider_unavailable": "現在、プロバイダーは利用できません。" -} \ No newline at end of file + "buy_provider_unavailable": "現在、プロバイダーは利用できません。", + "do_not_have_enough_gas_asset": "現在のブロックチェーン ネットワークの状況では、トランザクションを行うのに十分な ${currency} がありません。別のアセットを送信する場合でも、ブロックチェーン ネットワーク料金を支払うにはさらに ${currency} が必要です。", + "totp_auth_url": "TOTP認証URL" +} diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 7a47fed84..b3d28e578 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -698,5 +698,7 @@ "onramper_option_description": "많은 결제 방법으로 암호화를 신속하게 구입하십시오. 대부분의 국가에서 사용할 수 있습니다. 스프레드와 수수료는 다양합니다.", "default_buy_provider": "기본 구매 제공자", "ask_each_time": "매번 물어보십시오", - "buy_provider_unavailable": "제공자는 현재 사용할 수 없습니다." -} \ No newline at end of file + "buy_provider_unavailable": "제공자는 현재 사용할 수 없습니다.", + "do_not_have_enough_gas_asset": "현재 블록체인 네트워크 조건으로 거래를 하기에는 ${currency}이(가) 충분하지 않습니다. 다른 자산을 보내더라도 블록체인 네트워크 수수료를 지불하려면 ${currency}가 더 필요합니다.", + "totp_auth_url": "TOTP 인증 URL" +} diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index d7c5f5ac4..e2cd80c04 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -696,5 +696,7 @@ "onramper_option_description": "ငွေပေးချေမှုနည်းလမ်းများစွာဖြင့် Crypto ကိုလျင်မြန်စွာ 0 ယ်ပါ။ နိုင်ငံအများစုတွင်ရရှိနိုင်ပါသည်။ ဖြန့်ဖြူးနှင့်အခကြေးငွေကွဲပြားခြားနားသည်။", "default_buy_provider": "Default Provider ကိုဝယ်ပါ", "ask_each_time": "တစ်ခုချင်းစီကိုအချိန်မေးပါ", - "buy_provider_unavailable": "လက်ရှိတွင်လက်ရှိမရနိုင်ပါ။" -} \ No newline at end of file + "buy_provider_unavailable": "လက်ရှိတွင်လက်ရှိမရနိုင်ပါ။", + "do_not_have_enough_gas_asset": "လက်ရှိ blockchain ကွန်ရက်အခြေအနေများနှင့် အရောင်းအဝယ်ပြုလုပ်ရန် သင့်တွင် ${currency} လုံလောက်မှုမရှိပါ။ သင်သည် မတူညီသော ပိုင်ဆိုင်မှုတစ်ခုကို ပေးပို့နေသော်လည်း blockchain ကွန်ရက်အခကြေးငွေကို ပေးဆောင်ရန် သင်သည် နောက်ထပ် ${currency} လိုအပ်ပါသည်။", + "totp_auth_url": "TOTP AUTH URL" +} diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index b8f054047..659d2b60c 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -698,5 +698,7 @@ "onramper_option_description": "Koop snel crypto met veel betaalmethoden. Beschikbaar in de meeste landen. Spreads en vergoedingen variëren.", "default_buy_provider": "Standaard Koopprovider", "ask_each_time": "Vraag het elke keer", - "buy_provider_unavailable": "Provider momenteel niet beschikbaar." -} \ No newline at end of file + "buy_provider_unavailable": "Provider momenteel niet beschikbaar.", + "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.", + "totp_auth_url": "TOTP AUTH-URL" +} diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 28ce59405..663a3784d 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -698,5 +698,7 @@ "onramper_option_description": "Szybko kup kryptowaluty z wieloma metodami płatności. Dostępne w większości krajów. Spready i opłaty różnią się.", "default_buy_provider": "Domyślny dostawca zakupu", "ask_each_time": "Zapytaj za każdym razem", - "buy_provider_unavailable": "Dostawca obecnie niedostępny." -} \ No newline at end of file + "buy_provider_unavailable": "Dostawca obecnie niedostępny.", + "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.", + "totp_auth_url": "Adres URL TOTP AUTH" +} diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index dadfa748c..a1e663d1d 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -697,5 +697,7 @@ "onramper_option_description": "Compre rapidamente criptografia com muitos métodos de pagamento. Disponível na maioria dos países. Os spreads e taxas variam.", "default_buy_provider": "Provedor de compra padrão", "ask_each_time": "Pergunte cada vez", - "buy_provider_unavailable": "Provedor atualmente indisponível." -} \ No newline at end of file + "buy_provider_unavailable": "Provedor atualmente indisponí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.", + "totp_auth_url": "URL de autenticação TOTP" +} diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 7647b5e71..03f55813b 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -698,5 +698,7 @@ "onramper_option_description": "Быстро купите крипто со многими способами оплаты. Доступно в большинстве стран. Спреды и сборы различаются.", "default_buy_provider": "По умолчанию поставщик покупки", "ask_each_time": "Спросите каждый раз", - "buy_provider_unavailable": "Поставщик в настоящее время недоступен." -} \ No newline at end of file + "buy_provider_unavailable": "Поставщик в настоящее время недоступен.", + "do_not_have_enough_gas_asset": "У вас недостаточно ${currency} для совершения транзакции при текущих условиях сети блокчейн. Вам нужно больше ${currency} для оплаты комиссий за сеть блокчейна, даже если вы отправляете другой актив.", + "totp_auth_url": "URL-адрес TOTP-АВТОРИЗАЦИИ" +} diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index f4a61aa26..6db49b142 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -696,5 +696,7 @@ "onramper_option_description": "ซื้อ crypto อย่างรวดเร็วด้วยวิธีการชำระเงินจำนวนมาก มีให้บริการในประเทศส่วนใหญ่ สเปรดและค่าธรรมเนียมแตกต่างกันไป", "default_buy_provider": "ผู้ให้บริการซื้อเริ่มต้น", "ask_each_time": "ถามทุกครั้ง", - "buy_provider_unavailable": "ผู้ให้บริการไม่สามารถใช้งานได้ในปัจจุบัน" -} \ No newline at end of file + "buy_provider_unavailable": "ผู้ให้บริการไม่สามารถใช้งานได้ในปัจจุบัน", + "do_not_have_enough_gas_asset": "คุณมี ${currency} ไม่เพียงพอที่จะทำธุรกรรมกับเงื่อนไขเครือข่ายบล็อคเชนในปัจจุบัน คุณต้องมี ${currency} เพิ่มขึ้นเพื่อชำระค่าธรรมเนียมเครือข่ายบล็อคเชน แม้ว่าคุณจะส่งสินทรัพย์อื่นก็ตาม", + "totp_auth_url": "URL การตรวจสอบสิทธิ์ TOTP" +} diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index d65e36609..18c52aa83 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -687,5 +687,7 @@ "support_title_other_links": "Iba pang mga link sa suporta", "support_description_other_links": "Sumali sa aming mga komunidad o maabot sa amin ang aming mga kasosyo sa pamamagitan ng iba pang mga pamamaraan", "select_destination": "Mangyaring piliin ang patutunguhan para sa backup file.", - "save_to_downloads": "I -save sa mga pag -download" + "save_to_downloads": "I -save sa mga pag -download", + "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 bayarin sa network ng blockchain, kahit na nagpapadala ka ng ibang asset.", + "totp_auth_url": "TOTP AUTH URL" } \ No newline at end of file diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index e0d54e437..4b607f4e2 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -696,5 +696,7 @@ "onramper_option_description": "Birçok ödeme yöntemi ile hızlı bir şekilde kripto satın alın. Çoğu ülkede mevcuttur. Forma ve ücretler değişir.", "default_buy_provider": "Varsayılan Satın Alma Sağlayıcısı", "ask_each_time": "Her seferinde sor", - "buy_provider_unavailable": "Sağlayıcı şu anda kullanılamıyor." -} \ No newline at end of file + "buy_provider_unavailable": "Sağlayıcı şu anda kullanılamıyor.", + "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.", + "totp_auth_url": "TOTP YETKİ URL'si" +} diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index c8d018ac0..ee8ac0a2c 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -698,5 +698,7 @@ "onramper_option_description": "Швидко купуйте криптовалюту з багатьма методами оплати. Доступний у більшості країн. Поширення та збори різняться.", "default_buy_provider": "Постачальник покупки за замовчуванням", "ask_each_time": "Запитайте кожен раз", - "buy_provider_unavailable": "В даний час постачальник недоступний." -} \ No newline at end of file + "buy_provider_unavailable": "В даний час постачальник недоступний.", + "do_not_have_enough_gas_asset": "У вас недостатньо ${currency}, щоб здійснити трансакцію з поточними умовами мережі блокчейн. Вам потрібно більше ${currency}, щоб сплатити комісію мережі блокчейн, навіть якщо ви надсилаєте інший актив.", + "totp_auth_url": "TOTP AUTH URL" +} diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index cb6071d30..bf4e6c7b4 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -690,5 +690,7 @@ "onramper_option_description": "ادائیگی کے بہت سے طریقوں سے جلدی سے کرپٹو خریدیں۔ زیادہ تر ممالک میں دستیاب ہے۔ پھیلاؤ اور فیس مختلف ہوتی ہے۔", "default_buy_provider": "پہلے سے طے شدہ خریدنے والا", "ask_each_time": "ہر بار پوچھیں", - "buy_provider_unavailable": "فراہم کنندہ فی الحال دستیاب نہیں ہے۔" -} \ No newline at end of file + "buy_provider_unavailable": "فراہم کنندہ فی الحال دستیاب نہیں ہے۔", + "do_not_have_enough_gas_asset": "آپ کے پاس موجودہ بلاکچین نیٹ ورک کی شرائط کے ساتھ لین دین کرنے کے لیے کافی ${currency} نہیں ہے۔ آپ کو بلاکچین نیٹ ورک کی فیس ادا کرنے کے لیے مزید ${currency} کی ضرورت ہے، چاہے آپ کوئی مختلف اثاثہ بھیج رہے ہوں۔", + "totp_auth_url": "TOTP AUTH URL" +} diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index fb2ac974d..c1e3ddec1 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -692,5 +692,7 @@ "onramper_option_description": "Ni kiakia Ra Crypto pẹlu ọpọlọpọ awọn ọna isanwo. Wa ni ọpọlọpọ awọn orilẹ-ede. Itankale ati awọn idiyele yatọ.", "default_buy_provider": "Aiyipada Ra Olupese", "ask_each_time": "Beere lọwọ kọọkan", - "buy_provider_unavailable": "Olupese lọwọlọwọ ko si." -} \ No newline at end of file + "buy_provider_unavailable": "Olupese lọwọlọwọ ko si.", + "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ṣẹ.", + "totp_auth_url": "TOTP AUTH URL" +} diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 3cd6ed3b0..0dc833162 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -697,5 +697,7 @@ "onramper_option_description": "快速使用许多付款方式购买加密货币。在大多数国家 /地区可用。利差和费用各不相同。", "default_buy_provider": "默认购买提供商", "ask_each_time": "每次问", - "buy_provider_unavailable": "提供者目前不可用。" -} \ No newline at end of file + "buy_provider_unavailable": "提供者目前不可用。", + "do_not_have_enough_gas_asset": "您没有足够的 ${currency} 来在当前的区块链网络条件下进行交易。即使您发送的是不同的资产,您也需要更多的 ${currency} 来支付区块链网络费用。", + "totp_auth_url": "TOTP 授权 URL" +} diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index dd8c83948..92c8a0559 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.6.1" -MONERO_COM_BUILD_NUMBER=57 +MONERO_COM_VERSION="1.6.2" +MONERO_COM_BUILD_NUMBER=58 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.9.1" -CAKEWALLET_BUILD_NUMBER=170 +CAKEWALLET_VERSION="4.9.2" +CAKEWALLET_BUILD_NUMBER=171 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/append_translation.sh b/scripts/append_translation.sh index 2dc373e0e..0cc33fc0f 100755 --- a/scripts/append_translation.sh +++ b/scripts/append_translation.sh @@ -7,7 +7,7 @@ # if you get an error `command not found` # give the correct permissions to this file using `chmod 777 append_translation.sh` -langs=("ar" "bg" "cs" "de" "en" "es" "fr" "ha" "hi" "hr" "id" "it" "ja" "ko" "my" "nl" "pl" "pt" "ru" "th" "tr" "uk" "ur" "yo" "zh") +langs=("ar" "bg" "cs" "de" "en" "es" "fr" "ha" "hi" "hr" "id" "it" "ja" "ko" "my" "nl" "pl" "pt" "ru" "th" "tl" "tr" "uk" "ur" "yo" "zh") name=$1 text=$2 diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 714441167..078688918 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.6.1" -MONERO_COM_BUILD_NUMBER=55 +MONERO_COM_VERSION="1.6.2" +MONERO_COM_BUILD_NUMBER=56 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.9.1" -CAKEWALLET_BUILD_NUMBER=183 +CAKEWALLET_VERSION="4.9.2" +CAKEWALLET_BUILD_NUMBER=185 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index bb2aa2ebc..5103d42b2 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -15,8 +15,8 @@ if [ -n "$1" ]; then fi CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.2.1" -CAKEWALLET_BUILD_NUMBER=32 +CAKEWALLET_VERSION="1.2.2" +CAKEWALLET_BUILD_NUMBER=33 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 8c86e1b7f..5e959b99b 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -32,6 +32,7 @@ class SecretKey { SecretKey('fiatApiKey', () => ''), SecretKey('payfuraApiKey', () => ''), SecretKey('chatwootWebsiteToken', () => ''), + SecretKey('exolixApiKey', () => ''), SecretKey('robinhoodApplicationId', () => ''), SecretKey('robinhoodCIdApiSecret', () => ''), ];