diff --git a/cw_bitcoin/lib/bitcoin_transaction_priority.dart b/cw_bitcoin/lib/bitcoin_transaction_priority.dart index d51775368..7c4dcfd5f 100644 --- a/cw_bitcoin/lib/bitcoin_transaction_priority.dart +++ b/cw_bitcoin/lib/bitcoin_transaction_priority.dart @@ -37,7 +37,7 @@ class BitcoinTransactionPriority extends TransactionPriority { switch (this) { case BitcoinTransactionPriority.slow: - label = 'Slow ~24hrs'; // '${S.current.transaction_priority_slow} ~24hrs'; + label = 'Slow ~24hrs+'; // '${S.current.transaction_priority_slow} ~24hrs'; break; case BitcoinTransactionPriority.medium: label = 'Medium'; // S.current.transaction_priority_medium; diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 64147e984..2df8e5b2b 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -212,10 +212,14 @@ abstract class ElectrumWalletBase List privateKeys = []; int allInputsAmount = 0; + bool spendsUnconfirmedTX = false; + for (int i = 0; i < unspentCoins.length; i++) { final utx = unspentCoins[i]; - if (utx.isSending) { + if (utx.isSending && !utx.isFrozen) { + if (!spendsUnconfirmedTX) spendsUnconfirmedTX = utx.confirmations == 0; + allInputsAmount += utx.value; final address = addressTypeFromStr(utx.address, network); @@ -273,6 +277,10 @@ abstract class ElectrumWalletBase // Here, when sending all, the output amount equals to the input value - fee to fully spend every input on the transaction and have no amount left for change int amount = allInputsAmount - fee; + if (amount <= 0) { + throw BitcoinTransactionWrongBalanceException(); + } + // Attempting to send less than the dust limit if (_isBelowDust(amount)) { throw BitcoinTransactionNoDustException(); @@ -297,6 +305,7 @@ abstract class ElectrumWalletBase isSendAll: true, hasChange: false, memo: memo, + spendsUnconfirmedTX: spendsUnconfirmedTX, ); } @@ -306,17 +315,25 @@ abstract class ElectrumWalletBase int feeRate, { int? inputsCount, String? memo, + bool? useUnconfirmed, }) async { final utxos = []; List privateKeys = []; int allInputsAmount = 0; + bool spendsUnconfirmedTX = false; int leftAmount = credentialsAmount; - final sendingCoins = unspentCoins.where((utx) => utx.isSending).toList(); + final sendingCoins = unspentCoins.where((utx) => utx.isSending && !utx.isFrozen).toList(); + final unconfirmedCoins = sendingCoins.where((utx) => utx.confirmations == 0).toList(); for (int i = 0; i < sendingCoins.length; i++) { final utx = sendingCoins[i]; + final isUncormirmed = utx.confirmations == 0; + if (useUnconfirmed != true && isUncormirmed) continue; + + if (!spendsUnconfirmedTX) spendsUnconfirmedTX = isUncormirmed; + allInputsAmount += utx.value; leftAmount = leftAmount - utx.value; @@ -354,11 +371,23 @@ abstract class ElectrumWalletBase } final spendingAllCoins = sendingCoins.length == utxos.length; + final spendingAllConfirmedCoins = + !spendsUnconfirmedTX && utxos.length == sendingCoins.length - unconfirmedCoins.length; // How much is being spent - how much is being sent int amountLeftForChangeAndFee = allInputsAmount - credentialsAmount; if (amountLeftForChangeAndFee <= 0) { + if (!spendingAllCoins) { + return estimateTxForAmount( + credentialsAmount, + outputs, + feeRate, + inputsCount: utxos.length + 1, + memo: memo, + useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins, + ); + } throw BitcoinTransactionWrongBalanceException(); } @@ -412,6 +441,7 @@ abstract class ElectrumWalletBase feeRate, inputsCount: utxos.length + 1, memo: memo, + useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins, ); } @@ -458,6 +488,7 @@ abstract class ElectrumWalletBase feeRate, inputsCount: utxos.length + 1, memo: memo, + useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins, ); } } @@ -470,6 +501,7 @@ abstract class ElectrumWalletBase hasChange: true, isSendAll: false, memo: memo, + spendsUnconfirmedTX: spendsUnconfirmedTX, ); } @@ -540,7 +572,7 @@ abstract class ElectrumWalletBase network: network, memo: estimatedTx.memo, outputOrdering: BitcoinOrdering.none, - enableRBF: true, + enableRBF: !estimatedTx.spendsUnconfirmedTX, ); } else { txb = BitcoinTransactionBuilder( @@ -550,7 +582,7 @@ abstract class ElectrumWalletBase network: network, memo: estimatedTx.memo, outputOrdering: BitcoinOrdering.none, - enableRBF: true, + enableRBF: !estimatedTx.spendsUnconfirmedTX, ); } @@ -730,6 +762,7 @@ abstract class ElectrumWalletBase final tx = await fetchTransactionInfo( hash: coin.hash, height: 0, myAddresses: addressesSet); coin.isChange = tx?.direction == TransactionDirection.outgoing; + coin.confirmations = tx?.confirmations; updatedUnspentCoins.add(coin); } catch (_) {} })))); @@ -754,6 +787,7 @@ abstract class ElectrumWalletBase coin.isFrozen = coinInfo.isFrozen; coin.isSending = coinInfo.isSending; coin.note = coinInfo.note; + coin.bitcoinAddressRecord.balance += coinInfo.value; } else { _addCoinInfo(coin); } @@ -1046,9 +1080,11 @@ abstract class ElectrumWalletBase return Future.wait(addressesByType.map((addressRecord) async { final history = await _fetchAddressHistory(addressRecord, addressesSet, currentHeight); + final balance = await electrumClient.getBalance(addressRecord.scriptHash!); if (history.isNotEmpty) { addressRecord.txCount = history.length; + addressRecord.balance = balance['confirmed'] as int? ?? 0; historiesWithDetails.addAll(history); final matchedAddresses = @@ -1341,6 +1377,7 @@ class EstimatedTxResult { required this.hasChange, required this.isSendAll, this.memo, + required this.spendsUnconfirmedTX, }); final List utxos; @@ -1350,6 +1387,7 @@ class EstimatedTxResult { final bool hasChange; final bool isSendAll; final String? memo; + final bool spendsUnconfirmedTX; } BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) { diff --git a/cw_bitcoin/lib/exceptions.dart b/cw_bitcoin/lib/exceptions.dart index 4b03eb922..979c1a433 100644 --- a/cw_bitcoin/lib/exceptions.dart +++ b/cw_bitcoin/lib/exceptions.dart @@ -15,7 +15,9 @@ class BitcoinTransactionNoDustOnChangeException extends TransactionNoDustOnChang BitcoinTransactionNoDustOnChangeException(super.max, super.min); } -class BitcoinTransactionCommitFailed extends TransactionCommitFailed {} +class BitcoinTransactionCommitFailed extends TransactionCommitFailed { + BitcoinTransactionCommitFailed({super.errorMessage}); +} class BitcoinTransactionCommitFailedDustChange extends TransactionCommitFailedDustChange {} diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index 529ac61da..a59b4f429 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -73,7 +73,9 @@ class PendingBitcoinTransaction with PendingTransaction { if (error.contains("bad-txns-vout-negative")) { throw BitcoinTransactionCommitFailedVoutNegative(); } + throw BitcoinTransactionCommitFailed(errorMessage: error); } + throw BitcoinTransactionCommitFailed(); } diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 7a40a9a3e..1d034430e 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" asn1lib: dependency: transitive description: @@ -153,10 +153,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" url: "https://pub.dev" source: hosted - version: "2.4.8" + version: "2.4.9" build_runner_core: dependency: transitive description: @@ -177,10 +177,10 @@ packages: dependency: transitive description: name: built_value - sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6 + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.9.0" + version: "8.9.2" characters: dependency: transitive description: @@ -309,10 +309,10 @@ packages: dependency: "direct main" description: name: flutter_mobx - sha256: "4a5d062ff85ed3759f4aac6410ff0ffae32e324b2e71ca722ae1b37b32e865f4" + sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e" url: "https://pub.dev" source: hosted - version: "2.2.0+2" + version: "2.2.1+1" flutter_test: dependency: "direct dev" description: flutter @@ -322,10 +322,10 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" glob: dependency: transitive description: @@ -490,10 +490,10 @@ packages: dependency: "direct main" description: name: mobx - sha256: "74ee54012dc7c1b3276eaa960a600a7418ef5f9997565deb8fca1fd88fb36b78" + sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e" url: "https://pub.dev" source: hosted - version: "2.3.0+1" + version: "2.3.3+2" mobx_codegen: dependency: "direct dev" description: @@ -594,10 +594,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" + sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333" url: "https://pub.dev" source: hosted - version: "3.7.4" + version: "3.8.0" pool: dependency: transitive description: @@ -610,10 +610,10 @@ packages: dependency: transitive description: name: provider - sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "6.1.2" pub_semver: dependency: transitive description: @@ -787,6 +787,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: diff --git a/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart b/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart index da4710a8b..6d2ab4696 100644 --- a/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart +++ b/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart @@ -62,7 +62,9 @@ class PendingBitcoinCashTransaction with PendingTransaction { if (error.contains("bad-txns-vout-negative")) { throw BitcoinTransactionCommitFailedVoutNegative(); } + throw BitcoinTransactionCommitFailed(errorMessage: error); } + throw BitcoinTransactionCommitFailed(); } diff --git a/cw_core/lib/exceptions.dart b/cw_core/lib/exceptions.dart index 848ac40e6..d07da8109 100644 --- a/cw_core/lib/exceptions.dart +++ b/cw_core/lib/exceptions.dart @@ -19,7 +19,11 @@ class TransactionNoDustOnChangeException implements Exception { final String min; } -class TransactionCommitFailed implements Exception {} +class TransactionCommitFailed implements Exception { + final String? errorMessage; + + TransactionCommitFailed({this.errorMessage}); +} class TransactionCommitFailedDustChange implements Exception {} diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index d7e91d692..9d0806851 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -10,7 +10,7 @@ import 'package:http/io_client.dart' as ioc; part 'node.g.dart'; -Uri createUriFromElectrumAddress(String address) => Uri.tryParse('tcp://$address')!; +Uri createUriFromElectrumAddress(String address, String path) => Uri.tryParse('tcp://$address$path')!; @HiveType(typeId: Node.typeId) class Node extends HiveObject with Keyable { @@ -83,7 +83,7 @@ class Node extends HiveObject with Keyable { case WalletType.bitcoin: case WalletType.litecoin: case WalletType.bitcoinCash: - return createUriFromElectrumAddress(uriRaw); + return createUriFromElectrumAddress(uriRaw, path ?? ''); case WalletType.nano: case WalletType.banano: if (isSSL) { @@ -94,7 +94,7 @@ class Node extends HiveObject with Keyable { case WalletType.ethereum: case WalletType.polygon: case WalletType.solana: - return Uri.https(uriRaw, ''); + return Uri.https(uriRaw, path ?? ''); default: throw Exception('Unexpected type ${type.toString()} for Node uri'); } diff --git a/cw_core/lib/unspent_transaction_output.dart b/cw_core/lib/unspent_transaction_output.dart index b52daf43c..595df18f4 100644 --- a/cw_core/lib/unspent_transaction_output.dart +++ b/cw_core/lib/unspent_transaction_output.dart @@ -14,6 +14,7 @@ class Unspent { bool isChange; bool isSending; bool isFrozen; + int? confirmations; String note; bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc'); diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index 678e57b54..aef76f300 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" convert: dependency: transitive description: @@ -343,18 +343,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -564,10 +564,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -612,10 +612,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" timing: dependency: transitive description: @@ -641,13 +641,21 @@ packages: source: hosted version: "2.1.4" watcher: - dependency: transitive + dependency: "direct overridden" description: name: watcher sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -681,5 +689,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.10.0" diff --git a/cw_evm/lib/evm_chain_client.dart b/cw_evm/lib/evm_chain_client.dart index eebbe4f4f..8f0df3926 100644 --- a/cw_evm/lib/evm_chain_client.dart +++ b/cw_evm/lib/evm_chain_client.dart @@ -234,14 +234,17 @@ abstract class EVMChainClient { final decodedResponse = jsonDecode(response.body)[0] as Map; + + final symbol = (decodedResponse['symbol'] ?? '') as String; + String filteredSymbol = symbol.replaceFirst(RegExp('^\\\$'), ''); + final name = decodedResponse['name'] ?? ''; - final symbol = decodedResponse['symbol'] ?? ''; final decimal = decodedResponse['decimals'] ?? '0'; final iconPath = decodedResponse['logo'] ?? ''; return Erc20Token( name: name, - symbol: symbol, + symbol: filteredSymbol, contractAddress: contractAddress, decimal: int.tryParse(decimal) ?? 0, iconPath: iconPath, diff --git a/cw_evm/lib/evm_chain_wallet.dart b/cw_evm/lib/evm_chain_wallet.dart index 3e99d5ffa..2884cb0a7 100644 --- a/cw_evm/lib/evm_chain_wallet.dart +++ b/cw_evm/lib/evm_chain_wallet.dart @@ -470,9 +470,15 @@ abstract class EVMChainWalletBase await token.delete(); balance.remove(token); + await _removeTokenTransactionsInHistory(token); _updateBalance(); } + Future _removeTokenTransactionsInHistory(Erc20Token token) async { + transactionHistory.transactions.removeWhere((key, value) => value.tokenSymbol == token.title); + await transactionHistory.save(); + } + Future getErc20Token(String contractAddress, String chainName) async => await _client.getErc20Token(contractAddress, chainName); diff --git a/cw_haven/pubspec.lock b/cw_haven/pubspec.lock index b0a350cc7..d84523539 100644 --- a/cw_haven/pubspec.lock +++ b/cw_haven/pubspec.lock @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.0.0" build_resolvers: dependency: "direct dev" description: @@ -85,10 +85,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.4.9" build_runner_core: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" convert: dependency: transitive description: @@ -350,18 +350,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -563,10 +563,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -611,10 +611,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" timing: dependency: transitive description: @@ -640,13 +640,21 @@ packages: source: hosted version: "2.1.4" watcher: - dependency: transitive + dependency: "direct overridden" description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -680,5 +688,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.7.0" diff --git a/cw_monero/example/pubspec.lock b/cw_monero/example/pubspec.lock index c9ca8d92b..ece0d4395 100644 --- a/cw_monero/example/pubspec.lock +++ b/cw_monero/example/pubspec.lock @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" convert: dependency: transitive description: @@ -213,18 +213,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -354,10 +354,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -394,10 +394,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" typed_data: dependency: transitive description: @@ -414,6 +414,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" win32: dependency: transitive description: @@ -431,5 +439,5 @@ packages: source: hosted version: "0.2.0+3" sdks: - dart: ">=3.0.6 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.7.0" diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock index 0f8f2c90e..b736f80cb 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.0.0" build_resolvers: dependency: "direct dev" description: @@ -85,10 +85,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.4.9" build_runner_core: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" convert: dependency: transitive description: @@ -366,18 +366,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -587,10 +587,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -635,10 +635,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" timing: dependency: transitive description: @@ -664,13 +664,21 @@ packages: source: hosted version: "2.1.4" watcher: - dependency: transitive + dependency: "direct overridden" description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -704,5 +712,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=3.0.6 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.7.0" diff --git a/cw_solana/lib/solana_transaction_info.dart b/cw_solana/lib/solana_transaction_info.dart index 1b7610e34..7a0844e52 100644 --- a/cw_solana/lib/solana_transaction_info.dart +++ b/cw_solana/lib/solana_transaction_info.dart @@ -34,10 +34,7 @@ class SolanaTransactionInfo extends TransactionInfo { @override String amountFormatted() { String stringBalance = solAmount.toString(); - - if (stringBalance.toString().length >= 6) { - stringBalance = stringBalance.substring(0, 6); - } + return '$stringBalance $tokenSymbol'; } diff --git a/cw_solana/lib/solana_wallet.dart b/cw_solana/lib/solana_wallet.dart index 20389c63f..92bd5177c 100644 --- a/cw_solana/lib/solana_wallet.dart +++ b/cw_solana/lib/solana_wallet.dart @@ -111,7 +111,17 @@ abstract class SolanaWalletBase String? get seed => _mnemonic; @override - String get privateKey => HEX.encode(_keyPairData!.bytes); + String get privateKey { + final privateKeyBytes = _keyPairData!.bytes; + + final publicKeyBytes = _keyPairData!.publicKey.bytes; + + final encodedBytes = privateKeyBytes + publicKeyBytes; + + final privateKey = base58encode(encodedBytes); + + return privateKey; + } Future init() async { final boxName = "${walletInfo.name.replaceAll(" ", "_")}_${SPLToken.boxName}"; @@ -138,8 +148,8 @@ abstract class SolanaWalletBase assert(mnemonic != null || privateKey != null); if (privateKey != null) { - final privateKeyBytes = HEX.decode(privateKey); - return await Wallet.fromPrivateKeyBytes(privateKey: privateKeyBytes); + final privateKeyBytes = base58decode(privateKey); + return await Wallet.fromPrivateKeyBytes(privateKey: privateKeyBytes.take(32).toList()); } return Wallet.fromMnemonic(mnemonic!, account: 0, change: 0); @@ -265,32 +275,12 @@ abstract class SolanaWalletBase final transactions = await _client.fetchTransactions(address); - final Map result = {}; - - for (var transactionModel in transactions) { - result[transactionModel.id] = SolanaTransactionInfo( - id: transactionModel.id, - to: transactionModel.to, - from: transactionModel.from, - blockTime: transactionModel.blockTime, - direction: transactionModel.isOutgoingTx - ? TransactionDirection.outgoing - : TransactionDirection.incoming, - solAmount: transactionModel.amount, - isPending: false, - txFee: transactionModel.fee, - tokenSymbol: transactionModel.tokenSymbol, - ); - } - - transactionHistory.addMany(result); - - await transactionHistory.save(); + await _addTransactionsToTransactionHistory(transactions); } /// Fetches the SPL Tokens transactions linked to the token account Public Key Future _updateSPLTokenTransactions() async { - List splTokenTransactions = []; + // List splTokenTransactions = []; // Make a copy of keys to avoid concurrent modification var tokenKeys = List.from(balance.keys); @@ -304,13 +294,20 @@ abstract class SolanaWalletBase _walletKeyPair!, ); - splTokenTransactions.addAll(tokenTxs); + // splTokenTransactions.addAll(tokenTxs); + await _addTransactionsToTransactionHistory(tokenTxs); } } + // await _addTransactionsToTransactionHistory(splTokenTransactions); + } + + Future _addTransactionsToTransactionHistory( + List transactions, + ) async { final Map result = {}; - for (var transactionModel in splTokenTransactions) { + for (var transactionModel in transactions) { result[transactionModel.id] = SolanaTransactionInfo( id: transactionModel.id, to: transactionModel.to, @@ -452,12 +449,23 @@ abstract class SolanaWalletBase await token.delete(); balance.remove(token); + await _removeTokenTransactionsInHistory(token); _updateBalance(); } + Future _removeTokenTransactionsInHistory(SPLToken token) async { + transactionHistory.transactions.removeWhere((key, value) => value.tokenSymbol == token.title); + await transactionHistory.save(); + } + Future getSPLToken(String mintAddress) async { // Convert SPL token mint address to public key - final mintPublicKey = Ed25519HDPublicKey.fromBase58(mintAddress); + final Ed25519HDPublicKey mintPublicKey; + try { + mintPublicKey = Ed25519HDPublicKey.fromBase58(mintAddress); + } catch (_) { + return null; + } // Fetch token's metadata account try { @@ -472,10 +480,12 @@ abstract class SolanaWalletBase iconPath = await _client.getIconImageFromTokenUri(token.uri); } catch (_) {} + String filteredTokenSymbol = token.symbol.replaceFirst(RegExp('^\\\$'), ''); + return SPLToken.fromMetadata( name: token.name, mint: token.mint, - symbol: token.symbol, + symbol: filteredTokenSymbol, mintAddress: mintAddress, iconPath: iconPath, ); diff --git a/cw_solana/lib/spl_token.dart b/cw_solana/lib/spl_token.dart index 0b3b8b372..a40eb0b86 100644 --- a/cw_solana/lib/spl_token.dart +++ b/cw_solana/lib/spl_token.dart @@ -19,7 +19,7 @@ class SPLToken extends CryptoCurrency with HiveObjectMixin { @HiveField(3) final int decimal; - @HiveField(4, defaultValue: false) + @HiveField(4, defaultValue: true) bool _enabled; @HiveField(5) @@ -39,7 +39,7 @@ class SPLToken extends CryptoCurrency with HiveObjectMixin { required this.mint, this.iconPath, this.tag = 'SOL', - bool enabled = false, + bool enabled = true, }) : _enabled = enabled, super( name: mint.toLowerCase(), diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 862ce9db5..707f1157b 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -282,13 +282,20 @@ class CWBitcoin extends Bitcoin { } @override - int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, - {int? size}) { + int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount, + {int? outputsCount, int? size}) { final bitcoinWallet = wallet as ElectrumWallet; - return bitcoinWallet.feeAmountWithFeeRate( + return bitcoinWallet.calculateEstimatedFeeWithFeeRate( feeRate, - inputsCount, - outputsCount, + amount, + outputsCount: outputsCount, + size: size, ); } + + @override + int getMaxCustomFeeRate(Object wallet) { + final bitcoinWallet = wallet as ElectrumWallet; + return (bitcoinWallet.feeRate(BitcoinTransactionPriority.fast) * 1.1).round(); + } } diff --git a/lib/buy/dfx/dfx_buy_provider.dart b/lib/buy/dfx/dfx_buy_provider.dart index 3ba7b4d11..603dc0269 100644 --- a/lib/buy/dfx/dfx_buy_provider.dart +++ b/lib/buy/dfx/dfx_buy_provider.dart @@ -17,13 +17,12 @@ class DFXBuyProvider extends BuyProvider { : super(wallet: wallet, isTestEnvironment: isTestEnvironment); static const _baseUrl = 'api.dfx.swiss'; - static const _authPath = '/v1/auth/signMessage'; - static const _signUpPath = '/v1/auth/signUp'; - static const _signInPath = '/v1/auth/signIn'; + // static const _signMessagePath = '/v1/auth/signMessage'; + static const _authPath = '/v1/auth'; static const walletName = 'CakeWallet'; @override - String get title => 'DFX Connect'; + String get title => 'DFX.swiss'; @override String get providerDescription => S.current.dfx_option_description; @@ -73,21 +72,25 @@ class DFXBuyProvider extends BuyProvider { String get walletAddress => wallet.walletAddresses.primaryAddress ?? wallet.walletAddresses.address; - Future getSignMessage() async { - final uri = Uri.https(_baseUrl, _authPath, {'address': walletAddress}); + Future getSignMessage() async => + "By_signing_this_message,_you_confirm_that_you_are_the_sole_owner_of_the_provided_Blockchain_address._Your_ID:_$walletAddress"; - var response = await http.get(uri, headers: {'accept': 'application/json'}); + // // Lets keep this just in case, but we can avoid this API Call + // Future getSignMessage() async { + // final uri = Uri.https(_baseUrl, _signMessagePath, {'address': walletAddress}); + // + // final response = await http.get(uri, headers: {'accept': 'application/json'}); + // + // if (response.statusCode == 200) { + // final responseBody = jsonDecode(response.body); + // return responseBody['message'] as String; + // } else { + // throw Exception( + // 'Failed to get sign message. Status: ${response.statusCode} ${response.body}'); + // } + // } - if (response.statusCode == 200) { - final responseBody = jsonDecode(response.body); - return responseBody['message'] as String; - } else { - throw Exception( - 'Failed to get sign message. Status: ${response.statusCode} ${response.body}'); - } - } - - Future signUp() async { + Future auth() async { final signMessage = await getSignature(await getSignMessage()); final requestBody = jsonEncode({ @@ -96,7 +99,7 @@ class DFXBuyProvider extends BuyProvider { 'signature': signMessage, }); - final uri = Uri.https(_baseUrl, _signUpPath); + final uri = Uri.https(_baseUrl, _authPath); var response = await http.post( uri, headers: {'Content-Type': 'application/json'}, @@ -115,33 +118,6 @@ class DFXBuyProvider extends BuyProvider { } } - Future signIn() async { - final signMessage = await getSignature(await getSignMessage()); - - final requestBody = jsonEncode({ - 'address': walletAddress, - 'signature': signMessage, - }); - - final uri = Uri.https(_baseUrl, _signInPath); - var response = await http.post( - uri, - headers: {'Content-Type': 'application/json'}, - body: requestBody, - ); - - if (response.statusCode == 201) { - final responseBody = jsonDecode(response.body); - return responseBody['accessToken'] as String; - } else if (response.statusCode == 403) { - final responseBody = jsonDecode(response.body); - final message = responseBody['message'] ?? 'Service unavailable in your country'; - throw Exception(message); - } else { - throw Exception('Failed to sign in. Status: ${response.statusCode} ${response.body}'); - } - } - Future getSignature(String message) async { switch (wallet.type) { case WalletType.ethereum: @@ -151,7 +127,7 @@ class DFXBuyProvider extends BuyProvider { case WalletType.litecoin: case WalletType.bitcoin: case WalletType.bitcoinCash: - return wallet.signMessage(message, address: walletAddress); + return await wallet.signMessage(message, address: walletAddress); default: throw Exception("WalletType is not available for DFX ${wallet.type}"); } @@ -164,17 +140,7 @@ class DFXBuyProvider extends BuyProvider { final blockchain = this.blockchain; final actionType = isBuyAction == true ? '/buy' : '/sell'; - String accessToken; - - try { - accessToken = await signUp(); - } on Exception catch (e) { - if (e.toString().contains('409')) { - accessToken = await signIn(); - } else { - rethrow; - } - } + final accessToken = await auth(); final uri = Uri.https('services.dfx.swiss', actionType, { 'session': accessToken, @@ -198,7 +164,7 @@ class DFXBuyProvider extends BuyProvider { context: context, builder: (BuildContext context) { return AlertWithOneAction( - alertTitle: "DFX Connect", + alertTitle: "DFX.swiss", alertContent: S.of(context).buy_provider_unavailable + ': $e', buttonText: S.of(context).ok, buttonAction: () => Navigator.of(context).pop()); diff --git a/lib/core/create_trade_result.dart b/lib/core/create_trade_result.dart new file mode 100644 index 000000000..0e873d51e --- /dev/null +++ b/lib/core/create_trade_result.dart @@ -0,0 +1,9 @@ +class CreateTradeResult { + bool result; + String? errorMessage; + + CreateTradeResult({ + required this.result, + this.errorMessage, + }); +} diff --git a/lib/core/node_address_validator.dart b/lib/core/node_address_validator.dart index c1fe4ba91..0c8a0c37c 100644 --- a/lib/core/node_address_validator.dart +++ b/lib/core/node_address_validator.dart @@ -11,5 +11,9 @@ class NodeAddressValidator extends TextValidator { class NodePathValidator extends TextValidator { NodePathValidator() - : super(errorMessage: S.current.error_text_node_address, pattern: '^([/0-9a-zA-Z.\-]+)?\$'); + : super( + errorMessage: S.current.error_text_node_address, + pattern: '^([/0-9a-zA-Z.\-]+)?\$', + isAutovalidate: true, + ); } diff --git a/lib/core/wallet_loading_service.dart b/lib/core/wallet_loading_service.dart index 3323e7831..1f17a7a1c 100644 --- a/lib/core/wallet_loading_service.dart +++ b/lib/core/wallet_loading_service.dart @@ -1,27 +1,28 @@ import 'package:cake_wallet/core/generate_wallet_password.dart'; import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; +import 'package:cake_wallet/utils/exception_handler.dart'; +import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class WalletLoadingService { - WalletLoadingService( - this.sharedPreferences, this.keyService, this.walletServiceFactory); + WalletLoadingService(this.sharedPreferences, this.keyService, this.walletServiceFactory); final SharedPreferences sharedPreferences; final KeyService keyService; final WalletService Function(WalletType type) walletServiceFactory; - Future renameWallet( - WalletType type, String name, String newName) async { + Future renameWallet(WalletType type, String name, String newName) async { final walletService = walletServiceFactory.call(type); final password = await keyService.getWalletPassword(walletName: name); // Save the current wallet's password to the new wallet name's key - await keyService.saveWalletPassword( - walletName: newName, password: password); + await keyService.saveWalletPassword(walletName: newName, password: password); // Delete previous wallet name from keyService to keep only new wallet's name // otherwise keeps duplicate (old and new names) await keyService.deleteWalletPassword(walletName: name); @@ -38,15 +39,43 @@ class WalletLoadingService { } Future load(WalletType type, String name) async { - final walletService = walletServiceFactory.call(type); - final password = await keyService.getWalletPassword(walletName: name); - final wallet = await walletService.openWallet(name, password); + try { + final walletService = walletServiceFactory.call(type); + final password = await keyService.getWalletPassword(walletName: name); + final wallet = await walletService.openWallet(name, password); - if (type == WalletType.monero) { - await updateMoneroWalletPassword(wallet); + if (type == WalletType.monero) { + await updateMoneroWalletPassword(wallet); + } + + return wallet; + } catch (error, stack) { + ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack)); + + // try opening another wallet that is not corrupted to give user access to the app + final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); + + for (var walletInfo in walletInfoSource.values) { + try { + final walletService = walletServiceFactory.call(walletInfo.type); + final password = await keyService.getWalletPassword(walletName: walletInfo.name); + final wallet = await walletService.openWallet(walletInfo.name, password); + + if (walletInfo.type == WalletType.monero) { + await updateMoneroWalletPassword(wallet); + } + + await sharedPreferences.setString(PreferencesKey.currentWalletName, wallet.name); + await sharedPreferences.setInt( + PreferencesKey.currentWalletType, serializeToInt(wallet.type)); + + return wallet; + } catch (_) {} + } + + // if all user's wallets are corrupted throw exception + throw error; } - - return wallet; } Future updateMoneroWalletPassword(WalletBase wallet) async { @@ -61,11 +90,9 @@ class WalletLoadingService { // Save new generated password with backup key for case where // wallet will change password, but it will fail to update in secure storage final bakWalletName = '#__${wallet.name}_bak__#'; - await keyService.saveWalletPassword( - walletName: bakWalletName, password: password); + await keyService.saveWalletPassword(walletName: bakWalletName, password: password); await wallet.changePassword(password); - await keyService.saveWalletPassword( - walletName: wallet.name, password: password); + await keyService.saveWalletPassword(walletName: wallet.name, password: password); isPasswordUpdated = true; await sharedPreferences.setBool(key, isPasswordUpdated); } diff --git a/lib/entities/biometric_auth.dart b/lib/entities/biometric_auth.dart index 463a22775..7fb62c661 100644 --- a/lib/entities/biometric_auth.dart +++ b/lib/entities/biometric_auth.dart @@ -25,4 +25,4 @@ class BiometricAuth { return canAuthenticate; } -} +} \ No newline at end of file diff --git a/lib/entities/calculate_fiat_amount.dart b/lib/entities/calculate_fiat_amount.dart index 689ada31b..b0b38eacb 100644 --- a/lib/entities/calculate_fiat_amount.dart +++ b/lib/entities/calculate_fiat_amount.dart @@ -3,6 +3,8 @@ String calculateFiatAmount({double? price, String? cryptoAmount}) { return '0.00'; } + cryptoAmount = cryptoAmount.replaceAll(',', '.'); + final _amount = double.parse(cryptoAmount); final _result = price * _amount; final result = _result < 0 ? _result * -1 : _result; diff --git a/lib/entities/provider_types.dart b/lib/entities/provider_types.dart index 701781cc2..ca168a299 100644 --- a/lib/entities/provider_types.dart +++ b/lib/entities/provider_types.dart @@ -22,7 +22,7 @@ extension ProviderTypeName on ProviderType { case ProviderType.robinhood: return 'Robinhood Connect'; case ProviderType.dfx: - return 'DFX Connect'; + return 'DFX.swiss'; case ProviderType.onramper: return 'Onramper'; case ProviderType.moonpay: diff --git a/lib/ethereum/cw_ethereum.dart b/lib/ethereum/cw_ethereum.dart index 13fe3aafd..61d5b6ae3 100644 --- a/lib/ethereum/cw_ethereum.dart +++ b/lib/ethereum/cw_ethereum.dart @@ -142,8 +142,10 @@ class CWEthereum extends Ethereum { } wallet as EthereumWallet; - return wallet.erc20Currencies - .firstWhere((element) => transaction.tokenSymbol == element.symbol); + + return wallet.erc20Currencies.firstWhere( + (element) => transaction.tokenSymbol == element.symbol, + ); } @override diff --git a/lib/polygon/cw_polygon.dart b/lib/polygon/cw_polygon.dart index 9f0f9a1bf..5baf4fbbc 100644 --- a/lib/polygon/cw_polygon.dart +++ b/lib/polygon/cw_polygon.dart @@ -140,8 +140,10 @@ class CWPolygon extends Polygon { } wallet as PolygonWallet; + return wallet.erc20Currencies.firstWhere( - (element) => transaction.tokenSymbol.toLowerCase() == element.symbol.toLowerCase()); + (element) => transaction.tokenSymbol.toLowerCase() == element.symbol.toLowerCase(), + ); } @override diff --git a/lib/solana/cw_solana.dart b/lib/solana/cw_solana.dart index 6f4b17309..af66cf3e5 100644 --- a/lib/solana/cw_solana.dart +++ b/lib/solana/cw_solana.dart @@ -110,8 +110,10 @@ class CWSolana extends Solana { } wallet as SolanaWallet; - return wallet.splTokenCurrencies - .firstWhere((element) => transaction.tokenSymbol == element.symbol); + + return wallet.splTokenCurrencies.firstWhere( + (element) => transaction.tokenSymbol == element.symbol, + ); } @override diff --git a/lib/src/screens/nodes/widgets/node_form.dart b/lib/src/screens/nodes/widgets/node_form.dart index e8c4b0ab3..74daa41cc 100644 --- a/lib/src/screens/nodes/widgets/node_form.dart +++ b/lib/src/screens/nodes/widgets/node_form.dart @@ -96,18 +96,20 @@ class NodeForm extends StatelessWidget { ], ), SizedBox(height: 10.0), - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _pathController, - hintText: "/path", - validator: NodePathValidator(), - ), - ) - ], - ), - SizedBox(height: 10.0), + if (nodeViewModel.hasPathSupport) ...[ + Row( + children: [ + Expanded( + child: BaseTextFormField( + controller: _pathController, + hintText: "/path", + validator: NodePathValidator(), + ), + ) + ], + ), + SizedBox(height: 10.0), + ], Row( children: [ Expanded( diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 970bb31f2..93fadea72 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -100,7 +100,7 @@ class SendPage extends BasePage { AppBarStyle get appBarStyle => AppBarStyle.transparent; double _sendCardHeight(BuildContext context) { - double initialHeight = 450; + double initialHeight = 480; if (sendViewModel.hasCoinControl) { initialHeight += 35; } diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index 7c2bfedd0..c9ae5182a 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -675,6 +675,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin( @@ -689,6 +690,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin extends StandardListRow { this.isGridView = false, this.matchingCriteria, this.customValue, + this.maxValue, this.customItemIndex, this.onItemSelected}) : super( @@ -34,6 +35,7 @@ class SettingsPriorityPickerCell extends StandardListRow { displayItem: (ItemType item) => displayItem!(item, sliderValue.round()), selectedAtIndex: selectedAtIndex, customItemIndex: customItemIndex, + maxValue: maxValue, headerEnabled: false, closeOnItemSelected: false, mainAxisAlignment: MainAxisAlignment.center, @@ -61,6 +63,7 @@ class SettingsPriorityPickerCell extends StandardListRow { final bool isGridView; final bool Function(ItemType, String)? matchingCriteria; double? customValue; + double? maxValue; int? customItemIndex; @override diff --git a/lib/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart b/lib/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart index 8f722ee7e..7615065d7 100644 --- a/lib/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart +++ b/lib/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart @@ -10,6 +10,7 @@ class StandardPickerListItem extends TransactionDetailsListItem { required this.onItemSelected, required this.selectedIdx, required this.customItemIndex, + this.maxValue, required this.customValue}) : super(title: title, value: value); @@ -18,6 +19,7 @@ class StandardPickerListItem extends TransactionDetailsListItem { final Function(double) onSliderChanged; final Function(T) onItemSelected; final int selectedIdx; + final double? maxValue; final int customItemIndex; double customValue; } diff --git a/lib/src/screens/transaction_details/rbf_details_page.dart b/lib/src/screens/transaction_details/rbf_details_page.dart index 875e0a4ef..3faec48a8 100644 --- a/lib/src/screens/transaction_details/rbf_details_page.dart +++ b/lib/src/screens/transaction_details/rbf_details_page.dart @@ -74,6 +74,7 @@ class RBFDetailsPage extends BasePage { selectedIdx: item.selectedIdx, customItemIndex: item.customItemIndex, customValue: item.customValue, + maxValue: item.maxValue, ); } diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart index d87b5721e..b744d1db0 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -27,14 +27,21 @@ class Picker extends StatefulWidget { this.headerEnabled = true, this.closeOnItemSelected = true, this.sliderValue, + this.minValue, + this.maxValue, this.customItemIndex, this.isWrapped = true, this.borderColor, this.onSliderChanged, this.matchingCriteria, - }) : assert(hintText == null || - matchingCriteria != - null); // make sure that if the search field is enabled then there is a searching criteria provided + }) : assert(hintText == null || matchingCriteria != null) { + // make sure that if the search field is enabled then there is a searching criteria provided + if (sliderValue != null && maxValue != null) { + if (sliderValue! > maxValue!) { + sliderValue = maxValue; + } + } + } final int selectedAtIndex; final List items; @@ -49,12 +56,14 @@ class Picker extends StatefulWidget { final String? hintText; final bool headerEnabled; final bool closeOnItemSelected; - final double? sliderValue; + double? sliderValue; + final double? minValue; final int? customItemIndex; final bool isWrapped; final Color? borderColor; final Function(double)? onSliderChanged; final bool Function(Item, String)? matchingCriteria; + final double? maxValue; @override _PickerState createState() => _PickerState(items, images, onItemSelected); @@ -138,7 +147,7 @@ class _PickerState extends State> { containerHeight = height * 0.75; } - final content = Column ( + final content = Column( children: [ if (widget.title?.isNotEmpty ?? false) Container( @@ -211,8 +220,9 @@ class _PickerState extends State> { fontWeight: FontWeight.w500, fontFamily: 'Lato', decoration: TextDecoration.none, - color: - Theme.of(context).extension()!.titleColor, + color: Theme.of(context) + .extension()! + .titleColor, ), ), ) @@ -491,8 +501,8 @@ class _PickerState extends State> { child: Slider( value: widget.sliderValue ?? 1, onChanged: isActivated ? widget.onSliderChanged : null, - min: 1, - max: 100, + min: widget.minValue ?? 1, + max: widget.maxValue ?? 100, divisions: 100, ), ), diff --git a/lib/src/widgets/standard_picker_list.dart b/lib/src/widgets/standard_picker_list.dart index eb1d16900..ea8b07097 100644 --- a/lib/src/widgets/standard_picker_list.dart +++ b/lib/src/widgets/standard_picker_list.dart @@ -15,6 +15,7 @@ class StandardPickerList extends StatefulWidget { required this.selectedIdx, required this.customItemIndex, required this.customValue, + this.maxValue, }) : super(key: key); final String title; @@ -26,6 +27,7 @@ class StandardPickerList extends StatefulWidget { final String value; final int selectedIdx; final double customValue; + final double? maxValue; @override _StandardPickerListState createState() => _StandardPickerListState(); @@ -59,6 +61,7 @@ class _StandardPickerListState extends State> { displayItem: adaptedDisplayItem, selectedAtIndex: selectedIdx, customItemIndex: widget.customItemIndex, + maxValue: widget.maxValue, headerEnabled: false, closeOnItemSelected: false, mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/src/widgets/validable_annotated_editable_text.dart b/lib/src/widgets/validable_annotated_editable_text.dart index 7ea928d8a..134eb16a8 100644 --- a/lib/src/widgets/validable_annotated_editable_text.dart +++ b/lib/src/widgets/validable_annotated_editable_text.dart @@ -173,4 +173,4 @@ class ValidatableAnnotatedEditableTextState extends EditableTextState { return TextSpan(style: widget.style, text: text); } -} +} \ No newline at end of file diff --git a/lib/view_model/auth_view_model.dart b/lib/view_model/auth_view_model.dart index 0e6590845..cab2494ea 100644 --- a/lib/view_model/auth_view_model.dart +++ b/lib/view_model/auth_view_model.dart @@ -124,4 +124,4 @@ abstract class AuthViewModelBase with Store { _authService.saveLastAuthTime(); } } -} +} \ No newline at end of file diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index eba347ac4..4e5902faa 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -3,8 +3,20 @@ import 'dart:collection'; import 'dart:convert'; import 'package:bitcoin_base/bitcoin_base.dart'; -import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; +import 'package:cake_wallet/core/create_trade_result.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/sync_status.dart'; +import 'package:cw_core/transaction_priority.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:hive/hive.dart'; +import 'package:http/http.dart' as http; +import 'package:intl/intl.dart'; +import 'package:mobx/mobx.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; 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'; @@ -33,14 +45,6 @@ import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/templates/exchange_template_store.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; -import 'package:cw_core/crypto_currency.dart'; -import 'package:cw_core/sync_status.dart'; -import 'package:cw_core/transaction_priority.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:hive/hive.dart'; -import 'package:intl/intl.dart'; -import 'package:mobx/mobx.dart'; -import 'package:shared_preferences/shared_preferences.dart'; part 'exchange_view_model.g.dart'; @@ -516,10 +520,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with trade.walletId = wallet.id; trade.fromWalletAddress = wallet.walletAddresses.address; - if (!isCanCreateTrade(trade)) { + final canCreateTrade = await isCanCreateTrade(trade); + if (!canCreateTrade.result) { tradeState = TradeIsCreatedFailure( - title: S.current.trade_not_created, - error: S.current.thorchain_taproot_address_not_supported); + title: S.current.trade_not_created, + error: canCreateTrade.errorMessage ?? '', + ); return; } @@ -776,16 +782,100 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with int get receiveMaxDigits => receiveCurrency.decimals; - bool isCanCreateTrade(Trade trade) { + Future isCanCreateTrade(Trade trade) async { if (trade.provider == ExchangeProviderDescription.thorChain) { final payoutAddress = trade.payoutAddress ?? ''; final fromWalletAddress = trade.fromWalletAddress ?? ''; final tapRootPattern = RegExp(P2trAddress.regex.pattern); if (tapRootPattern.hasMatch(payoutAddress) || tapRootPattern.hasMatch(fromWalletAddress)) { - return false; + return CreateTradeResult( + result: false, + errorMessage: S.current.thorchain_taproot_address_not_supported, + ); + } + + final currenciesToCheckPattern = RegExp('0x[0-9a-zA-Z]'); + + // Perform checks for payOutAddress + final isPayOutAddressAccordingToPattern = currenciesToCheckPattern.hasMatch(payoutAddress); + + if (isPayOutAddressAccordingToPattern) { + final isPayOutAddressEOA = await _isExternallyOwnedAccountAddress(payoutAddress); + + return CreateTradeResult( + result: isPayOutAddressEOA, + errorMessage: + !isPayOutAddressEOA ? S.current.thorchain_contract_address_not_supported : null, + ); + } + + // Perform checks for fromWalletAddress + final isFromWalletAddressAddressAccordingToPattern = + currenciesToCheckPattern.hasMatch(fromWalletAddress); + + if (isFromWalletAddressAddressAccordingToPattern) { + final isFromWalletAddressEOA = await _isExternallyOwnedAccountAddress(fromWalletAddress); + + return CreateTradeResult( + result: isFromWalletAddressEOA, + errorMessage: + !isFromWalletAddressEOA ? S.current.thorchain_contract_address_not_supported : null, + ); } } - return true; + return CreateTradeResult(result: true); + } + + String _normalizeReceiveCurrency(CryptoCurrency receiveCurrency) { + switch (receiveCurrency) { + case CryptoCurrency.eth: + return 'eth'; + case CryptoCurrency.maticpoly: + return 'polygon'; + default: + return receiveCurrency.tag ?? ''; + } + } + + Future _isExternallyOwnedAccountAddress(String receivingAddress) async { + final normalizedReceiveCurrency = _normalizeReceiveCurrency(receiveCurrency); + + final isEOAAddress = !(await _isContractAddress(normalizedReceiveCurrency, receivingAddress)); + return isEOAAddress; + } + + Future _isContractAddress(String chainName, String contractAddress) async { + final httpClient = http.Client(); + + final uri = Uri.https( + 'deep-index.moralis.io', + '/api/v2.2/erc20/metadata', + { + "chain": chainName, + "addresses": contractAddress, + }, + ); + + try { + final response = await httpClient.get( + uri, + headers: { + "Accept": "application/json", + "X-API-Key": secrets.moralisApiKey, + }, + ); + + final decodedResponse = jsonDecode(response.body)[0] as Map; + + final name = decodedResponse['name'] as String?; + + bool isContractAddress = name!.isNotEmpty; + + return isContractAddress; + } catch (e) { + print(e); + return false; + } } } diff --git a/lib/view_model/node_list/node_create_or_edit_view_model.dart b/lib/view_model/node_list/node_create_or_edit_view_model.dart index 283a32cbf..7fe3d1c98 100644 --- a/lib/view_model/node_list/node_create_or_edit_view_model.dart +++ b/lib/view_model/node_list/node_create_or_edit_view_model.dart @@ -69,6 +69,24 @@ abstract class NodeCreateOrEditViewModelBase with Store { bool get hasTestnetSupport => _walletType == WalletType.bitcoin; + bool get hasPathSupport { + switch (_walletType) { + case WalletType.ethereum: + case WalletType.polygon: + case WalletType.solana: + case WalletType.banano: + case WalletType.nano: + return true; + case WalletType.none: + case WalletType.monero: + case WalletType.haven: + case WalletType.litecoin: + case WalletType.bitcoinCash: + case WalletType.bitcoin: + return false; + } + } + String get uri { var uri = address; @@ -217,7 +235,6 @@ abstract class NodeCreateOrEditViewModelBase with Store { final port = uri.port.toString(); final path = uri.path; - setAddress(ipAddress); setPath(path); setPassword(rpcPassword); diff --git a/lib/view_model/send/output.dart b/lib/view_model/send/output.dart index 6bb3fbb31..07d98ff32 100644 --- a/lib/view_model/send/output.dart +++ b/lib/view_model/send/output.dart @@ -126,8 +126,8 @@ abstract class OutputBase with Store { if (_wallet.type == WalletType.bitcoin) { if (_settingsStore.priority[_wallet.type] == bitcoin!.getBitcoinTransactionPriorityCustom()) { - fee = bitcoin!.getFeeAmountWithFeeRate( - _settingsStore.customBitcoinFeeRate, formattedCryptoAmount, 1, 1); + fee = bitcoin!.getEstimatedFeeWithFeeRate(_wallet, + _settingsStore.customBitcoinFeeRate,formattedCryptoAmount); } return bitcoin!.formatterBitcoinAmountToDouble(amount: fee); diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 038301db4..cabb723e1 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -166,6 +166,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor return null; } + int? get maxCustomFeeRate { + if (wallet.type == WalletType.bitcoin) { + return bitcoin!.getMaxCustomFeeRate(wallet); + } + return null; + } + @computed int get customBitcoinFeeRate => _settingsStore.customBitcoinFeeRate; @@ -324,14 +331,16 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor Future createTransaction({ExchangeProvider? provider}) async { try { state = IsExecutingState(); + pendingTransaction = await wallet.createTransaction(_credentials()); if (provider is ThorChainExchangeProvider) { final outputCount = pendingTransaction?.outputCount ?? 0; if (outputCount > 10) { - throw Exception("ThorChain does not support more than 10 outputs"); + throw Exception("THORChain does not support more than 10 outputs"); } + if (_hasTaprootInput(pendingTransaction)) { - throw Exception("ThorChain does not support Taproot addresses"); + throw Exception("THORChain does not support Taproot addresses"); } } state = ExecutedSuccessfullyState(); @@ -553,7 +562,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor return S.current.tx_no_dust_exception; } if (error is TransactionCommitFailed) { - return S.current.tx_commit_failed; + return "${S.current.tx_commit_failed}${error.errorMessage != null ? "\n\n${error.errorMessage}" : ""}"; } if (error is TransactionCommitFailedDustChange) { return S.current.tx_rejected_dust_change; diff --git a/lib/view_model/settings/other_settings_view_model.dart b/lib/view_model/settings/other_settings_view_model.dart index cf410a1a9..0493acf81 100644 --- a/lib/view_model/settings/other_settings_view_model.dart +++ b/lib/view_model/settings/other_settings_view_model.dart @@ -140,6 +140,13 @@ abstract class OtherSettingsViewModelBase with Store { return customItem != null ? priorities.indexOf(customItem) : null; } + int? get maxCustomFeeRate { + if (_wallet.type == WalletType.bitcoin) { + return bitcoin!.getMaxCustomFeeRate(_wallet); + } + return null; + } + @action ProviderType onBuyProviderTypeSelected(ProviderType buyProviderType) => _settingsStore.defaultBuyProviders[walletType] = buyProviderType; diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index fd6d3ef6e..faa49dfc4 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -348,12 +348,14 @@ abstract class TransactionDetailsViewModelBase with Store { final customItem = priorities.firstWhereOrNull( (element) => element == sendViewModel.bitcoinTransactionPriorityCustom); final customItemIndex = customItem != null ? priorities.indexOf(customItem) : null; + final maxCustomFeeRate = sendViewModel.maxCustomFeeRate?.toDouble(); RBFListItems.add(StandardPickerListItem( title: S.current.estimated_new_fee, value: bitcoin!.formatterBitcoinAmountToString(amount: newFee) + ' ${walletTypeToCryptoCurrency(wallet.type)}', items: priorityForWalletType(wallet.type), customValue: settingsStore.customBitcoinFeeRate.toDouble(), + maxValue: maxCustomFeeRate, selectedIdx: selectedItem, customItemIndex: customItemIndex ?? 0, displayItem: (dynamic priority, double sliderValue) => @@ -388,16 +390,12 @@ abstract class TransactionDetailsViewModelBase with Store { String setNewFee({double? value, required TransactionPriority priority}) { newFee = priority == bitcoin!.getBitcoinTransactionPriorityCustom() && value != null - ? bitcoin!.getFeeAmountWithFeeRate( - wallet, - value.round(), - transactionInfo.inputAddresses?.length ?? 1, - transactionInfo.outputAddresses?.length ?? 1) + ? bitcoin!.getEstimatedFeeWithFeeRate(wallet, value.round(), transactionInfo.amount) : bitcoin!.getFeeAmountForPriority( - wallet, - priority, - transactionInfo.inputAddresses?.length ?? 1, - transactionInfo.outputAddresses?.length ?? 1); + wallet, + priority, + transactionInfo.inputAddresses?.length ?? 1, + transactionInfo.outputAddresses?.length ?? 1); return bitcoin!.formatterBitcoinAmountToString(amount: newFee); } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 4a24d0ece..52b45899a 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -667,6 +667,7 @@ "template_name": "اسم القالب", "third_intro_content": "يعيش Yats خارج Cake Wallet أيضًا. يمكن استبدال أي عنوان محفظة على وجه الأرض بـ Yat!", "third_intro_title": "يتماشي Yat بلطف مع الآخرين", + "thorchain_contract_address_not_supported": "لا يدعم Thorchain الإرسال إلى عنوان العقد", "thorchain_taproot_address_not_supported": "لا يدعم مزود Thorchain عناوين Taproot. يرجى تغيير العنوان أو تحديد مزود مختلف.", "time": "${minutes}د ${seconds}س", "tip": "بقشيش:", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index c6bc2b7fd..f2c6b881d 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -667,6 +667,7 @@ "template_name": "Име на шаблон", "third_intro_content": "Yats също живее извън Cake Wallet. Всеки адрес на портфейл може да бъде заменен с Yat!", "third_intro_title": "Yat добре се сработва с други", + "thorchain_contract_address_not_supported": "Thorchain не подкрепя изпращането до адрес на договор", "thorchain_taproot_address_not_supported": "Доставчикът на Thorchain не поддържа адреси на TapRoot. Моля, променете адреса или изберете друг доставчик.", "time": "${minutes} мин ${seconds} сек", "tip": "Tip:", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 865e1d250..efaa07792 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -667,6 +667,7 @@ "template_name": "Název šablony", "third_intro_content": "Yat existuje i mimo Cake Wallet. Jakákoliv adresa peněženky na světě může být nahrazena Yatem!", "third_intro_title": "Yat dobře spolupracuje s ostatními", + "thorchain_contract_address_not_supported": "Thorchain nepodporuje odeslání na adresu smlouvy", "thorchain_taproot_address_not_supported": "Poskytovatel Thorchain nepodporuje adresy Taproot. Změňte adresu nebo vyberte jiného poskytovatele.", "time": "${minutes}m ${seconds}s", "tip": "Spropitné:", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index b6076c9fc..5ef38b1af 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -50,7 +50,7 @@ "anonpay_description": "Generieren Sie ${type}. Der Empfänger kann ${method} mit jeder unterstützten Kryptowährung verwenden, und Sie erhalten Geld in dieser Wallet.", "apk_update": "APK-Update", "approve": "Genehmigen", - "arrive_in_this_address": "${currency} ${tag}wird an dieser Adresse ankommen", + "arrive_in_this_address": "${currency} ${tag} wird an dieser Adresse ankommen", "ascending": "Aufsteigend", "ask_each_time": "Jedes Mal fragen", "auth_store_ban_timeout": "ban_timeout", @@ -87,7 +87,7 @@ "buy_provider_unavailable": "Anbieter derzeit nicht verfügbar.", "buy_with": "Kaufen mit", "by_cake_pay": "von Cake Pay", - "cake_2fa_preset": "Kuchen 2FA-Voreinstellung", + "cake_2fa_preset": "Cake 2FA-Voreinstellung", "cake_dark_theme": "Cake Dark Thema", "cake_pay_account_note": "Melden Sie sich nur mit einer E-Mail-Adresse an, um Karten anzuzeigen und zu kaufen. Einige sind sogar mit Rabatt erhältlich!", "cake_pay_learn_more": "Kaufen und lösen Sie Geschenkkarten sofort in der App ein!\nWischen Sie von links nach rechts, um mehr zu erfahren.", @@ -120,7 +120,7 @@ "change_wallet_alert_title": "Aktuelle Wallet ändern", "choose_account": "Konto auswählen", "choose_address": "\n\nBitte wählen Sie die Adresse:", - "choose_derivation": "Wählen Sie Brieftaschenableitung", + "choose_derivation": "Wählen Sie Wallet-Ableitung", "choose_from_available_options": "Wähle aus verfügbaren Optionen:", "choose_one": "Wähle ein", "choose_relay": "Bitte wählen Sie ein zu verwendendes Relais aus", @@ -199,7 +199,7 @@ "disable_fiat": "Fiat deaktivieren", "disable_sell": "Verkaufsaktion deaktivieren", "disableBatteryOptimization": "Batterieoptimierung deaktivieren", - "disableBatteryOptimizationDescription": "Möchten Sie die Batterieoptimierung deaktivieren, um die Hintergrundsynchronisierung freier und reibungsloser zu gestalten?", + "disableBatteryOptimizationDescription": "Möchten Sie die Batterieoptimierung deaktivieren, um die Hintergrundsynchronisierung reibungsloser zu gestalten?", "disabled": "Deaktiviert", "discount": "${value} % sparen", "display_settings": "Anzeigeeinstellungen", @@ -460,8 +460,8 @@ "reconnect_alert_text": "Sind Sie sicher, dass Sie sich neu verbinden möchten?", "reconnection": "Neu verbinden", "red_dark_theme": "Red Dark Thema", - "red_light_theme": "Rotlichtthema", - "redeemed": "Versilbert", + "red_light_theme": "Red Light Thema", + "redeemed": "Eingelöst", "refund_address": "Rückerstattungsadresse", "reject": "Ablehnen", "remaining": "Rest", @@ -533,7 +533,7 @@ "seed_alert_title": "Achtung", "seed_alert_yes": "Ja, habe ich", "seed_choose": "Seed-Sprache auswählen", - "seed_hex_form": "Brieftaschensamen (Sechskantform)", + "seed_hex_form": "Seed (Hexformat)", "seed_key": "Seed-Schlüssel", "seed_language": "Seed-Sprache", "seed_language_chinese": "Chinesisch", @@ -585,7 +585,7 @@ "send_your_wallet": "Ihre Wallet", "sending": "Senden", "sent": "Versendet", - "service_health_disabled": "Service Health Bulletin ist behindert", + "service_health_disabled": "Service Health Bulletin ist deaktiviert", "service_health_disabled_message": "Dies ist die Seite \"Service Health Bulletin\", können Sie diese Seite unter Einstellungen -> Privatsphäre aktivieren", "settings": "Einstellungen", "settings_all": "ALLE", @@ -668,6 +668,7 @@ "template_name": "Vorlagenname", "third_intro_content": "Yats leben auch außerhalb von Cake Wallet. Jede Wallet-Adresse auf der Welt kann durch ein Yat ersetzt werden!", "third_intro_title": "Yat spielt gut mit anderen", + "thorchain_contract_address_not_supported": "Thorchain unterstützt das Senden an eine Vertragsadresse nicht", "thorchain_taproot_address_not_supported": "Der Thorchain -Anbieter unterstützt keine Taproot -Adressen. Bitte ändern Sie die Adresse oder wählen Sie einen anderen Anbieter aus.", "time": "${minutes}m ${seconds}s", "tip": "Hinweis:", @@ -829,4 +830,4 @@ "you_will_get": "Konvertieren zu", "you_will_send": "Konvertieren von", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 3c3773ab3..e2b3df754 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -667,6 +667,7 @@ "template_name": "Template Name", "third_intro_content": "Yats live outside of Cake Wallet, too. Any wallet address on earth can be replaced with a Yat!", "third_intro_title": "Yat plays nicely with others", + "thorchain_contract_address_not_supported": "THORChain does not support sending to a contract address", "thorchain_taproot_address_not_supported": "The ThorChain provider does not support Taproot addresses. Please change the address or select a different provider.", "time": "${minutes}m ${seconds}s", "tip": "Tip:", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index e9519c6ca..df7b827c9 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -668,6 +668,7 @@ "template_name": "Nombre de la plantilla", "third_intro_content": "Los Yats también viven fuera de Cake Wallet. Cualquier dirección de billetera en la tierra se puede reemplazar con un Yat!", "third_intro_title": "Yat juega muy bien con otras", + "thorchain_contract_address_not_supported": "Thorchain no admite enviar a una dirección de contrato", "thorchain_taproot_address_not_supported": "El proveedor de Thorchain no admite las direcciones de Taproot. Cambie la dirección o seleccione un proveedor diferente.", "time": "${minutes}m ${seconds}s", "tip": "Consejo:", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 4a9f369f7..a8d316feb 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -667,6 +667,7 @@ "template_name": "Nom du modèle", "third_intro_content": "Les Yats existent aussi en dehors de Cake Wallet. Toute adresse sur terre peut être remplacée par un Yat !", "third_intro_title": "Yat est universel", + "thorchain_contract_address_not_supported": "Thorchain ne prend pas en charge l'envoi à une adresse de contrat", "thorchain_taproot_address_not_supported": "Le fournisseur de Thorchain ne prend pas en charge les adresses de tapoot. Veuillez modifier l'adresse ou sélectionner un autre fournisseur.", "time": "${minutes}m ${seconds}s", "tip": "Pourboire :", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index fc5256f85..146145592 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -669,6 +669,7 @@ "template_name": "Sunan Samfura", "third_intro_content": "Yats suna zaune a wajen Kek Wallet, kuma. Ana iya maye gurbin kowane adireshin walat a duniya da Yat!", "third_intro_title": "Yat yana wasa da kyau tare da wasu", + "thorchain_contract_address_not_supported": "Thorchain baya goyon bayan aika zuwa adireshin kwangila", "thorchain_taproot_address_not_supported": "Mai ba da tallafi na ThorChain baya goyan bayan adreshin taproot. Da fatan za a canza adireshin ko zaɓi mai bayarwa daban.", "time": "${minutes}m ${seconds}s", "tip": "Tukwici:", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 32ee85cec..e9f52d186 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -669,6 +669,7 @@ "template_name": "टेम्पलेट नाम", "third_intro_content": "Yats Cake Wallet के बाहर भी रहता है। धरती पर किसी भी वॉलेट पते को Yat से बदला जा सकता है!", "third_intro_title": "Yat दूसरों के साथ अच्छा खेलता है", + "thorchain_contract_address_not_supported": "थोरचेन एक अनुबंध पते पर भेजने का समर्थन नहीं करता है", "thorchain_taproot_address_not_supported": "थोरचेन प्रदाता टैपरोट पते का समर्थन नहीं करता है। कृपया पता बदलें या एक अलग प्रदाता का चयन करें।", "time": "${minutes}m ${seconds}s", "tip": "टिप:", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 02d5f234d..784e70aa5 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -667,6 +667,7 @@ "template_name": "Naziv predloška", "third_intro_content": "Yats žive i izvan Cake Wallet -a. Bilo koja adresa novčanika na svijetu može se zamijeniti Yat!", "third_intro_title": "Yat se lijepo igra s drugima", + "thorchain_contract_address_not_supported": "Thorchain ne podržava slanje na adresu ugovora", "thorchain_taproot_address_not_supported": "Thorchain pružatelj ne podržava Taproot adrese. Promijenite adresu ili odaberite drugog davatelja usluga.", "time": "${minutes}m ${seconds}s", "tip": "Savjet:", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index a9de6d0cf..525746c46 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -670,6 +670,7 @@ "template_name": "Nama Templat", "third_intro_content": "Yats hidup di luar Cake Wallet juga. Setiap alamat dompet di dunia dapat diganti dengan Yat!", "third_intro_title": "Yat bermain baik dengan yang lain", + "thorchain_contract_address_not_supported": "Thorchain tidak mendukung pengiriman ke alamat kontrak", "thorchain_taproot_address_not_supported": "Penyedia Thorchain tidak mendukung alamat Taproot. Harap ubah alamatnya atau pilih penyedia yang berbeda.", "time": "${minutes}m ${seconds}s", "tip": "Tip:", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 23f39b88f..2a175dadf 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -669,6 +669,7 @@ "template_name": "Nome modello", "third_intro_content": "Yat può funzionare anche fuori da Cake Wallet. Qualsiasi indirizzo di portafoglio sulla terra può essere sostituito con uno Yat!", "third_intro_title": "Yat gioca bene con gli altri", + "thorchain_contract_address_not_supported": "Thorchain non supporta l'invio a un indirizzo contrattuale", "thorchain_taproot_address_not_supported": "Il provider di Thorchain non supporta gli indirizzi di TapRoot. Si prega di modificare l'indirizzo o selezionare un fornitore diverso.", "time": "${minutes}m ${seconds}s", "tip": "Suggerimento:", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 79c65eed9..0b350f7aa 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -668,6 +668,7 @@ "template_name": "テンプレート名", "third_intro_content": "YatsはCakeWalletの外にも住んでいます。 地球上のどのウォレットアドレスもYatに置き換えることができます!", "third_intro_title": "Yatは他の人とうまく遊ぶ", + "thorchain_contract_address_not_supported": "Thorchainは、契約アドレスへの送信をサポートしていません", "thorchain_taproot_address_not_supported": "Thorchainプロバイダーは、TapRootアドレスをサポートしていません。アドレスを変更するか、別のプロバイダーを選択してください。", "time": "${minutes}m ${seconds}s", "tip": "ヒント: ", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 4d7e51622..1c0181b8a 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -668,6 +668,7 @@ "template_name": "템플릿 이름", "third_intro_content": "Yats는 Cake Wallet 밖에서도 살고 있습니다. 지구상의 모든 지갑 주소는 Yat!", "third_intro_title": "Yat는 다른 사람들과 잘 놉니다.", + "thorchain_contract_address_not_supported": "Thorchain은 계약 주소로 보내는 것을 지원하지 않습니다", "thorchain_taproot_address_not_supported": "Thorchain 제공 업체는 Taproot 주소를 지원하지 않습니다. 주소를 변경하거나 다른 공급자를 선택하십시오.", "time": "${minutes}m ${seconds}s", "tip": "팁:", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 9b44a268f..dc34b89d9 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -667,6 +667,7 @@ "template_name": "နမူနာပုံစံ", "third_intro_content": "Yats သည် Cake Wallet အပြင်ဘက်တွင် နေထိုင်ပါသည်။ ကမ္ဘာပေါ်ရှိ မည်သည့်ပိုက်ဆံအိတ်လိပ်စာကို Yat ဖြင့် အစားထိုးနိုင်ပါသည်။", "third_intro_title": "Yat သည် အခြားသူများနှင့် ကောင်းစွာကစားသည်။", + "thorchain_contract_address_not_supported": "Thorchain သည်စာချုပ်လိပ်စာသို့ပို့ခြင်းမပြုပါ", "thorchain_taproot_address_not_supported": "Thorchain Provider သည် Taproot လိပ်စာများကိုမထောက်ခံပါ။ ကျေးဇူးပြု. လိပ်စာကိုပြောင်းပါသို့မဟုတ်အခြားပံ့ပိုးပေးသူကိုရွေးချယ်ပါ။", "time": "${minutes}m ${seconds}s", "tip": "အကြံပြုချက်-", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index a937e5868..792215f56 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -667,6 +667,7 @@ "template_name": "Sjabloonnaam", "third_intro_content": "Yats wonen ook buiten Cake Wallet. Elk portemonnee-adres op aarde kan worden vervangen door een Yat!", "third_intro_title": "Yat speelt leuk met anderen", + "thorchain_contract_address_not_supported": "Thorchain ondersteunt het verzenden niet naar een contractadres", "thorchain_taproot_address_not_supported": "De Thorchain -provider ondersteunt geen Taprooot -adressen. Wijzig het adres of selecteer een andere provider.", "time": "${minutes}m ${seconds}s", "tip": "Tip:", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index e75a8fbe2..ca8aa0658 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -667,6 +667,7 @@ "template_name": "Nazwa szablonu", "third_intro_content": "Yats mieszkają również poza Cake Wallet. Każdy adres portfela na ziemi można zastąpić Yat!", "third_intro_title": "Yat ładnie bawi się z innymi", + "thorchain_contract_address_not_supported": "Thorchain nie wspiera wysyłania na adres umowy", "thorchain_taproot_address_not_supported": "Dostawca Thorchain nie obsługuje adresów TAPROOT. Zmień adres lub wybierz innego dostawcę.", "time": "${minutes}m ${seconds}s", "tip": "wskazówka:", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index dc8aa6f61..7357dcfd6 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -669,6 +669,7 @@ "template_name": "Nome do modelo", "third_intro_content": "Yats também mora fora da Cake Wallet. Qualquer endereço de carteira na Terra pode ser substituído por um Yat!", "third_intro_title": "Yat joga bem com os outros", + "thorchain_contract_address_not_supported": "Thorchain não suporta o envio para um endereço de contrato", "thorchain_taproot_address_not_supported": "O provedor de Thorchain não suporta endereços de raiz de Tap. Altere o endereço ou selecione um provedor diferente.", "time": "${minutes}m ${seconds}s", "tip": "Dica:", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 1f63e83ac..34f0409e3 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -668,6 +668,7 @@ "template_name": "Имя Шаблона", "third_intro_content": "Yat находятся за пределами Cake Wallet. Любой адрес кошелька на земле можно заменить на Yat!", "third_intro_title": "Yat хорошо взаимодействует с другими", + "thorchain_contract_address_not_supported": "Thorchain не поддерживает отправку на адрес контракта", "thorchain_taproot_address_not_supported": "Поставщик Thorchain не поддерживает адреса taproot. Пожалуйста, измените адрес или выберите другого поставщика.", "time": "${minutes}мин ${seconds}сек", "tip": "Совет:", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 9ac5b4524..d4cb55f94 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -667,6 +667,7 @@ "template_name": "ชื่อแม่แบบ", "third_intro_content": "Yat อาศัยอยู่นอก Cake Wallet ด้วย ที่อยู่กระเป๋าใดๆ ทั่วโลกสามารถแทนด้วย Yat ได้อีกด้วย!", "third_intro_title": "Yat ปฏิบัติตนอย่างดีกับผู้อื่น", + "thorchain_contract_address_not_supported": "Thorchain ไม่สนับสนุนการส่งไปยังที่อยู่สัญญา", "thorchain_taproot_address_not_supported": "ผู้ให้บริการ Thorchain ไม่รองรับที่อยู่ taproot โปรดเปลี่ยนที่อยู่หรือเลือกผู้ให้บริการอื่น", "time": "${minutes}m ${seconds}s", "tip": "เพิ่มค่าตอบแทน:", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 6aa4155ab..89d6ef59f 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -667,6 +667,7 @@ "template_name": "Pangalan ng Template", "third_intro_content": "Ang mga yats ay nakatira sa labas ng cake wallet, din. Ang anumang address ng pitaka sa mundo ay maaaring mapalitan ng isang yat!", "third_intro_title": "Si Yat ay mahusay na gumaganap sa iba", + "thorchain_contract_address_not_supported": "Hindi sinusuportahan ng Thorchain ang pagpapadala sa isang address ng kontrata", "thorchain_taproot_address_not_supported": "Ang Tagabigay ng Thorchain ay hindi sumusuporta sa mga address ng taproot. Mangyaring baguhin ang address o pumili ng ibang provider.", "time": "${minutes} m ${seconds} s", "tip": "Tip:", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index d60ea3049..2c866b51a 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -667,6 +667,7 @@ "template_name": "şablon adı", "third_intro_content": "Yat'lar Cake Wallet'ın dışında da çalışabilir. Dünya üzerindeki herhangi bir cüzdan adresi Yat ile değiştirilebilir!", "third_intro_title": "Yat diğerleriyle iyi çalışır", + "thorchain_contract_address_not_supported": "Thorchain bir sözleşme adresine göndermeyi desteklemiyor", "thorchain_taproot_address_not_supported": "Thorchain sağlayıcısı Taproot adreslerini desteklemiyor. Lütfen adresi değiştirin veya farklı bir sağlayıcı seçin.", "time": "${minutes}d ${seconds}s", "tip": "Bahşiş:", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 835e65652..6152daa22 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -668,6 +668,7 @@ "template_name": "Назва шаблону", "third_intro_content": "Yat знаходиться за межами Cake Wallet. Будь-яку адресу гаманця на землі можна замінити на Yat!", "third_intro_title": "Yat добре взаємодіє з іншими", + "thorchain_contract_address_not_supported": "Thorchain не підтримує надсилання на адресу контракту", "thorchain_taproot_address_not_supported": "Постачальник Thorchain не підтримує адреси Taproot. Будь ласка, змініть адресу або виберіть іншого постачальника.", "time": "${minutes}хв ${seconds}сек", "tip": "Порада:", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 7a0f1ef67..d0d3a8935 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -669,6 +669,7 @@ "template_name": "ٹیمپلیٹ کا نام", "third_intro_content": "Yats بھی Cake والیٹ سے باہر رہتے ہیں۔ زمین پر کسی بھی بٹوے کے پتے کو Yat سے تبدیل کیا جا سکتا ہے!", "third_intro_title": "Yat دوسروں کے ساتھ اچھی طرح کھیلتا ہے۔", + "thorchain_contract_address_not_supported": "تھورچین معاہدے کے پتے بھیجنے کی حمایت نہیں کرتا ہے", "thorchain_taproot_address_not_supported": "تھورچین فراہم کنندہ ٹیپروٹ پتے کی حمایت نہیں کرتا ہے۔ براہ کرم پتہ تبدیل کریں یا ایک مختلف فراہم کنندہ کو منتخب کریں۔", "time": "${minutes}m ${seconds}s", "tip": "ٹپ:", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 42bcb3435..ed1b3c502 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -668,6 +668,7 @@ "template_name": "Orukọ Awoṣe", "third_intro_content": "A sì lè lo Yats níta Cake Wallet. A lè rọ́pò Àdírẹ́sì kankan àpamọ́wọ́ fún Yat!", "third_intro_title": "Àlàáfíà ni Yat àti àwọn ìmíìn jọ wà", + "thorchain_contract_address_not_supported": "Thorchain ko ṣe atilẹyin fifiranṣẹ si adirẹsi adehun kan", "thorchain_taproot_address_not_supported": "Olupese Trockchain ko ṣe atilẹyin awọn adirẹsi Taproot. Jọwọ yi adirẹsi pada tabi yan olupese ti o yatọ.", "time": "${minutes}ìṣj ${seconds}ìṣs", "tip": "Owó àfikún:", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 66aa3abba..bf8062fcd 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -667,6 +667,7 @@ "template_name": "模板名称", "third_intro_content": "Yats 也住在 Cake Wallet 之外。 地球上任何一個錢包地址都可以用一個Yat來代替!", "third_intro_title": "Yat 和別人玩得很好", + "thorchain_contract_address_not_supported": "Thorchain不支持发送到合同地址", "thorchain_taproot_address_not_supported": "Thorchain提供商不支持Taproot地址。请更改地址或选择其他提供商。", "time": "${minutes}m ${seconds}s", "tip": "提示:", diff --git a/tool/configure.dart b/tool/configure.dart index 6ee84d63a..3b73bfe80 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -158,7 +158,8 @@ abstract class Bitcoin { Future canReplaceByFee(Object wallet, String transactionHash); Future isChangeSufficientForFee(Object wallet, String txId, String newFee); int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size}); - int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size}); + int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount, {int? outputsCount, int? size}); + int getMaxCustomFeeRate(Object wallet); } """; @@ -1057,7 +1058,8 @@ Future generatePubspec( final inputFile = File(pubspecOutputPath); final inputText = await inputFile.readAsString(); final inputLines = inputText.split('\n'); - final dependenciesIndex = inputLines.indexWhere((line) => line.toLowerCase().contains('dependencies:')); + final dependenciesIndex = + inputLines.indexWhere((line) => line.toLowerCase().contains('dependencies:')); var output = cwCore; if (hasMonero) {