Merge branch 'main' into ui-enhancements

This commit is contained in:
tuxsudo 2024-12-27 15:29:08 -05:00 committed by GitHub
commit f52b8faa72
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 307 additions and 232 deletions

View file

@ -171,8 +171,8 @@ jobs:
echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart 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 ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_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 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 etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
@ -185,6 +185,7 @@ jobs:
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const authorization = '${{ secrets.CAKE_PAY_AUTHORIZATION }}';" >> lib/.secrets.g.dart echo "const authorization = '${{ secrets.CAKE_PAY_AUTHORIZATION }}';" >> lib/.secrets.g.dart

View file

@ -182,8 +182,8 @@ jobs:
echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart 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 ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_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 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 etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
@ -197,6 +197,7 @@ jobs:
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const authorization = '${{ secrets.CAKE_PAY_AUTHORIZATION }}';" >> lib/.secrets.g.dart echo "const authorization = '${{ secrets.CAKE_PAY_AUTHORIZATION }}';" >> lib/.secrets.g.dart

View file

@ -154,8 +154,8 @@ jobs:
echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart 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 ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/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 moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
@ -167,6 +167,7 @@ jobs:
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const authorization = '${{ secrets.CAKE_PAY_AUTHORIZATION }}';" >> lib/.secrets.g.dart echo "const authorization = '${{ secrets.CAKE_PAY_AUTHORIZATION }}';" >> lib/.secrets.g.dart

View file

@ -7,4 +7,7 @@
- -
uri: solana-rpc.publicnode.com:443 uri: solana-rpc.publicnode.com:443
useSSL: true useSSL: true
-
uri: solana-mainnet.core.chainstack.com
useSSL: true
is_default: true is_default: true

View file

@ -235,7 +235,7 @@ class ElectrumClient {
return []; return [];
}); });
Future<List<Map<String, dynamic>>> getListUnspent(String scriptHash) async { Future<List<Map<String, dynamic>>?> getListUnspent(String scriptHash) async {
final result = await call(method: 'blockchain.scripthash.listunspent', params: [scriptHash]); final result = await call(method: 'blockchain.scripthash.listunspent', params: [scriptHash]);
if (result is List) { if (result is List) {
@ -248,7 +248,7 @@ class ElectrumClient {
}).toList(); }).toList();
} }
return []; return null;
} }
Future<List<Map<String, dynamic>>> getMempool(String scriptHash) => Future<List<Map<String, dynamic>>> getMempool(String scriptHash) =>

View file

@ -39,7 +39,7 @@ class ElectrumBalance extends Balance {
int secondUnconfirmed = 0; int secondUnconfirmed = 0;
@override @override
String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed - frozen); String get formattedAvailableBalance => bitcoinAmountToString(amount: ((confirmed + unconfirmed) - frozen) );
@override @override
String get formattedAdditionalBalance => bitcoinAmountToString(amount: unconfirmed); String get formattedAdditionalBalance => bitcoinAmountToString(amount: unconfirmed);
@ -58,7 +58,7 @@ class ElectrumBalance extends Balance {
@override @override
String get formattedFullAvailableBalance => String get formattedFullAvailableBalance =>
bitcoinAmountToString(amount: confirmed + secondConfirmed - frozen); bitcoinAmountToString(amount: (confirmed + unconfirmed) + secondConfirmed - frozen);
String toJSON() => json.encode({ String toJSON() => json.encode({
'confirmed': confirmed, 'confirmed': confirmed,

View file

@ -478,6 +478,7 @@ abstract class ElectrumWalletBase
if (alwaysScan == true) { if (alwaysScan == true) {
_setListeners(walletInfo.restoreHeight); _setListeners(walletInfo.restoreHeight);
} else { } else {
if (syncStatus is LostConnectionSyncStatus) return;
syncStatus = SyncedSyncStatus(); syncStatus = SyncedSyncStatus();
} }
} catch (e, stacktrace) { } catch (e, stacktrace) {
@ -1361,6 +1362,10 @@ abstract class ElectrumWalletBase
Future<void> updateAllUnspents() async { Future<void> updateAllUnspents() async {
List<BitcoinUnspent> updatedUnspentCoins = []; List<BitcoinUnspent> updatedUnspentCoins = [];
final previousUnspentCoins = List<BitcoinUnspent>.from(unspentCoins.where((utxo) =>
utxo.bitcoinAddressRecord.type != SegwitAddresType.mweb &&
utxo.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord));
if (hasSilentPaymentsScanning) { if (hasSilentPaymentsScanning) {
// Update unspents stored from scanned silent payment transactions // Update unspents stored from scanned silent payment transactions
transactionHistory.transactions.values.forEach((tx) { transactionHistory.transactions.values.forEach((tx) {
@ -1377,13 +1382,27 @@ abstract class ElectrumWalletBase
if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0; if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0;
}); });
await Future.wait(walletAddresses.allAddresses final addressFutures = walletAddresses.allAddresses
.where((element) => element.type != SegwitAddresType.mweb) .where((element) => element.type != SegwitAddresType.mweb)
.map((address) async { .map((address) => fetchUnspent(address))
updatedUnspentCoins.addAll(await fetchUnspent(address)); .toList();
}));
unspentCoins = updatedUnspentCoins; final results = await Future.wait(addressFutures);
final failedCount = results.where((result) => result == null).length;
if (failedCount == 0) {
for (final result in results) {
updatedUnspentCoins.addAll(result!);
}
unspentCoins = updatedUnspentCoins;
} else {
unspentCoins = handleFailedUtxoFetch(
failedCount: failedCount,
previousUnspentCoins: previousUnspentCoins,
updatedUnspentCoins: updatedUnspentCoins,
results: results,
);
}
final currentWalletUnspentCoins = final currentWalletUnspentCoins =
unspentCoinsInfo.values.where((element) => element.walletId == id); unspentCoinsInfo.values.where((element) => element.walletId == id);
@ -1396,6 +1415,38 @@ abstract class ElectrumWalletBase
await _refreshUnspentCoinsInfo(); await _refreshUnspentCoinsInfo();
} }
List<BitcoinUnspent> handleFailedUtxoFetch({
required int failedCount,
required List<BitcoinUnspent> previousUnspentCoins,
required List<BitcoinUnspent> updatedUnspentCoins,
required List<List<BitcoinUnspent>?> results,
}) {
if (failedCount == results.length) {
printV("All UTXOs failed to fetch, falling back to previous UTXOs");
return previousUnspentCoins;
}
final successfulUtxos = <BitcoinUnspent>[];
for (final result in results) {
if (result != null) {
successfulUtxos.addAll(result);
}
}
if (failedCount > 0 && successfulUtxos.isEmpty) {
printV("Some UTXOs failed, but no successful UTXOs, falling back to previous UTXOs");
return previousUnspentCoins;
}
if (failedCount > 0) {
printV("Some UTXOs failed, updating with successful UTXOs");
updatedUnspentCoins.addAll(successfulUtxos);
}
return updatedUnspentCoins;
}
Future<void> updateCoins(List<BitcoinUnspent> newUnspentCoins) async { Future<void> updateCoins(List<BitcoinUnspent> newUnspentCoins) async {
if (newUnspentCoins.isEmpty) { if (newUnspentCoins.isEmpty) {
return; return;
@ -1427,15 +1478,17 @@ abstract class ElectrumWalletBase
@action @action
Future<void> updateUnspentsForAddress(BitcoinAddressRecord address) async { Future<void> updateUnspentsForAddress(BitcoinAddressRecord address) async {
final newUnspentCoins = await fetchUnspent(address); final newUnspentCoins = await fetchUnspent(address);
await updateCoins(newUnspentCoins); await updateCoins(newUnspentCoins ?? []);
} }
@action @action
Future<List<BitcoinUnspent>> fetchUnspent(BitcoinAddressRecord address) async { Future<List<BitcoinUnspent>?> fetchUnspent(BitcoinAddressRecord address) async {
List<Map<String, dynamic>> unspents = [];
List<BitcoinUnspent> updatedUnspentCoins = []; List<BitcoinUnspent> updatedUnspentCoins = [];
unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); final unspents = await electrumClient.getListUnspent(address.getScriptHash(network));
// Failed to fetch unspents
if (unspents == null) return null;
await Future.wait(unspents.map((unspent) async { await Future.wait(unspents.map((unspent) async {
try { try {
@ -2160,18 +2213,6 @@ abstract class ElectrumWalletBase
var totalConfirmed = 0; var totalConfirmed = 0;
var totalUnconfirmed = 0; var totalUnconfirmed = 0;
unspentCoinsInfo.values.forEach((info) {
unspentCoins.forEach((element) {
if (element.hash == info.hash &&
element.vout == info.vout &&
info.isFrozen &&
element.bitcoinAddressRecord.address == info.address &&
element.value == info.value) {
totalFrozen += element.value;
}
});
});
if (hasSilentPaymentsScanning) { if (hasSilentPaymentsScanning) {
// Add values from unspent coins that are not fetched by the address list // Add values from unspent coins that are not fetched by the address list
// i.e. scanned silent payments // i.e. scanned silent payments
@ -2187,6 +2228,20 @@ abstract class ElectrumWalletBase
}); });
} }
unspentCoinsInfo.values.forEach((info) {
unspentCoins.forEach((element) {
if (element.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) return;
if (element.hash == info.hash &&
element.vout == info.vout &&
info.isFrozen &&
element.bitcoinAddressRecord.address == info.address &&
element.value == info.value) {
totalFrozen += element.value;
}
});
});
final balances = await Future.wait(balanceFutures); final balances = await Future.wait(balanceFutures);
if (balances.isNotEmpty && balances.first['confirmed'] == null) { if (balances.isNotEmpty && balances.first['confirmed'] == null) {

View file

@ -47,7 +47,11 @@ class TransactionInputNotSupported implements Exception {}
class SignNativeTokenTransactionRentException implements Exception {} class SignNativeTokenTransactionRentException implements Exception {}
class CreateAssociatedTokenAccountException implements Exception {} class CreateAssociatedTokenAccountException implements Exception {
final String errorMessage;
CreateAssociatedTokenAccountException(this.errorMessage);
}
class SignSPLTokenTransactionRentException implements Exception {} class SignSPLTokenTransactionRentException implements Exception {}

View file

@ -3,36 +3,25 @@ import 'package:cw_core/monero_amount_format.dart';
class MoneroBalance extends Balance { class MoneroBalance extends Balance {
MoneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0}) MoneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0})
: formattedFullBalance = moneroAmountToString(amount: frozenBalance + fullBalance), : formattedUnconfirmedBalance = moneroAmountToString(amount: fullBalance - unlockedBalance),
formattedUnlockedBalance = moneroAmountToString(amount: unlockedBalance), formattedUnlockedBalance = moneroAmountToString(amount: unlockedBalance - frozenBalance),
formattedLockedBalance = formattedFrozenBalance = moneroAmountToString(amount: frozenBalance),
moneroAmountToString(amount: frozenBalance + fullBalance - unlockedBalance),
super(unlockedBalance, fullBalance); super(unlockedBalance, fullBalance);
MoneroBalance.fromString(
{required this.formattedFullBalance,
required this.formattedUnlockedBalance,
this.formattedLockedBalance = '0.0'})
: fullBalance = moneroParseAmount(amount: formattedFullBalance),
unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance),
frozenBalance = moneroParseAmount(amount: formattedLockedBalance),
super(moneroParseAmount(amount: formattedUnlockedBalance),
moneroParseAmount(amount: formattedFullBalance));
final int fullBalance; final int fullBalance;
final int unlockedBalance; final int unlockedBalance;
final int frozenBalance; final int frozenBalance;
final String formattedFullBalance; final String formattedUnconfirmedBalance;
final String formattedUnlockedBalance; final String formattedUnlockedBalance;
final String formattedLockedBalance; final String formattedFrozenBalance;
@override @override
String get formattedUnAvailableBalance => String get formattedUnAvailableBalance =>
formattedLockedBalance == '0.0' ? '' : formattedLockedBalance; formattedFrozenBalance == '0.0' ? '' : formattedFrozenBalance;
@override @override
String get formattedAvailableBalance => formattedUnlockedBalance; String get formattedAvailableBalance => formattedUnlockedBalance;
@override @override
String get formattedAdditionalBalance => formattedFullBalance; String get formattedAdditionalBalance => formattedUnconfirmedBalance;
} }

View file

@ -22,8 +22,8 @@ class Node extends HiveObject with Keyable {
this.useSSL, this.useSSL,
this.trusted = false, this.trusted = false,
this.socksProxyAddress, this.socksProxyAddress,
this.path = '',
String? uri, String? uri,
String? path,
WalletType? type, WalletType? type,
}) { }) {
if (uri != null) { if (uri != null) {
@ -32,9 +32,6 @@ class Node extends HiveObject with Keyable {
if (type != null) { if (type != null) {
this.type = type; this.type = type;
} }
if (path != null) {
this.path = path;
}
} }
Node.fromMap(Map<String, Object?> map) Node.fromMap(Map<String, Object?> map)
@ -95,19 +92,15 @@ class Node extends HiveObject with Keyable {
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.litecoin: case WalletType.litecoin:
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
return createUriFromElectrumAddress(uriRaw, path ?? ''); return createUriFromElectrumAddress(uriRaw, path!);
case WalletType.nano: case WalletType.nano:
case WalletType.banano: case WalletType.banano:
if (isSSL) {
return Uri.https(uriRaw, path ?? '');
} else {
return Uri.http(uriRaw, path ?? '');
}
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.polygon: case WalletType.polygon:
case WalletType.solana: case WalletType.solana:
case WalletType.tron: case WalletType.tron:
return Uri.https(uriRaw, path ?? ''); return Uri.parse(
"http${isSSL ? "s" : ""}://$uriRaw${path!.startsWith("/") ? path : "/$path"}");
case WalletType.none: case WalletType.none:
throw Exception('Unexpected type ${type.toString()} for Node uri'); throw Exception('Unexpected type ${type.toString()} for Node uri');
} }
@ -247,7 +240,7 @@ class Node extends HiveObject with Keyable {
if (proxy == null) { if (proxy == null) {
return false; return false;
} }
final proxyAddress = proxy!.split(':')[0]; final proxyAddress = proxy.split(':')[0];
final proxyPort = int.parse(proxy.split(':')[1]); final proxyPort = int.parse(proxy.split(':')[1]);
try { try {
final socket = await Socket.connect(proxyAddress, proxyPort, timeout: Duration(seconds: 5)); final socket = await Socket.connect(proxyAddress, proxyPort, timeout: Duration(seconds: 5));

View file

@ -3,36 +3,26 @@ import 'package:cw_core/wownero_amount_format.dart';
class WowneroBalance extends Balance { class WowneroBalance extends Balance {
WowneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0}) WowneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0})
: formattedFullBalance = wowneroAmountToString(amount: fullBalance), : formattedUnconfirmedBalance = wowneroAmountToString(amount: fullBalance - unlockedBalance),
formattedUnlockedBalance = wowneroAmountToString(amount: unlockedBalance - frozenBalance), formattedUnlockedBalance = wowneroAmountToString(amount: unlockedBalance - frozenBalance),
formattedLockedBalance = formattedFrozenBalance =
wowneroAmountToString(amount: frozenBalance + fullBalance - unlockedBalance), wowneroAmountToString(amount: frozenBalance),
super(unlockedBalance, fullBalance); super(unlockedBalance, fullBalance);
WowneroBalance.fromString(
{required this.formattedFullBalance,
required this.formattedUnlockedBalance,
this.formattedLockedBalance = '0.0'})
: fullBalance = wowneroParseAmount(amount: formattedFullBalance),
unlockedBalance = wowneroParseAmount(amount: formattedUnlockedBalance),
frozenBalance = wowneroParseAmount(amount: formattedLockedBalance),
super(wowneroParseAmount(amount: formattedUnlockedBalance),
wowneroParseAmount(amount: formattedFullBalance));
final int fullBalance; final int fullBalance;
final int unlockedBalance; final int unlockedBalance;
final int frozenBalance; final int frozenBalance;
final String formattedFullBalance; final String formattedUnconfirmedBalance;
final String formattedUnlockedBalance; final String formattedUnlockedBalance;
final String formattedLockedBalance; final String formattedFrozenBalance;
@override @override
String get formattedUnAvailableBalance => String get formattedUnAvailableBalance =>
formattedLockedBalance == '0.0' ? '' : formattedLockedBalance; formattedFrozenBalance == '0.0' ? '' : formattedFrozenBalance;
@override @override
String get formattedAvailableBalance => formattedUnlockedBalance; String get formattedAvailableBalance => formattedUnlockedBalance;
@override @override
String get formattedAdditionalBalance => formattedFullBalance; String get formattedAdditionalBalance => formattedUnconfirmedBalance;
} }

View file

@ -11,13 +11,12 @@ class EVMChainERC20Balance extends Balance {
final int exponent; final int exponent;
@override @override
String get formattedAdditionalBalance { String get formattedAdditionalBalance => _balance();
final String formattedBalance = (balance / BigInt.from(10).pow(exponent)).toString();
return formattedBalance.substring(0, min(12, formattedBalance.length));
}
@override @override
String get formattedAvailableBalance { String get formattedAvailableBalance => _balance();
String _balance() {
final String formattedBalance = (balance / BigInt.from(10).pow(exponent)).toString(); final String formattedBalance = (balance / BigInt.from(10).pow(exponent)).toString();
return formattedBalance.substring(0, min(12, formattedBalance.length)); return formattedBalance.substring(0, min(12, formattedBalance.length));
} }

View file

@ -751,11 +751,18 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
int _getFrozenBalance() { int _getFrozenBalance() {
var frozenBalance = 0; var frozenBalance = 0;
for (var coin in unspentCoinsInfo.values.where((element) => unspentCoinsInfo.values.forEach((info) {
element.walletId == id && unspentCoins.forEach((element) {
element.accountIndex == walletAddresses.account!.id)) { if (element.hash == info.hash &&
if (coin.isFrozen && !coin.isSending) frozenBalance += coin.value; element.vout == info.vout &&
} info.isFrozen &&
element.value == info.value && info.walletId == id &&
info.accountIndex == walletAddresses.account!.id) {
if (element.isFrozen && !element.isSending) frozenBalance+= element.value;
}
});
});
return frozenBalance; return frozenBalance;
} }

View file

@ -21,22 +21,23 @@ class SolanaWalletClient {
bool connect(Node node) { bool connect(Node node) {
try { try {
Uri? rpcUri; Uri rpcUri = node.uri;
String webSocketUrl; String webSocketUrl = 'wss://${node.uriRaw}';
bool isModifiedNodeUri = false;
if (node.uriRaw == 'rpc.ankr.com') { if (node.uriRaw == 'rpc.ankr.com') {
isModifiedNodeUri = true;
String ankrApiKey = secrets.ankrApiKey; String ankrApiKey = secrets.ankrApiKey;
rpcUri = Uri.https(node.uriRaw, '/solana/$ankrApiKey'); rpcUri = Uri.https(node.uriRaw, '/solana/$ankrApiKey');
webSocketUrl = 'wss://${node.uriRaw}/solana/ws/$ankrApiKey'; webSocketUrl = 'wss://${node.uriRaw}/solana/ws/$ankrApiKey';
} else { } else if (node.uriRaw == 'solana-mainnet.core.chainstack.com') {
webSocketUrl = 'wss://${node.uriRaw}'; String chainStackApiKey = secrets.chainStackApiKey;
rpcUri = Uri.https(node.uriRaw, '/$chainStackApiKey');
webSocketUrl = 'wss://${node.uriRaw}/$chainStackApiKey';
} }
_client = SolanaClient( _client = SolanaClient(
rpcUrl: isModifiedNodeUri ? rpcUri! : node.uri, rpcUrl: rpcUri,
websocketUrl: Uri.parse(webSocketUrl), websocketUrl: Uri.parse(webSocketUrl),
timeout: const Duration(minutes: 2), timeout: const Duration(minutes: 2),
); );
@ -115,10 +116,14 @@ class SolanaWalletClient {
final message = final message =
_getMessageForNativeTransaction(ownerKeypair, ownerKeypair.address, lamportsPerSol); _getMessageForNativeTransaction(ownerKeypair, ownerKeypair.address, lamportsPerSol);
final recentBlockhash = await _getRecentBlockhash(commitment); final latestBlockhash = await _getLatestBlockhash(commitment);
final estimatedFee = final estimatedFee = _getFeeFromCompiledMessage(
_getFeeFromCompiledMessage(message, ownerKeypair.publicKey, recentBlockhash, commitment); message,
ownerKeypair.publicKey,
latestBlockhash,
commitment,
);
return estimatedFee; return estimatedFee;
} }
@ -131,13 +136,25 @@ class SolanaWalletClient {
List<SolanaTransactionModel> transactions = []; List<SolanaTransactionModel> transactions = [];
try { try {
final response = await _client!.rpcClient.getTransactionsList( final signatures = await _client!.rpcClient.getSignaturesForAddress(
publicKey, publicKey.toBase58(),
commitment: Commitment.confirmed, commitment: Commitment.confirmed,
limit: 1000,
); );
for (final tx in response) { final List<TransactionDetails> transactionDetails = [];
for (int i = 0; i < signatures.length; i += 20) {
final response = await _client!.rpcClient.getMultipleTransactions(
signatures.sublist(i, math.min(i + 20, signatures.length)),
commitment: Commitment.confirmed,
encoding: Encoding.jsonParsed,
);
transactionDetails.addAll(response);
// to avoid reaching the node RPS limit
await Future.delayed(Duration(milliseconds: 500));
}
for (final tx in transactionDetails) {
if (tx.transaction is ParsedTransaction) { if (tx.transaction is ParsedTransaction) {
final parsedTx = (tx.transaction as ParsedTransaction); final parsedTx = (tx.transaction as ParsedTransaction);
final message = parsedTx.message; final message = parsedTx.message;
@ -310,16 +327,16 @@ class SolanaWalletClient {
} }
} }
Future<RecentBlockhash> _getRecentBlockhash(Commitment commitment) async { Future<LatestBlockhash> _getLatestBlockhash(Commitment commitment) async {
final latestBlockhash = final latestBlockHashResult =
await _client!.rpcClient.getLatestBlockhash(commitment: commitment).value; await _client!.rpcClient.getLatestBlockhash(commitment: commitment).value;
final recentBlockhash = RecentBlockhash( final latestBlockhash = LatestBlockhash(
blockhash: latestBlockhash.blockhash, blockhash: latestBlockHashResult.blockhash,
feeCalculator: const FeeCalculator(lamportsPerSignature: 500), lastValidBlockHeight: latestBlockHashResult.lastValidBlockHeight,
); );
return recentBlockhash; return latestBlockhash;
} }
Message _getMessageForNativeTransaction( Message _getMessageForNativeTransaction(
@ -342,11 +359,11 @@ class SolanaWalletClient {
Future<double> _getFeeFromCompiledMessage( Future<double> _getFeeFromCompiledMessage(
Message message, Message message,
Ed25519HDPublicKey feePayer, Ed25519HDPublicKey feePayer,
RecentBlockhash recentBlockhash, LatestBlockhash latestBlockhash,
Commitment commitment, Commitment commitment,
) async { ) async {
final compile = message.compile( final compile = message.compile(
recentBlockhash: recentBlockhash.blockhash, recentBlockhash: latestBlockhash.blockhash,
feePayer: feePayer, feePayer: feePayer,
); );
@ -391,12 +408,12 @@ class SolanaWalletClient {
final signers = [ownerKeypair]; final signers = [ownerKeypair];
RecentBlockhash recentBlockhash = await _getRecentBlockhash(commitment); LatestBlockhash latestBlockhash = await _getLatestBlockhash(commitment);
final fee = await _getFeeFromCompiledMessage( final fee = await _getFeeFromCompiledMessage(
message, message,
signers.first.publicKey, signers.first.publicKey,
recentBlockhash, latestBlockhash,
commitment, commitment,
); );
@ -422,14 +439,14 @@ class SolanaWalletClient {
message: updatedMessage, message: updatedMessage,
signers: signers, signers: signers,
commitment: commitment, commitment: commitment,
recentBlockhash: recentBlockhash, latestBlockhash: latestBlockhash,
); );
} else { } else {
signedTx = await _signTransactionInternal( signedTx = await _signTransactionInternal(
message: message, message: message,
signers: signers, signers: signers,
commitment: commitment, commitment: commitment,
recentBlockhash: recentBlockhash, latestBlockhash: latestBlockhash,
); );
} }
@ -507,12 +524,12 @@ class SolanaWalletClient {
final signers = [ownerKeypair]; final signers = [ownerKeypair];
RecentBlockhash recentBlockhash = await _getRecentBlockhash(commitment); LatestBlockhash latestBlockhash = await _getLatestBlockhash(commitment);
final fee = await _getFeeFromCompiledMessage( final fee = await _getFeeFromCompiledMessage(
message, message,
signers.first.publicKey, signers.first.publicKey,
recentBlockhash, latestBlockhash,
commitment, commitment,
); );
@ -530,7 +547,7 @@ class SolanaWalletClient {
message: message, message: message,
signers: signers, signers: signers,
commitment: commitment, commitment: commitment,
recentBlockhash: recentBlockhash, latestBlockhash: latestBlockhash,
); );
sendTx() async => await sendTransaction( sendTx() async => await sendTransaction(
@ -552,9 +569,9 @@ class SolanaWalletClient {
required Message message, required Message message,
required List<Ed25519HDKeyPair> signers, required List<Ed25519HDKeyPair> signers,
required Commitment commitment, required Commitment commitment,
required RecentBlockhash recentBlockhash, required LatestBlockhash latestBlockhash,
}) async { }) async {
final signedTx = await signTransaction(recentBlockhash, message, signers); final signedTx = await signTransaction(latestBlockhash, message, signers);
return signedTx; return signedTx;
} }

View file

@ -25,9 +25,7 @@ class SolanaSignNativeTokenTransactionRentException
extends SignNativeTokenTransactionRentException {} extends SignNativeTokenTransactionRentException {}
class SolanaCreateAssociatedTokenAccountException extends CreateAssociatedTokenAccountException { class SolanaCreateAssociatedTokenAccountException extends CreateAssociatedTokenAccountException {
SolanaCreateAssociatedTokenAccountException(this.exceptionMessage); SolanaCreateAssociatedTokenAccountException(super.errorMessage);
final String exceptionMessage;
} }
class SolanaSignSPLTokenTransactionRentException extends SignSPLTokenTransactionRentException {} class SolanaSignSPLTokenTransactionRentException extends SignSPLTokenTransactionRentException {}

View file

@ -11,7 +11,7 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
solana: ^0.30.4 solana: ^0.31.0+1
cw_core: cw_core:
path: ../cw_core path: ../cw_core
http: ^1.1.0 http: ^1.1.0

View file

@ -1,24 +0,0 @@
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/wallet_base.dart';
class PayfuraBuyProvider {
PayfuraBuyProvider({required SettingsStore settingsStore, required WalletBase wallet})
: this._settingsStore = settingsStore,
this._wallet = wallet;
final SettingsStore _settingsStore;
final WalletBase _wallet;
static const _baseUrl = 'exchange.payfura.com';
Uri requestUrl() {
return Uri.https(_baseUrl, '', <String, dynamic>{
'apiKey': secrets.payfuraApiKey,
'to': _wallet.currency.title,
'from': _settingsStore.fiatCurrency.title,
'walletAddress': '${_wallet.currency.title}:${_wallet.walletAddresses.address}',
'mode': 'buy'
});
}
}

View file

@ -204,8 +204,8 @@ class CakePayApi {
/// Get Vendors /// Get Vendors
Future<List<CakePayVendor>> getVendors({ Future<List<CakePayVendor>> getVendors({
required String apiKey, required String apiKey,
required String country,
int? page, int? page,
String? country,
String? countryCode, String? countryCode,
String? search, String? search,
List<String>? vendorIds, List<String>? vendorIds,
@ -247,7 +247,7 @@ class CakePayApi {
} }
return (bodyJson['results'] as List) return (bodyJson['results'] as List)
.map((e) => CakePayVendor.fromJson(e as Map<String, dynamic>)) .map((e) => CakePayVendor.fromJson(e as Map<String, dynamic>, country))
.toList(); .toList();
} }
} }

View file

@ -81,7 +81,11 @@ class CakePayCard {
} }
static String fixEncoding(String text) { static String fixEncoding(String text) {
final bytes = latin1.encode(text); try {
return utf8.decode(bytes, allowMalformed: true); final bytes = latin1.encode(text);
return utf8.decode(bytes, allowMalformed: true);
} catch (_) {
return text;
}
} }
} }

View file

@ -29,8 +29,8 @@ class CakePayService {
/// Get Vendors /// Get Vendors
Future<List<CakePayVendor>> getVendors({ Future<List<CakePayVendor>> getVendors({
required String country,
int? page, int? page,
String? country,
String? countryCode, String? countryCode,
String? search, String? search,
List<String>? vendorIds, List<String>? vendorIds,

View file

@ -7,7 +7,7 @@ class CakePayVendor {
final String name; final String name;
final bool unavailable; final bool unavailable;
final String? cakeWarnings; final String? cakeWarnings;
final List<String> countries; final String country;
final CakePayCard? card; final CakePayCard? card;
CakePayVendor({ CakePayVendor({
@ -15,19 +15,23 @@ class CakePayVendor {
required this.name, required this.name,
required this.unavailable, required this.unavailable,
this.cakeWarnings, this.cakeWarnings,
required this.countries, required this.country,
this.card, this.card,
}); });
factory CakePayVendor.fromJson(Map<String, dynamic> json) { factory CakePayVendor.fromJson(Map<String, dynamic> json, String country) {
final name = stripHtmlIfNeeded(json['name'] as String); final name = stripHtmlIfNeeded(json['name'] as String);
final decodedName = fixEncoding(name); final decodedName = fixEncoding(name);
var cardsJson = json['cards'] as List?; var cardsJson = json['cards'] as List?;
CakePayCard? firstCard; CakePayCard? cardForVendor;
if (cardsJson != null && cardsJson.isNotEmpty) { if (cardsJson != null && cardsJson.isNotEmpty) {
firstCard = CakePayCard.fromJson(cardsJson.first as Map<String, dynamic>); try {
cardForVendor = CakePayCard.fromJson(cardsJson
.where((element) => element['country'] == country)
.first as Map<String, dynamic>);
} catch (_) {}
} }
return CakePayVendor( return CakePayVendor(
@ -35,8 +39,8 @@ class CakePayVendor {
name: decodedName, name: decodedName,
unavailable: json['unavailable'] as bool? ?? false, unavailable: json['unavailable'] as bool? ?? false,
cakeWarnings: json['cake_warnings'] as String?, cakeWarnings: json['cake_warnings'] as String?,
countries: List<String>.from(json['countries'] as List? ?? []), country: country,
card: firstCard, card: cardForVendor,
); );
} }

View file

@ -140,25 +140,24 @@ abstract class Web3WalletServiceBase with Store {
for (final cId in SolanaChainId.values) { for (final cId in SolanaChainId.values) {
final node = appStore.settingsStore.getCurrentNode(appStore.wallet!.type); final node = appStore.settingsStore.getCurrentNode(appStore.wallet!.type);
Uri? rpcUri; Uri rpcUri = node.uri;
String webSocketUrl; String webSocketUrl = 'wss://${node.uriRaw}';
bool isModifiedNodeUri = false;
if (node.uriRaw == 'rpc.ankr.com') { if (node.uriRaw == 'rpc.ankr.com') {
isModifiedNodeUri = true;
//A better way to handle this instead of adding this to the general secrets?
String ankrApiKey = secrets.ankrApiKey; String ankrApiKey = secrets.ankrApiKey;
rpcUri = Uri.https(node.uriRaw, '/solana/$ankrApiKey'); rpcUri = Uri.https(node.uriRaw, '/solana/$ankrApiKey');
webSocketUrl = 'wss://${node.uriRaw}/solana/ws/$ankrApiKey'; webSocketUrl = 'wss://${node.uriRaw}/solana/ws/$ankrApiKey';
} else { } else if (node.uriRaw == 'solana-mainnet.core.chainstack.com') {
webSocketUrl = 'wss://${node.uriRaw}'; String chainStackApiKey = secrets.chainStackApiKey;
rpcUri = Uri.https(node.uriRaw, '/$chainStackApiKey');
webSocketUrl = 'wss://${node.uriRaw}/$chainStackApiKey';
} }
SolanaChainServiceImpl( SolanaChainServiceImpl(
reference: cId, reference: cId,
rpcUrl: isModifiedNodeUri ? rpcUri! : node.uri, rpcUrl: rpcUri,
webSocketUrl: webSocketUrl, webSocketUrl: webSocketUrl,
wcKeyService: walletKeyService, wcKeyService: walletKeyService,
bottomSheetService: _bottomSheetHandler, bottomSheetService: _bottomSheetHandler,

View file

@ -2,12 +2,10 @@ import 'dart:async';
import 'package:cake_wallet/core/generate_wallet_password.dart'; import 'package:cake_wallet/core/generate_wallet_password.dart';
import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/core/secure_storage.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/reactions/on_authentication_state_change.dart'; import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.dart';

View file

@ -11,7 +11,6 @@ import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart'; import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
import 'package:cake_wallet/core/new_wallet_arguments.dart'; import 'package:cake_wallet/core/new_wallet_arguments.dart';
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart'; import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/auth_service.dart';
@ -1022,11 +1021,6 @@ Future<void> setup({
getIt.registerFactoryParam<WebViewPage, String, Uri>((title, uri) => WebViewPage(title, uri)); getIt.registerFactoryParam<WebViewPage, String, Uri>((title, uri) => WebViewPage(title, uri));
getIt.registerFactory<PayfuraBuyProvider>(() => PayfuraBuyProvider(
settingsStore: getIt.get<AppStore>().settingsStore,
wallet: getIt.get<AppStore>().wallet!,
));
getIt.registerFactory(() => ExchangeViewModel( getIt.registerFactory(() => ExchangeViewModel(
getIt.get<AppStore>(), getIt.get<AppStore>(),
_tradesSource, _tradesSource,

View file

@ -40,7 +40,7 @@ const polygonDefaultNodeUri = 'polygon-bor.publicnode.com';
const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002'; const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002';
const nanoDefaultNodeUri = 'nano.nownodes.io'; const nanoDefaultNodeUri = 'nano.nownodes.io';
const nanoDefaultPowNodeUri = 'rpc.nano.to'; const nanoDefaultPowNodeUri = 'rpc.nano.to';
const solanaDefaultNodeUri = 'solana-rpc.publicnode.com:443'; const solanaDefaultNodeUri = 'solana-mainnet.core.chainstack.com';
const tronDefaultNodeUri = 'api.trongrid.io'; const tronDefaultNodeUri = 'api.trongrid.io';
const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002'; const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002';
const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568'; const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568';
@ -167,7 +167,11 @@ Future<void> defaultSettingsMigration(
break; break;
case 18: case 18:
await addOnionNode(nodes); await updateWalletTypeNodesWithNewNode(
nodes: nodes,
newNodeUri: "cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081",
type: WalletType.monero,
);
break; break;
case 19: case 19:
@ -261,15 +265,15 @@ Future<void> defaultSettingsMigration(
await updateTronNodesWithNowNodes(sharedPreferences: sharedPreferences, nodes: nodes); await updateTronNodesWithNowNodes(sharedPreferences: sharedPreferences, nodes: nodes);
break; break;
case 42: case 42:
updateBtcElectrumNodeToUseSSL(nodes, sharedPreferences); _fixNodesUseSSLFlag(nodes);
break; break;
case 43: case 43:
await _updateCakeXmrNode(nodes); _fixNodesUseSSLFlag(nodes);
_deselectExchangeProvider(sharedPreferences, "THORChain"); _deselectExchangeProvider(sharedPreferences, "THORChain");
_deselectExchangeProvider(sharedPreferences, "SimpleSwap"); _deselectExchangeProvider(sharedPreferences, "SimpleSwap");
break; break;
case 44: case 44:
await _updateCakeXmrNode(nodes); _fixNodesUseSSLFlag(nodes);
await _changeDefaultNode( await _changeDefaultNode(
nodes: nodes, nodes: nodes,
sharedPreferences: sharedPreferences, sharedPreferences: sharedPreferences,
@ -297,14 +301,12 @@ Future<void> defaultSettingsMigration(
updateWalletTypeNodesWithNewNode( updateWalletTypeNodesWithNewNode(
newNodeUri: 'matic.nownodes.io', newNodeUri: 'matic.nownodes.io',
sharedPreferences: sharedPreferences,
nodes: nodes, nodes: nodes,
type: WalletType.polygon, type: WalletType.polygon,
useSSL: true, useSSL: true,
); );
updateWalletTypeNodesWithNewNode( updateWalletTypeNodesWithNewNode(
newNodeUri: 'eth.nownodes.io', newNodeUri: 'eth.nownodes.io',
sharedPreferences: sharedPreferences,
nodes: nodes, nodes: nodes,
type: WalletType.ethereum, type: WalletType.ethereum,
useSSL: true, useSSL: true,
@ -330,6 +332,35 @@ Future<void> defaultSettingsMigration(
useSSL: true, useSSL: true,
oldUri: ['rpc.ankr.com'], oldUri: ['rpc.ankr.com'],
); );
break;
case 46:
_fixNodesUseSSLFlag(nodes);
updateWalletTypeNodesWithNewNode(
newNodeUri: 'litecoin.stackwallet.com:20063',
nodes: nodes,
type: WalletType.litecoin,
useSSL: true,
);
updateWalletTypeNodesWithNewNode(
newNodeUri: 'electrum-ltc.bysh.me:50002',
nodes: nodes,
type: WalletType.litecoin,
useSSL: true,
);
_changeDefaultNode(
nodes: nodes,
sharedPreferences: sharedPreferences,
type: WalletType.solana,
newDefaultUri: solanaDefaultNodeUri,
currentNodePreferenceKey: PreferencesKey.currentSolanaNodeIdKey,
useSSL: true,
oldUri: [
'rpc.ankr.com',
'api.mainnet-beta.solana.com:443',
'solana-rpc.publicnode.com:443',
],
);
break;
default: default:
break; break;
} }
@ -361,7 +392,8 @@ Future<void> _changeDefaultNode({
required String newDefaultUri, required String newDefaultUri,
required String currentNodePreferenceKey, required String currentNodePreferenceKey,
required bool useSSL, required bool useSSL,
required List<String> oldUri, // leave empty if you want to force replace the node regardless of the user's current node required List<String>
oldUri, // leave empty if you want to force replace the node regardless of the user's current node
}) async { }) async {
final currentNodeId = sharedPreferences.getInt(currentNodePreferenceKey); final currentNodeId = sharedPreferences.getInt(currentNodePreferenceKey);
final currentNode = nodes.values.firstWhere((node) => node.key == currentNodeId); final currentNode = nodes.values.firstWhere((node) => node.key == currentNodeId);
@ -389,11 +421,10 @@ Future<void> _changeDefaultNode({
/// Generic function for adding a new Node for a Wallet Type. /// Generic function for adding a new Node for a Wallet Type.
Future<void> updateWalletTypeNodesWithNewNode({ Future<void> updateWalletTypeNodesWithNewNode({
required SharedPreferences sharedPreferences,
required Box<Node> nodes, required Box<Node> nodes,
required WalletType type, required WalletType type,
required String newNodeUri, required String newNodeUri,
required bool useSSL, bool? useSSL,
}) async { }) async {
// If it already exists in the box of nodes, no need to add it annymore. // If it already exists in the box of nodes, no need to add it annymore.
if (nodes.values.any((node) => node.uriRaw == newNodeUri)) return; if (nodes.values.any((node) => node.uriRaw == newNodeUri)) return;
@ -407,26 +438,6 @@ Future<void> updateWalletTypeNodesWithNewNode({
); );
} }
Future<void> _updateCakeXmrNode(Box<Node> nodes) async {
final node = nodes.values.firstWhereOrNull((element) => element.uriRaw == newCakeWalletMoneroUri);
if (node != null) {
node.trusted = true;
node.useSSL = true;
await node.save();
}
}
void updateBtcElectrumNodeToUseSSL(Box<Node> nodes, SharedPreferences sharedPreferences) {
final btcElectrumNode =
nodes.values.firstWhereOrNull((element) => element.uriRaw == newCakeWalletBitcoinUri);
if (btcElectrumNode != null) {
btcElectrumNode.useSSL = true;
btcElectrumNode.save();
}
}
void _deselectExchangeProvider(SharedPreferences sharedPreferences, String providerName) { void _deselectExchangeProvider(SharedPreferences sharedPreferences, String providerName) {
final Map<String, dynamic> exchangeProvidersSelection = final Map<String, dynamic> exchangeProvidersSelection =
json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
@ -445,8 +456,10 @@ void _fixNodesUseSSLFlag(Box<Node> nodes) {
switch (node.uriRaw) { switch (node.uriRaw) {
case cakeWalletLitecoinElectrumUri: case cakeWalletLitecoinElectrumUri:
case cakeWalletBitcoinElectrumUri: case cakeWalletBitcoinElectrumUri:
case newCakeWalletBitcoinUri:
case newCakeWalletMoneroUri:
node.useSSL = true; node.useSSL = true;
break; node.trusted = true;
} }
} }
} }
@ -580,15 +593,6 @@ Future<void> validateBitcoinSavedTransactionPriority(SharedPreferences sharedPre
} }
} }
Future<void> addOnionNode(Box<Node> nodes) async {
final onionNodeUri = "cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081";
// check if the user has this node before (added it manually)
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == onionNodeUri) == null) {
await nodes.add(Node(uri: onionNodeUri, type: WalletType.monero));
}
}
Future<void> replaceNodesMigration({required Box<Node> nodes}) async { Future<void> replaceNodesMigration({required Box<Node> nodes}) async {
final replaceNodes = <String, Node>{ final replaceNodes = <String, Node>{
'eu-node.cakewallet.io:18081': 'eu-node.cakewallet.io:18081':

View file

@ -1,3 +1,5 @@
import 'package:cake_wallet/src/screens/wallet_connect/utils/string_parsing.dart';
class EVMTransactionErrorFeesHandler { class EVMTransactionErrorFeesHandler {
EVMTransactionErrorFeesHandler({ EVMTransactionErrorFeesHandler({
this.balanceWei, this.balanceWei,
@ -64,14 +66,14 @@ class EVMTransactionErrorFeesHandler {
return EVMTransactionErrorFeesHandler( return EVMTransactionErrorFeesHandler(
balanceWei: balanceWei.toString(), balanceWei: balanceWei.toString(),
balanceEth: balanceEth.toString().substring(0, 12), balanceEth: balanceEth.toString().safeSubString(0, 12),
balanceUsd: balanceUsd.toString().substring(0, 4), balanceUsd: balanceUsd.toString().safeSubString(0, 4),
txCostWei: txCostWei.toString(), txCostWei: txCostWei.toString(),
txCostEth: txCostEth.toString().substring(0, 12), txCostEth: txCostEth.toString().safeSubString(0, 12),
txCostUsd: txCostUsd.toString().substring(0, 4), txCostUsd: txCostUsd.toString().safeSubString(0, 4),
overshotWei: overshotWei.toString(), overshotWei: overshotWei.toString(),
overshotEth: overshotEth.toString().substring(0, 12), overshotEth: overshotEth.toString().safeSubString(0, 12),
overshotUsd: overshotUsd.toString().substring(0, 4), overshotUsd: overshotUsd.toString().safeSubString(0, 4),
); );
} else { } else {
// If any value is missing, return an error message // If any value is missing, return an error message

View file

@ -215,7 +215,7 @@ Future<void> initializeAppConfigs() async {
secureStorage: secureStorage, secureStorage: secureStorage,
anonpayInvoiceInfo: anonpayInvoiceInfo, anonpayInvoiceInfo: anonpayInvoiceInfo,
havenSeedStore: havenSeedStore, havenSeedStore: havenSeedStore,
initialMigrationVersion: 45, initialMigrationVersion: 46,
); );
} }

View file

@ -70,6 +70,8 @@ class SeedVerificationStepView extends StatelessWidget {
(option) { (option) {
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
if (walletSeedViewModel.wrongEntries > 2) return;
final isCorrectWord = walletSeedViewModel.isChosenWordCorrect(option); final isCorrectWord = walletSeedViewModel.isChosenWordCorrect(option);
final isSecondWrongEntry = walletSeedViewModel.wrongEntries >= 2; final isSecondWrongEntry = walletSeedViewModel.wrongEntries >= 2;
if (!isCorrectWord) { if (!isCorrectWord) {
@ -81,7 +83,9 @@ class SeedVerificationStepView extends StatelessWidget {
); );
if (isSecondWrongEntry) { if (isSecondWrongEntry) {
Navigator.pop(context); if (context.mounted) {
Navigator.pop(context);
}
} }
} }
}, },

View file

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:math';
import 'package:convert/convert.dart'; import 'package:convert/convert.dart';
@ -13,4 +14,8 @@ extension StringParsing on String {
return this; return this;
} }
String safeSubString(int start, int end) {
return this.substring(0, min(this.toString().length, 12));
}
} }

View file

@ -219,9 +219,9 @@ class ExceptionHandler {
// probably when the device was locked and then opened on Cake // probably when the device was locked and then opened on Cake
// this is solved by a restart of the app // this is solved by a restart of the app
// just ignoring until we find a solution to this issue or migrate from flutter secure storage // just ignoring until we find a solution to this issue or migrate from flutter secure storage
"core/auth_service.dart:63", "core/auth_service.dart:64",
"core/key_service.dart:14", "core/key_service.dart:14",
"core/wallet_loading_service.dart:133", "core/wallet_loading_service.dart:131",
]; ];
static Future<void> _addDeviceInfo(File file) async { static Future<void> _addDeviceInfo(File file) async {

View file

@ -4,13 +4,11 @@ import 'dart:io' show Platform;
import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/bitcoin/bitcoin.dart'; 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/core/key_service.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.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/balance_display_mode.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/provider_types.dart';
import 'package:cake_wallet/entities/service_status.dart'; import 'package:cake_wallet/entities/service_status.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
@ -643,7 +641,7 @@ abstract class DashboardViewModelBase with Store {
transactions.clear(); transactions.clear();
transactions.addAll( transactions = ObservableList.of(
wallet.transactionHistory.transactions.values.map( wallet.transactionHistory.transactions.values.map(
(transaction) => TransactionListItem( (transaction) => TransactionListItem(
transaction: transaction, transaction: transaction,
@ -705,7 +703,7 @@ abstract class DashboardViewModelBase with Store {
monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id) monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
.toList(); .toList();
transactions.addAll( transactions = ObservableList.of(
_accountTransactions.map( _accountTransactions.map(
(transaction) => TransactionListItem( (transaction) => TransactionListItem(
transaction: transaction, transaction: transaction,
@ -725,7 +723,7 @@ abstract class DashboardViewModelBase with Store {
wow.wownero!.getCurrentAccount(wallet).id) wow.wownero!.getCurrentAccount(wallet).id)
.toList(); .toList();
transactions.addAll( transactions = ObservableList.of(
_accountTransactions.map( _accountTransactions.map(
(transaction) => TransactionListItem( (transaction) => TransactionListItem(
transaction: transaction, transaction: transaction,

View file

@ -680,7 +680,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
} }
if (error is CreateAssociatedTokenAccountException) { if (error is CreateAssociatedTokenAccountException) {
return S.current.solana_create_associated_token_account_exception; return "${S.current.solana_create_associated_token_account_exception}\n\n${error.errorMessage}";
} }
if (error is SignSPLTokenTransactionRentException) { if (error is SignSPLTokenTransactionRentException) {

View file

@ -106,7 +106,7 @@ dependencies:
flutter_svg: ^2.0.9 flutter_svg: ^2.0.9
polyseed: ^0.0.6 polyseed: ^0.0.6
nostr_tools: ^1.0.9 nostr_tools: ^1.0.9
solana: ^0.30.1 solana: ^0.31.0+1
ledger_flutter_plus: ^1.4.1 ledger_flutter_plus: ^1.4.1
hashlib: ^1.19.2 hashlib: ^1.19.2

View file

@ -330,6 +330,7 @@
"freeze": "تجميد", "freeze": "تجميد",
"frequently_asked_questions": "الأسئلة الشائعة", "frequently_asked_questions": "الأسئلة الشائعة",
"frozen": "مجمدة", "frozen": "مجمدة",
"frozen_balance": "التوازن المجمد",
"full_balance": "الرصيد الكامل", "full_balance": "الرصيد الكامل",
"gas_exceeds_allowance": "الغاز المطلوب بالمعاملة يتجاوز البدل.", "gas_exceeds_allowance": "الغاز المطلوب بالمعاملة يتجاوز البدل.",
"generate_name": "توليد الاسم", "generate_name": "توليد الاسم",

View file

@ -330,6 +330,7 @@
"freeze": "Замразяване", "freeze": "Замразяване",
"frequently_asked_questions": "Често задавани въпроси", "frequently_asked_questions": "Често задавани въпроси",
"frozen": "Замразени", "frozen": "Замразени",
"frozen_balance": "Замразен баланс",
"full_balance": "Пълен баланс", "full_balance": "Пълен баланс",
"gas_exceeds_allowance": "Газът, изискван от транзакцията, надвишава надбавката.", "gas_exceeds_allowance": "Газът, изискван от транзакцията, надвишава надбавката.",
"generate_name": "Генериране на име", "generate_name": "Генериране на име",

View file

@ -330,6 +330,7 @@
"freeze": "Zmrazit", "freeze": "Zmrazit",
"frequently_asked_questions": "Často kladené otázky", "frequently_asked_questions": "Často kladené otázky",
"frozen": "Zmraženo", "frozen": "Zmraženo",
"frozen_balance": "Zmrazená rovnováha",
"full_balance": "Celkový zůstatek", "full_balance": "Celkový zůstatek",
"gas_exceeds_allowance": "Plyn vyžadovaný transakcí přesahuje příspěvek.", "gas_exceeds_allowance": "Plyn vyžadovaný transakcí přesahuje příspěvek.",
"generate_name": "Generovat jméno", "generate_name": "Generovat jméno",

View file

@ -330,6 +330,7 @@
"freeze": "Einfrieren", "freeze": "Einfrieren",
"frequently_asked_questions": "Häufig gestellte Fragen", "frequently_asked_questions": "Häufig gestellte Fragen",
"frozen": "Gefroren", "frozen": "Gefroren",
"frozen_balance": "Gefrorenes Gleichgewicht",
"full_balance": "Gesamtguthaben", "full_balance": "Gesamtguthaben",
"gas_exceeds_allowance": "Die durch Transaktion erforderliche Gas übertrifft die Zulage.", "gas_exceeds_allowance": "Die durch Transaktion erforderliche Gas übertrifft die Zulage.",
"generate_name": "Namen generieren", "generate_name": "Namen generieren",

View file

@ -330,6 +330,7 @@
"freeze": "Freeze", "freeze": "Freeze",
"frequently_asked_questions": "Frequently asked questions", "frequently_asked_questions": "Frequently asked questions",
"frozen": "Frozen", "frozen": "Frozen",
"frozen_balance": "Frozen Balance",
"full_balance": "Full Balance", "full_balance": "Full Balance",
"gas_exceeds_allowance": "Gas required by transaction exceeds allowance.", "gas_exceeds_allowance": "Gas required by transaction exceeds allowance.",
"generate_name": "Generate Name", "generate_name": "Generate Name",

View file

@ -330,6 +330,7 @@
"freeze": "Congelar", "freeze": "Congelar",
"frequently_asked_questions": "Preguntas frecuentes", "frequently_asked_questions": "Preguntas frecuentes",
"frozen": "Congelada", "frozen": "Congelada",
"frozen_balance": "Equilibrio congelado",
"full_balance": "Balance completo", "full_balance": "Balance completo",
"gas_exceeds_allowance": "El gas requerido por la transacción excede la asignación.", "gas_exceeds_allowance": "El gas requerido por la transacción excede la asignación.",
"generate_name": "Generar nombre", "generate_name": "Generar nombre",

View file

@ -330,6 +330,7 @@
"freeze": "Geler", "freeze": "Geler",
"frequently_asked_questions": "Foire aux questions", "frequently_asked_questions": "Foire aux questions",
"frozen": "Gelées", "frozen": "Gelées",
"frozen_balance": "Équilibre gelé",
"full_balance": "Solde Complet", "full_balance": "Solde Complet",
"gas_exceeds_allowance": "Le gaz requis par la transaction dépasse l'allocation.", "gas_exceeds_allowance": "Le gaz requis par la transaction dépasse l'allocation.",
"generate_name": "Générer un nom", "generate_name": "Générer un nom",

View file

@ -330,6 +330,7 @@
"freeze": "Daskare", "freeze": "Daskare",
"frequently_asked_questions": "Tambayoyin da ake yawan yi", "frequently_asked_questions": "Tambayoyin da ake yawan yi",
"frozen": "Daskararre", "frozen": "Daskararre",
"frozen_balance": "Daidaituwa mai sanyi",
"full_balance": "DUKAN KUDI", "full_balance": "DUKAN KUDI",
"gas_exceeds_allowance": "Gas da ake buƙata ta hanyar ma'amala ya wuce izini.", "gas_exceeds_allowance": "Gas da ake buƙata ta hanyar ma'amala ya wuce izini.",
"generate_name": "Ƙirƙirar Suna", "generate_name": "Ƙirƙirar Suna",

View file

@ -330,6 +330,7 @@
"freeze": "फ्रीज", "freeze": "फ्रीज",
"frequently_asked_questions": "अक्सर पूछे जाने वाले प्रश्न", "frequently_asked_questions": "अक्सर पूछे जाने वाले प्रश्न",
"frozen": "जमा हुआ", "frozen": "जमा हुआ",
"frozen_balance": "जमे हुए संतुलन",
"full_balance": "पूर्ण संतुलन", "full_balance": "पूर्ण संतुलन",
"gas_exceeds_allowance": "लेनदेन द्वारा आवश्यक गैस भत्ता से अधिक है।", "gas_exceeds_allowance": "लेनदेन द्वारा आवश्यक गैस भत्ता से अधिक है।",
"generate_name": "नाम जनरेट करें", "generate_name": "नाम जनरेट करें",

View file

@ -330,6 +330,7 @@
"freeze": "Zamrznuti", "freeze": "Zamrznuti",
"frequently_asked_questions": "Često postavljana pitanja", "frequently_asked_questions": "Često postavljana pitanja",
"frozen": "Smrznuto", "frozen": "Smrznuto",
"frozen_balance": "Smrznuta ravnoteža",
"full_balance": "Pun iznos", "full_balance": "Pun iznos",
"gas_exceeds_allowance": "Plin potreban transakcijom premašuje dodatak.", "gas_exceeds_allowance": "Plin potreban transakcijom premašuje dodatak.",
"generate_name": "Generiraj ime", "generate_name": "Generiraj ime",

View file

@ -330,6 +330,7 @@
"freeze": "Կասեցնել", "freeze": "Կասեցնել",
"frequently_asked_questions": "Հաճախ տրվող հարցեր", "frequently_asked_questions": "Հաճախ տրվող հարցեր",
"frozen": "Կասեցված", "frozen": "Կասեցված",
"frozen_balance": "Սառեցված հավասարակշռություն",
"full_balance": "Լրիվ մնացորդ", "full_balance": "Լրիվ մնացորդ",
"gas_exceeds_allowance": "Գործարքով պահանջվող գազը գերազանցում է նպաստը:", "gas_exceeds_allowance": "Գործարքով պահանջվող գազը գերազանցում է նպաստը:",
"generate_name": "Գեներացնել անուն", "generate_name": "Գեներացնել անուն",

View file

@ -330,6 +330,7 @@
"freeze": "Freeze", "freeze": "Freeze",
"frequently_asked_questions": "Pertanyaan yang sering diajukan", "frequently_asked_questions": "Pertanyaan yang sering diajukan",
"frozen": "Dibekukan", "frozen": "Dibekukan",
"frozen_balance": "Keseimbangan beku",
"full_balance": "Saldo Penuh", "full_balance": "Saldo Penuh",
"gas_exceeds_allowance": "Gas yang dibutuhkan oleh transaksi melebihi tunjangan.", "gas_exceeds_allowance": "Gas yang dibutuhkan oleh transaksi melebihi tunjangan.",
"generate_name": "Hasilkan Nama", "generate_name": "Hasilkan Nama",

View file

@ -331,6 +331,7 @@
"freeze": "Congelare", "freeze": "Congelare",
"frequently_asked_questions": "Domande frequenti", "frequently_asked_questions": "Domande frequenti",
"frozen": "Congelato", "frozen": "Congelato",
"frozen_balance": "Equilibrio congelato",
"full_balance": "Saldo Completo", "full_balance": "Saldo Completo",
"gas_exceeds_allowance": "Il gas richiesto dalla transazione supera l'indennità.", "gas_exceeds_allowance": "Il gas richiesto dalla transazione supera l'indennità.",
"generate_name": "Genera nome", "generate_name": "Genera nome",

View file

@ -330,6 +330,7 @@
"freeze": "氷結", "freeze": "氷結",
"frequently_asked_questions": "よくある質問", "frequently_asked_questions": "よくある質問",
"frozen": "凍った", "frozen": "凍った",
"frozen_balance": "凍結バランス",
"full_balance": "フルバランス", "full_balance": "フルバランス",
"gas_exceeds_allowance": "取引に必要なガスは、手当を超えています。", "gas_exceeds_allowance": "取引に必要なガスは、手当を超えています。",
"generate_name": "名前の生成", "generate_name": "名前の生成",

View file

@ -330,6 +330,7 @@
"freeze": "얼다", "freeze": "얼다",
"frequently_asked_questions": "자주 묻는 질문", "frequently_asked_questions": "자주 묻는 질문",
"frozen": "겨울 왕국", "frozen": "겨울 왕국",
"frozen_balance": "냉동 균형",
"full_balance": "풀 밸런스", "full_balance": "풀 밸런스",
"gas_exceeds_allowance": "거래에 필요한 가스는 수당을 초과합니다.", "gas_exceeds_allowance": "거래에 필요한 가스는 수당을 초과합니다.",
"generate_name": "이름 생성", "generate_name": "이름 생성",

View file

@ -330,6 +330,7 @@
"freeze": "အေးခဲ", "freeze": "အေးခဲ",
"frequently_asked_questions": "မေးလေ့ရှိသောမေးခွန်းများ", "frequently_asked_questions": "မေးလေ့ရှိသောမေးခွန်းများ",
"frozen": "ဖြူဖြူ", "frozen": "ဖြူဖြူ",
"frozen_balance": "လက်ကျန်ငွေ",
"full_balance": "Balance အပြည့်", "full_balance": "Balance အပြည့်",
"gas_exceeds_allowance": "ငွေပေးငွေယူမှလိုအပ်သောဓာတ်ငွေ့ထောက်ပံ့ကြေးကျော်လွန်။", "gas_exceeds_allowance": "ငွေပေးငွေယူမှလိုအပ်သောဓာတ်ငွေ့ထောက်ပံ့ကြေးကျော်လွန်။",
"generate_name": "အမည်ဖန်တီးပါ။", "generate_name": "အမည်ဖန်တီးပါ။",

View file

@ -330,6 +330,7 @@
"freeze": "Bevriezen", "freeze": "Bevriezen",
"frequently_asked_questions": "Veelgestelde vragen", "frequently_asked_questions": "Veelgestelde vragen",
"frozen": "Bevroren", "frozen": "Bevroren",
"frozen_balance": "Bevroren balans",
"full_balance": "Volledig saldo", "full_balance": "Volledig saldo",
"gas_exceeds_allowance": "Gas vereist door transactie overschrijdt de vergoeding.", "gas_exceeds_allowance": "Gas vereist door transactie overschrijdt de vergoeding.",
"generate_name": "Naam genereren", "generate_name": "Naam genereren",

View file

@ -330,6 +330,7 @@
"freeze": "Zamróź", "freeze": "Zamróź",
"frequently_asked_questions": "Często zadawane pytania", "frequently_asked_questions": "Często zadawane pytania",
"frozen": "Zamrożone", "frozen": "Zamrożone",
"frozen_balance": "Mrożona równowaga",
"full_balance": "Pełne saldo", "full_balance": "Pełne saldo",
"gas_exceeds_allowance": "Gaz wymagany przez transakcję przekracza dodatek.", "gas_exceeds_allowance": "Gaz wymagany przez transakcję przekracza dodatek.",
"generate_name": "Wygeneruj nazwę", "generate_name": "Wygeneruj nazwę",

View file

@ -330,6 +330,7 @@
"freeze": "Congelar", "freeze": "Congelar",
"frequently_asked_questions": "Perguntas frequentes", "frequently_asked_questions": "Perguntas frequentes",
"frozen": "Congeladas", "frozen": "Congeladas",
"frozen_balance": "Equilíbrio congelado",
"full_balance": "Saldo total", "full_balance": "Saldo total",
"gas_exceeds_allowance": "O gás exigido pela transação excede o subsídio.", "gas_exceeds_allowance": "O gás exigido pela transação excede o subsídio.",
"generate_name": "Gerar nome", "generate_name": "Gerar nome",

View file

@ -330,6 +330,7 @@
"freeze": "Заморозить", "freeze": "Заморозить",
"frequently_asked_questions": "Часто задаваемые вопросы", "frequently_asked_questions": "Часто задаваемые вопросы",
"frozen": "Заморожено", "frozen": "Заморожено",
"frozen_balance": "Замороженный баланс",
"full_balance": "Весь баланс", "full_balance": "Весь баланс",
"gas_exceeds_allowance": "Газ, требуемый в результате транзакции, превышает пособие.", "gas_exceeds_allowance": "Газ, требуемый в результате транзакции, превышает пособие.",
"generate_name": "Создать имя", "generate_name": "Создать имя",

View file

@ -330,6 +330,7 @@
"freeze": "ดักจับ", "freeze": "ดักจับ",
"frequently_asked_questions": "คำถามที่พบบ่อย", "frequently_asked_questions": "คำถามที่พบบ่อย",
"frozen": "ถูกดักจับ", "frozen": "ถูกดักจับ",
"frozen_balance": "สมดุลแช่แข็ง",
"full_balance": "ยอดคงเหลือทั้งหมด", "full_balance": "ยอดคงเหลือทั้งหมด",
"gas_exceeds_allowance": "ก๊าซที่ต้องการโดยการทำธุรกรรมเกินค่าเผื่อ", "gas_exceeds_allowance": "ก๊าซที่ต้องการโดยการทำธุรกรรมเกินค่าเผื่อ",
"generate_name": "สร้างชื่อ", "generate_name": "สร้างชื่อ",

View file

@ -330,6 +330,7 @@
"freeze": "I-freeze", "freeze": "I-freeze",
"frequently_asked_questions": "Mga madalas itanong", "frequently_asked_questions": "Mga madalas itanong",
"frozen": "Frozen", "frozen": "Frozen",
"frozen_balance": "Frozen na balanse",
"full_balance": "Buong Balanse", "full_balance": "Buong Balanse",
"gas_exceeds_allowance": "Ang gas na kinakailangan ng transaksyon ay lumampas sa allowance.", "gas_exceeds_allowance": "Ang gas na kinakailangan ng transaksyon ay lumampas sa allowance.",
"generate_name": "Bumuo ng pangalan", "generate_name": "Bumuo ng pangalan",

View file

@ -330,6 +330,7 @@
"freeze": "Dondur", "freeze": "Dondur",
"frequently_asked_questions": "Sıkça sorulan sorular", "frequently_asked_questions": "Sıkça sorulan sorular",
"frozen": "Dondurulmuş", "frozen": "Dondurulmuş",
"frozen_balance": "Dondurulmuş denge",
"full_balance": "Tüm bakiye", "full_balance": "Tüm bakiye",
"gas_exceeds_allowance": "İşlemin gerektirdiği gaz ödeneği aşar.", "gas_exceeds_allowance": "İşlemin gerektirdiği gaz ödeneği aşar.",
"generate_name": "İsim Oluştur", "generate_name": "İsim Oluştur",

View file

@ -330,6 +330,7 @@
"freeze": "Заморозити", "freeze": "Заморозити",
"frequently_asked_questions": "Часті запитання", "frequently_asked_questions": "Часті запитання",
"frozen": "Заморожено", "frozen": "Заморожено",
"frozen_balance": "Заморожений баланс",
"full_balance": "Весь баланс", "full_balance": "Весь баланс",
"gas_exceeds_allowance": "Газ, необхідний транзакціям, перевищує надбавку.", "gas_exceeds_allowance": "Газ, необхідний транзакціям, перевищує надбавку.",
"generate_name": "Згенерувати назву", "generate_name": "Згенерувати назву",

View file

@ -330,6 +330,7 @@
"freeze": "منجمد", "freeze": "منجمد",
"frequently_asked_questions": "اکثر پوچھے گئے سوالات", "frequently_asked_questions": "اکثر پوچھے گئے سوالات",
"frozen": "منجمد", "frozen": "منجمد",
"frozen_balance": "منجمد توازن",
"full_balance": "مکمل بیلنس", "full_balance": "مکمل بیلنس",
"gas_exceeds_allowance": "لین دین کے ذریعہ درکار گیس الاؤنس سے زیادہ ہے۔", "gas_exceeds_allowance": "لین دین کے ذریعہ درکار گیس الاؤنس سے زیادہ ہے۔",
"generate_name": "نام پیدا کریں۔", "generate_name": "نام پیدا کریں۔",

View file

@ -329,6 +329,7 @@
"freeze": "Đóng băng", "freeze": "Đóng băng",
"frequently_asked_questions": "Các câu hỏi thường gặp", "frequently_asked_questions": "Các câu hỏi thường gặp",
"frozen": "Đã đóng băng", "frozen": "Đã đóng băng",
"frozen_balance": "Cân bằng đông lạnh",
"full_balance": "Số dư đầy đủ", "full_balance": "Số dư đầy đủ",
"gas_exceeds_allowance": "Gas theo yêu cầu của giao dịch vượt quá trợ cấp.", "gas_exceeds_allowance": "Gas theo yêu cầu của giao dịch vượt quá trợ cấp.",
"generate_name": "Tạo tên", "generate_name": "Tạo tên",

View file

@ -331,6 +331,7 @@
"freeze": "Tì pa", "freeze": "Tì pa",
"frequently_asked_questions": "Àwọn ìbéèrè la máa ń béèrè", "frequently_asked_questions": "Àwọn ìbéèrè la máa ń béèrè",
"frozen": "Ó l'a tì pa", "frozen": "Ó l'a tì pa",
"frozen_balance": "Iwontunwonsi ti o tutu",
"full_balance": "Ìyókù owó kíkún", "full_balance": "Ìyókù owó kíkún",
"gas_exceeds_allowance": "Gaasi ti a beere nipasẹ idunadura ju lọ.", "gas_exceeds_allowance": "Gaasi ti a beere nipasẹ idunadura ju lọ.",
"generate_name": "Ṣẹda Orukọ", "generate_name": "Ṣẹda Orukọ",

View file

@ -330,6 +330,7 @@
"freeze": "凍結", "freeze": "凍結",
"frequently_asked_questions": "常见问题", "frequently_asked_questions": "常见问题",
"frozen": "凍結的", "frozen": "凍結的",
"frozen_balance": "冷冻平衡",
"full_balance": "全部余额", "full_balance": "全部余额",
"gas_exceeds_allowance": "交易要求的气体超出了津贴。", "gas_exceeds_allowance": "交易要求的气体超出了津贴。",
"generate_name": "生成名称", "generate_name": "生成名称",

View file

@ -30,7 +30,6 @@ class SecretKey {
SecretKey('twitterBearerToken', () => ''), SecretKey('twitterBearerToken', () => ''),
SecretKey('anonPayReferralCode', () => ''), SecretKey('anonPayReferralCode', () => ''),
SecretKey('fiatApiKey', () => ''), SecretKey('fiatApiKey', () => ''),
SecretKey('payfuraApiKey', () => ''),
SecretKey('chatwootWebsiteToken', () => ''), SecretKey('chatwootWebsiteToken', () => ''),
SecretKey('exolixApiKey', () => ''), SecretKey('exolixApiKey', () => ''),
SecretKey('robinhoodApplicationId', () => ''), SecretKey('robinhoodApplicationId', () => ''),
@ -38,6 +37,7 @@ class SecretKey {
SecretKey('walletConnectProjectId', () => ''), SecretKey('walletConnectProjectId', () => ''),
SecretKey('moralisApiKey', () => ''), SecretKey('moralisApiKey', () => ''),
SecretKey('ankrApiKey', () => ''), SecretKey('ankrApiKey', () => ''),
SecretKey('chainStackApiKey', () => ''),
SecretKey('quantexExchangeMarkup', () => ''), SecretKey('quantexExchangeMarkup', () => ''),
SecretKey('seeds', () => ''), SecretKey('seeds', () => ''),
SecretKey('testCakePayApiKey', () => ''), SecretKey('testCakePayApiKey', () => ''),
@ -86,6 +86,7 @@ class SecretKey {
static final solanaSecrets = [ static final solanaSecrets = [
SecretKey('ankrApiKey', () => ''), SecretKey('ankrApiKey', () => ''),
SecretKey('chainStackApiKey', () => ''),
]; ];
static final nanoSecrets = [ static final nanoSecrets = [