Litcoin bitcoin cash fix (#1339)

* Make address to output script a single entry point Fix network type for bitcoin cash * Add MoonPay to sell polygon * Normalize currency for moonpay widget * Minor fix * fix: litecoin & bch address types * fix: remove print * fix: network decode location * fix: missing place additional network type * fix: wrong initial address page type * fix: initial address generation * fix: btc exchange sending all, bch without change addresses * Minor fixes * Update app versions [skip ci] --------- Co-authored-by: Rafael Saes <git@rafael.saes.dev>

Omar Hatem committed Mar 21, 2024 at 04:51 UTC 5a7a0e01a735849435b56930a3efa998805db657
30 files changed +497 -447
assets/text/Release_Notes.txt
-4
@@ -1,5 +1 @@
1 -Monero enhancements
2 -Bitcoin support different address types (Taproot, Segwit P2WPKH/P2WSH, Legacy)
3 -In-App live status page for the app services
4 -Add Exolix exchange provider
1 Bug fixes and enhancements
\ No newline at end of file
cw_bitcoin/lib/address_to_output_script.dart
+3
@@ -3,6 +3,9 @@ import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin;
3
4 List<int> addressToOutputScript(String address, bitcoin.BasedUtxoNetwork network) {
5 try {
6 + if (network == bitcoin.BitcoinCashNetwork.mainnet) {
7 + return bitcoin.BitcoinCashAddress(address).baseAddress.toScriptPubKey().toBytes();
8 + }
9 return bitcoin.addressToOutputScript(address: address, network: network);
10 } catch (err) {
11 print(err);
cw_bitcoin/lib/bitcoin_address_record.dart
+4 -11
@@ -1,5 +1,4 @@
1 import 'dart:convert';
2 -import 'package:bitbox/bitbox.dart' as bitbox;
2
3 import 'package:bitcoin_base/bitcoin_base.dart';
4 import 'package:cw_bitcoin/script_hash.dart' as sh;
@@ -20,10 +19,9 @@ class BitcoinAddressRecord {
19 _balance = balance,
20 _name = name,
21 _isUsed = isUsed,
23 - scriptHash =
24 - scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null);
22 + scriptHash = scriptHash ?? sh.scriptHash(address, network: network);
23
26 - factory BitcoinAddressRecord.fromJSON(String jsonSource, BasedUtxoNetwork? network) {
24 + factory BitcoinAddressRecord.fromJSON(String jsonSource, BasedUtxoNetwork network) {
25 final decoded = json.decode(jsonSource) as Map;
26
27 return BitcoinAddressRecord(
@@ -39,9 +37,7 @@ class BitcoinAddressRecord {
37 .firstWhere((type) => type.toString() == decoded['type'] as String)
38 : SegwitAddresType.p2wpkh,
39 scriptHash: decoded['scriptHash'] as String?,
42 - network: (decoded['network'] as String?) == null
43 - ? network
44 - : BasedUtxoNetwork.fromName(decoded['network'] as String),
40 + network: network,
41 );
42 }
43
@@ -56,7 +52,7 @@ class BitcoinAddressRecord {
52 String _name;
53 bool _isUsed;
54 String? scriptHash;
59 - BasedUtxoNetwork? network;
55 + BasedUtxoNetwork network;
56
57 int get txCount => _txCount;
58
@@ -76,8 +72,6 @@ class BitcoinAddressRecord {
72 @override
73 int get hashCode => address.hashCode;
74
79 - String get cashAddr => bitbox.Address.toCashAddress(address);
80 -
75 BitcoinAddressType type;
76
77 String updateScriptHash(BasedUtxoNetwork network) {
@@ -95,6 +89,5 @@ class BitcoinAddressRecord {
89 'balance': balance,
90 'type': type.toString(),
91 'scriptHash': scriptHash,
98 - 'network': network?.value,
92 });
93 }
cw_bitcoin/lib/bitcoin_wallet.dart
+5 -3
@@ -92,8 +92,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
92 required Box<UnspentCoinsInfo> unspentCoinsInfo,
93 required String password,
94 }) async {
95 - final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password,
96 - walletInfo.network != null ? BasedUtxoNetwork.fromName(walletInfo.network!) : null);
95 + final network = walletInfo.network != null
96 + ? BasedUtxoNetwork.fromName(walletInfo.network!)
97 + : BitcoinNetwork.mainnet;
98 + final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network);
99
100 return BitcoinWallet(
101 mnemonic: snp.mnemonic,
@@ -106,7 +108,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
108 initialRegularAddressIndex: snp.regularAddressIndex,
109 initialChangeAddressIndex: snp.changeAddressIndex,
110 addressPageType: snp.addressPageType,
109 - networkParam: snp.network,
111 + networkParam: network,
112 );
113 }
114 }
cw_bitcoin/lib/electrum_wallet.dart
+37 -19
@@ -75,11 +75,7 @@ abstract class ElectrumWalletBase
75 }
76 : {}),
77 this.unspentCoinsInfo = unspentCoinsInfo,
78 - this.network = networkType == bitcoin.bitcoin
79 - ? BitcoinNetwork.mainnet
80 - : networkType == litecoinNetwork
81 - ? LitecoinNetwork.mainnet
82 - : BitcoinNetwork.testnet,
78 + this.network = _getNetwork(networkType, currency),
79 this.isTestnet = networkType == bitcoin.testnet,
80 super(walletInfo) {
81 this.electrumClient = electrumClient ?? ElectrumClient();
@@ -192,12 +188,13 @@ abstract class ElectrumWalletBase
188 }
189 }
190
195 - Future<EstimatedTxResult> _estimateTxFeeAndInputsToUse(
191 + Future<EstimatedTxResult> estimateTxFeeAndInputsToUse(
192 int credentialsAmount,
193 bool sendAll,
194 List<BitcoinBaseAddress> outputAddresses,
195 List<BitcoinOutput> outputs,
200 - BitcoinTransactionCredentials transactionCredentials,
196 + int? feeRate,
197 + BitcoinTransactionPriority? priority,
198 {int? inputsCount}) async {
199 final utxos = <UtxoWithAddress>[];
200 List<ECPrivate> privateKeys = [];
@@ -212,7 +209,7 @@ abstract class ElectrumWalletBase
209 allInputsAmount += utx.value;
210 leftAmount = leftAmount - utx.value;
211
215 - final address = _addressTypeFromStr(utx.address, network);
212 + final address = addressTypeFromStr(utx.address, network);
213 final privkey = generateECPrivate(
214 hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
215 index: utx.bitcoinAddressRecord.index,
@@ -249,7 +246,7 @@ abstract class ElectrumWalletBase
246 if (!sendAll) {
247 if (changeValue > 0) {
248 final changeAddress = await walletAddresses.getChangeAddress();
252 - final address = _addressTypeFromStr(changeAddress, network);
249 + final address = addressTypeFromStr(changeAddress, network);
250 outputAddresses.add(address);
251 outputs.add(BitcoinOutput(address: address, value: BigInt.from(changeValue)));
252 }
@@ -258,9 +255,9 @@ abstract class ElectrumWalletBase
255 final estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize(
256 utxos: utxos, outputs: outputs, network: network);
257
261 - final fee = transactionCredentials.feeRate != null
262 - ? feeAmountWithFeeRate(transactionCredentials.feeRate!, 0, 0, size: estimatedSize)
263 - : feeAmountForPriority(transactionCredentials.priority!, 0, 0, size: estimatedSize);
258 + int fee = feeRate != null
259 + ? feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize)
260 + : feeAmountForPriority(priority!, 0, 0, size: estimatedSize);
261
262 if (fee == 0) {
263 throw BitcoinTransactionWrongBalanceException(currency);
@@ -297,8 +294,8 @@ abstract class ElectrumWalletBase
294 outputs.removeLast();
295 }
296
300 - return _estimateTxFeeAndInputsToUse(
301 - credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials,
297 + return estimateTxFeeAndInputsToUse(
298 + credentialsAmount, sendAll, outputAddresses, outputs, feeRate, priority,
299 inputsCount: utxos.length + 1);
300 }
301 }
@@ -319,7 +316,7 @@ abstract class ElectrumWalletBase
316
317 for (final out in transactionCredentials.outputs) {
318 final outputAddress = out.isParsedAddress ? out.extractedAddress! : out.address;
322 - final address = _addressTypeFromStr(outputAddress, network);
319 + final address = addressTypeFromStr(outputAddress, network);
320
321 outputAddresses.add(address);
322
@@ -344,8 +341,14 @@ abstract class ElectrumWalletBase
341 }
342 }
343
347 - final estimatedTx = await _estimateTxFeeAndInputsToUse(
348 - credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials);
344 + final estimatedTx = await estimateTxFeeAndInputsToUse(
345 + credentialsAmount,
346 + sendAll,
347 + outputAddresses,
348 + outputs,
349 + transactionCredentials.feeRate,
350 + transactionCredentials.priority,
351 + );
352
353 final txb = BitcoinTransactionBuilder(
354 utxos: estimatedTx.utxos,
@@ -391,7 +394,6 @@ abstract class ElectrumWalletBase
394 ? SegwitAddresType.p2wpkh.toString()
395 : walletInfo.addressPageType.toString(),
396 'balance': balance[currency]?.toJSON(),
394 - 'network_type': network == BitcoinNetwork.testnet ? 'testnet' : 'mainnet',
397 });
398
399 int feeRate(TransactionPriority priority) {
@@ -852,6 +854,22 @@ abstract class ElectrumWalletBase
854 final HD = index == null ? hd : hd.derive(index);
855 return base64Encode(HD.signMessage(message));
856 }
857 +
858 + static BasedUtxoNetwork _getNetwork(bitcoin.NetworkType networkType, CryptoCurrency? currency) {
859 + if (networkType == bitcoin.bitcoin && currency == CryptoCurrency.bch) {
860 + return BitcoinCashNetwork.mainnet;
861 + }
862 +
863 + if (networkType == litecoinNetwork) {
864 + return LitecoinNetwork.mainnet;
865 + }
866 +
867 + if (networkType == bitcoin.testnet) {
868 + return BitcoinNetwork.testnet;
869 + }
870 +
871 + return BitcoinNetwork.mainnet;
872 + }
873 }
874
875 class EstimateTxParams {
@@ -879,7 +897,7 @@ class EstimatedTxResult {
897 final int amount;
898 }
899
882 -BitcoinBaseAddress _addressTypeFromStr(String address, BasedUtxoNetwork network) {
900 +BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
901 if (P2pkhAddress.regex.hasMatch(address)) {
902 return P2pkhAddress.fromAddress(address: address, network: network);
903 } else if (P2shAddress.regex.hasMatch(address)) {
cw_bitcoin/lib/electrum_wallet_addresses.dart
+19 -22
@@ -1,6 +1,5 @@
1 import 'package:bitcoin_base/bitcoin_base.dart';
2 import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
3 -import 'package:bitbox/bitbox.dart' as bitbox;
3 import 'package:cw_bitcoin/bitcoin_address_record.dart';
4 import 'package:cw_bitcoin/electrum.dart';
5 import 'package:cw_core/wallet_addresses.dart';
@@ -30,6 +29,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
29 List<BitcoinAddressRecord>? initialAddresses,
30 Map<String, int>? initialRegularAddressIndex,
31 Map<String, int>? initialChangeAddressIndex,
32 + BitcoinAddressType? initialAddressPageType,
33 }) : _addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
34 addressesByReceiveType =
35 ObservableList<BitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()),
@@ -41,9 +41,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
41 .toSet()),
42 currentReceiveAddressIndexByType = initialRegularAddressIndex ?? {},
43 currentChangeAddressIndexByType = initialChangeAddressIndex ?? {},
44 - _addressPageType = walletInfo.addressPageType != null
45 - ? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
46 - : SegwitAddresType.p2wpkh,
44 + _addressPageType = initialAddressPageType ??
45 + (walletInfo.addressPageType != null
46 + ? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
47 + : SegwitAddresType.p2wpkh),
48 super(walletInfo) {
49 updateAddressesByMatch();
50 }
@@ -52,10 +53,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
53 static const defaultChangeAddressesCount = 17;
54 static const gap = 20;
55
55 - static String toCashAddr(String address) => bitbox.Address.toCashAddress(address);
56 -
57 - static String toLegacy(String address) => bitbox.Address.toLegacyAddress(address);
58 -
56 final ObservableList<BitcoinAddressRecord> _addresses;
57 // Matched by addressPageType
58 late ObservableList<BitcoinAddressRecord> addressesByReceiveType;
@@ -67,7 +64,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
64 final bitcoin.HDWallet sideHd;
65
66 @observable
70 - BitcoinAddressType _addressPageType = SegwitAddresType.p2wpkh;
67 + late BitcoinAddressType _addressPageType;
68
69 @computed
70 BitcoinAddressType get addressPageType => _addressPageType;
@@ -97,7 +94,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
94 }
95 }
96
100 - return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(receiveAddress) : receiveAddress;
97 + return receiveAddress;
98 }
99
100 @observable
@@ -105,9 +102,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
102
103 @override
104 set address(String addr) {
108 - if (addr.startsWith('bitcoincash:')) {
109 - addr = toLegacy(addr);
110 - }
105 final addressRecord = _addresses.firstWhere((addressRecord) => addressRecord.address == addr);
106
107 previousAddressRecord = addressRecord;
@@ -155,11 +149,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
149
150 @override
151 Future<void> init() async {
158 - await _generateInitialAddresses();
159 - await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
160 - await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
161 - await _generateInitialAddresses(type: SegwitAddresType.p2tr);
162 - await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
152 + if (walletInfo.type == WalletType.bitcoinCash) {
153 + await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
154 + } else if (walletInfo.type == WalletType.litecoin) {
155 + await _generateInitialAddresses();
156 + } else if (walletInfo.type == WalletType.bitcoin) {
157 + await _generateInitialAddresses();
158 + await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
159 + await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
160 + await _generateInitialAddresses(type: SegwitAddresType.p2tr);
161 + await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
162 + }
163 updateAddressesByMatch();
164 updateReceiveAddresses();
165 updateChangeAddresses();
@@ -229,9 +229,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
229
230 @action
231 void updateAddress(String address, String label) {
232 - if (address.startsWith('bitcoincash:')) {
233 - address = toLegacy(address);
234 - }
232 final addressRecord =
233 _addresses.firstWhere((addressRecord) => addressRecord.address == address);
234 addressRecord.setNewName(label);
@@ -261,7 +258,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
258 addressRecord.isHidden &&
259 !addressRecord.isUsed &&
260 // TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
264 - addressRecord.type == SegwitAddresType.p2wpkh);
261 + (walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddresType.p2wpkh));
262 changeAddresses.addAll(newAddresses);
263 }
264
cw_bitcoin/lib/electrum_wallet_snapshot.dart
+4 -6
@@ -17,14 +17,12 @@ class ElectrumWalletSnapshot {
17 required this.regularAddressIndex,
18 required this.changeAddressIndex,
19 required this.addressPageType,
20 - required this.network,
20 });
21
22 final String name;
23 final String password;
24 final WalletType type;
26 - final String addressPageType;
27 - final BasedUtxoNetwork network;
25 + final String? addressPageType;
26
27 String mnemonic;
28 List<BitcoinAddressRecord> addresses;
@@ -32,7 +30,8 @@ class ElectrumWalletSnapshot {
30 Map<String, int> regularAddressIndex;
31 Map<String, int> changeAddressIndex;
32
35 - static Future<ElectrumWalletSnapshot> load(String name, WalletType type, String password, BasedUtxoNetwork? network) async {
33 + static Future<ElectrumWalletSnapshot> load(
34 + String name, WalletType type, String password, BasedUtxoNetwork network) async {
35 final path = await pathForWallet(name: name, type: type);
36 final jsonSource = await read(path: path, password: password);
37 final data = json.decode(jsonSource) as Map;
@@ -71,8 +70,7 @@ class ElectrumWalletSnapshot {
70 balance: balance,
71 regularAddressIndex: regularAddressIndexByType,
72 changeAddressIndex: changeAddressIndexByType,
74 - addressPageType: data['address_page_type'] as String? ?? SegwitAddresType.p2wpkh.toString(),
75 - network: data['network_type'] == 'testnet' ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet,
73 + addressPageType: data['address_page_type'] as String?,
74 );
75 }
76 }
cw_bitcoin/lib/script_hash.dart
+4 -3
@@ -1,8 +1,9 @@
1 -import 'package:bitcoin_base/bitcoin_base.dart';
1 import 'package:crypto/crypto.dart';
2 +import 'package:cw_bitcoin/address_to_output_script.dart';
3 +import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin;
4
4 -String scriptHash(String address, {required BasedUtxoNetwork network}) {
5 - final outputScript = addressToOutputScript(address: address, network: network);
5 +String scriptHash(String address, {required bitcoin.BasedUtxoNetwork network}) {
6 + final outputScript = addressToOutputScript(address, network);
7 final parts = sha256.convert(outputScript).toString().split('');
8 var res = '';
9
cw_bitcoin/pubspec.lock
+5 -5
@@ -79,11 +79,11 @@ packages:
79 dependency: "direct main"
80 description:
81 path: "."
82 - ref: cake-update-v1
83 - resolved-ref: "9611e9db77e92a8434e918cdfb620068f6fcb1aa"
82 + ref: cake-update-v2
83 + resolved-ref: "3fd81d238b990bb767fc7a4fdd5053a22a142e2e"
84 url: "https://github.com/cake-tech/bitcoin_base.git"
85 source: git
86 - version: "4.0.0"
86 + version: "4.2.0"
87 bitcoin_flutter:
88 dependency: "direct main"
89 description:
@@ -97,10 +97,10 @@ packages:
97 dependency: "direct main"
98 description:
99 name: blockchain_utils
100 - sha256: "9701dfaa74caad4daae1785f1ec4445cf7fb94e45620bc3a4aca1b9b281dc6c9"
100 + sha256: "38ef5f4a22441ac4370aed9071dc71c460acffc37c79b344533f67d15f24c13c"
101 url: "https://pub.dev"
102 source: hosted
103 - version: "1.6.0"
103 + version: "2.1.1"
104 boolean_selector:
105 dependency: transitive
106 description:
cw_bitcoin/pubspec.yaml
+2 -2
@@ -33,8 +33,8 @@ dependencies:
33 bitcoin_base:
34 git:
35 url: https://github.com/cake-tech/bitcoin_base.git
36 - ref: cake-update-v1
37 - blockchain_utils: ^1.6.0
36 + ref: cake-update-v2
37 + blockchain_utils: ^2.1.1
38
39 dev_dependencies:
40 flutter_test:
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart
+24 -4
@@ -34,7 +34,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
34 required WalletInfo walletInfo,
35 required Box<UnspentCoinsInfo> unspentCoinsInfo,
36 required Uint8List seedBytes,
37 - String? addressPageType,
37 + BitcoinAddressType? addressPageType,
38 List<BitcoinAddressRecord>? initialAddresses,
39 ElectrumBalance? initialBalance,
40 Map<String, int>? initialRegularAddressIndex,
@@ -58,6 +58,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
58 mainHd: hd,
59 sideHd: bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/1"),
60 network: network,
61 + initialAddressPageType: addressPageType,
62 );
63 autorun((_) {
64 this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
@@ -84,7 +85,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
85 seedBytes: await Mnemonic.toSeed(mnemonic),
86 initialRegularAddressIndex: initialRegularAddressIndex,
87 initialChangeAddressIndex: initialChangeAddressIndex,
87 - addressPageType: addressPageType,
88 + addressPageType: P2pkhAddressType.p2pkh,
89 );
90 }
91
@@ -101,12 +102,31 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
102 password: password,
103 walletInfo: walletInfo,
104 unspentCoinsInfo: unspentCoinsInfo,
104 - initialAddresses: snp.addresses,
105 + initialAddresses: snp.addresses.map((addr) {
106 + try {
107 + BitcoinCashAddress(addr.address);
108 + return BitcoinAddressRecord(
109 + addr.address,
110 + index: addr.index,
111 + isHidden: addr.isHidden,
112 + type: P2pkhAddressType.p2pkh,
113 + network: BitcoinCashNetwork.mainnet,
114 + );
115 + } catch (_) {
116 + return BitcoinAddressRecord(
117 + AddressUtils.getCashAddrFormat(addr.address),
118 + index: addr.index,
119 + isHidden: addr.isHidden,
120 + type: P2pkhAddressType.p2pkh,
121 + network: BitcoinCashNetwork.mainnet,
122 + );
123 + }
124 + }).toList(),
125 initialBalance: snp.balance,
126 seedBytes: await Mnemonic.toSeed(snp.mnemonic),
127 initialRegularAddressIndex: snp.regularAddressIndex,
128 initialChangeAddressIndex: snp.changeAddressIndex,
109 - addressPageType: snp.addressPageType,
129 + addressPageType: P2pkhAddressType.p2pkh,
130 );
131 }
132
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart
+1
@@ -19,6 +19,7 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
19 super.initialAddresses,
20 super.initialRegularAddressIndex,
21 super.initialChangeAddressIndex,
22 + super.initialAddressPageType,
23 }) : super(walletInfo);
24
25 @override
cw_bitcoin_cash/pubspec.yaml
+1 -1
@@ -32,7 +32,7 @@ dependencies:
32 bitcoin_base:
33 git:
34 url: https://github.com/cake-tech/bitcoin_base.git
35 - ref: cake-update-v1
35 + ref: cake-update-v2
36
37
38
ios/Podfile.lock
+2 -2
@@ -277,7 +277,7 @@ SPEC CHECKSUMS:
277 flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0
278 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
279 flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
280 - fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
280 + fluttertoast: 48c57db1b71b0ce9e6bba9f31c940ff4b001293c
281 in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
282 local_auth_ios: 1ba1475238daa33a6ffa2a29242558437be435ac
283 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
@@ -302,4 +302,4 @@ SPEC CHECKSUMS:
302
303 PODFILE CHECKSUM: fcb1b8418441a35b438585c9dd8374e722e6c6ca
304
305 -COCOAPODS: 1.12.1
305 +COCOAPODS: 1.15.2
lib/bitcoin/cw_bitcoin.dart
+23 -1
@@ -113,13 +113,35 @@ class CWBitcoin extends Bitcoin {
113 .map((BitcoinAddressRecord addr) => ElectrumSubAddress(
114 id: addr.index,
115 name: addr.name,
116 - address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address,
116 + address: addr.address,
117 txCount: addr.txCount,
118 balance: addr.balance,
119 isChange: addr.isHidden))
120 .toList();
121 }
122
123 + @override
124 + Future<int> estimateFakeSendAllTxAmount(Object wallet, TransactionPriority priority) async {
125 + final electrumWallet = wallet as ElectrumWallet;
126 + final sk = ECPrivate.random();
127 +
128 + final p2shAddr = sk.getPublic().toP2pkhInP2sh();
129 + final p2wpkhAddr = sk.getPublic().toP2wpkhAddress();
130 + final estimatedTx = await electrumWallet.estimateTxFeeAndInputsToUse(
131 + 0,
132 + true,
133 + // Deposit address + change address
134 + [p2shAddr, p2wpkhAddr],
135 + [
136 + BitcoinOutput(address: p2shAddr, value: BigInt.zero),
137 + BitcoinOutput(address: p2wpkhAddr, value: BigInt.zero)
138 + ],
139 + null,
140 + priority as BitcoinTransactionPriority);
141 +
142 + return estimatedTx.amount;
143 + }
144 +
145 @override
146 String getAddress(Object wallet) {
147 final bitcoinWallet = wallet as ElectrumWallet;
lib/buy/moonpay/moonpay_provider.dart
+9 -1
@@ -81,7 +81,7 @@ class MoonPaySellProvider extends BuyProvider {
81 '',
82 <String, dynamic>{
83 'apiKey': _apiKey,
84 - 'defaultBaseCurrencyCode': currency.toString().toLowerCase(),
84 + 'defaultBaseCurrencyCode': _normalizeCurrency(currency),
85 'refundWalletAddress': refundWalletAddress,
86 }..addAll(customParams),
87 );
@@ -134,6 +134,14 @@ class MoonPaySellProvider extends BuyProvider {
134 );
135 }
136 }
137 +
138 + String _normalizeCurrency(CryptoCurrency currency) {
139 + if (currency == CryptoCurrency.maticpoly) {
140 + return "MATIC_POLYGON";
141 + }
142 +
143 + return currency.toString().toLowerCase();
144 + }
145 }
146
147 class MoonPayBuyProvider extends BuyProvider {
lib/core/address_validator.dart
+1 -1
@@ -274,7 +274,7 @@ class AddressValidator extends TextValidator {
274 '|([^0-9a-zA-Z]|^)([23][a-km-zA-HJ-NP-Z1-9]{25,34})([^0-9a-zA-Z]|\$)' //P2shAddress type
275 '|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{25,39})([^0-9a-zA-Z]|\$)' //P2wpkhAddress type
276 '|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{40,80})([^0-9a-zA-Z]|\$)' //P2wshAddress type
277 - '|([^0-9a-zA-Z]|^)((bc|tb)1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89}))([^0-9a-zA-Z]|\$)'; //P2trAddress type
277 + '|([^0-9a-zA-Z]|^)((bc|tb)1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89}))([^0-9a-zA-Z]|\$)'; //P2trAddress type
278 case CryptoCurrency.ltc:
279 return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
280 '|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'
lib/entities/provider_types.dart
+6 -1
@@ -89,7 +89,12 @@ class ProvidersHelper {
89 case WalletType.bitcoinCash:
90 return [ProviderType.askEachTime, ProviderType.moonpaySell];
91 case WalletType.polygon:
92 - return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.dfx];
92 + return [
93 + ProviderType.askEachTime,
94 + ProviderType.onramper,
95 + ProviderType.moonpaySell,
96 + ProviderType.dfx,
97 + ];
98 case WalletType.solana:
99 return [
100 ProviderType.askEachTime,
lib/src/screens/exchange/exchange_page.dart
+14 -7
@@ -384,7 +384,7 @@ class ExchangePage extends BasePage {
384 (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey));
385
386 reaction((_) => exchangeViewModel.depositAmount, (String amount) {
387 - if (depositKey.currentState!.amountController.text != amount) {
387 + if (depositKey.currentState!.amountController.text != amount && amount != S.of(context).all) {
388 depositKey.currentState!.amountController.text = amount;
389 }
390 });
@@ -467,7 +467,9 @@ class ExchangePage extends BasePage {
467 .addListener(() => exchangeViewModel.depositAddress = depositAddressController.text);
468
469 depositAmountController.addListener(() {
470 - if (depositAmountController.text != exchangeViewModel.depositAmount) {
470 + if (depositAmountController.text != exchangeViewModel.depositAmount &&
471 + depositAmountController.text != S.of(context).all) {
472 + exchangeViewModel.isSendAllEnabled = false;
473 _depositAmountDebounce.run(() {
474 exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
475 exchangeViewModel.isReceiveAmountEntered = false;
@@ -589,8 +591,9 @@ class ExchangePage extends BasePage {
591 onDispose: disposeBestRateSync,
592 hasAllAmount: exchangeViewModel.hasAllAmount,
593 allAmount: exchangeViewModel.hasAllAmount
592 - ? () => exchangeViewModel.calculateDepositAllAmount()
594 + ? () => exchangeViewModel.enableSendAllAmount()
595 : null,
596 + isAllAmountEnabled: exchangeViewModel.isSendAllEnabled,
597 amountFocusNode: _depositAmountFocus,
598 addressFocusNode: _depositAddressFocus,
599 key: depositKey,
@@ -626,8 +629,10 @@ class ExchangePage extends BasePage {
629 },
630 imageArrow: arrowBottomPurple,
631 currencyButtonColor: Colors.transparent,
629 - addressButtonsColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
630 - borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderTopPanelColor,
632 + addressButtonsColor:
633 + Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
634 + borderColor:
635 + Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderTopPanelColor,
636 currencyValueValidator: (value) {
637 return !exchangeViewModel.isFixedRateMode
638 ? AmountValidator(
@@ -673,8 +678,10 @@ class ExchangePage extends BasePage {
678 exchangeViewModel.changeReceiveCurrency(currency: currency),
679 imageArrow: arrowBottomCakeGreen,
680 currencyButtonColor: Colors.transparent,
676 - addressButtonsColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
677 - borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor,
681 + addressButtonsColor:
682 + Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
683 + borderColor:
684 + Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor,
685 currencyValueValidator: (value) {
686 return exchangeViewModel.isFixedRateMode
687 ? AmountValidator(
lib/src/screens/exchange/exchange_template_page.dart
+171 -194
@@ -56,17 +56,14 @@ class ExchangeTemplatePage extends BasePage {
56 height: 8,
57 );
58
59 - final depositWalletName =
60 - exchangeViewModel.depositCurrency == CryptoCurrency.xmr
59 + final depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
60 ? exchangeViewModel.wallet.name
61 : null;
63 - final receiveWalletName =
64 - exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
62 + final receiveWalletName = exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
63 ? exchangeViewModel.wallet.name
64 : null;
65
68 - WidgetsBinding.instance
69 - .addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
66 + WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
67
68 return KeyboardActions(
69 disableScroll: true,
@@ -76,128 +73,125 @@ class ExchangeTemplatePage extends BasePage {
73 nextFocus: false,
74 actions: [
75 KeyboardActionsItem(
79 - focusNode: _depositAmountFocus,
80 - toolbarButtons: [(_) => KeyboardDoneButton()]),
76 + focusNode: _depositAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()]),
77 KeyboardActionsItem(
82 - focusNode: _receiveAmountFocus,
83 - toolbarButtons: [(_) => KeyboardDoneButton()])
78 + focusNode: _receiveAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()])
79 ]),
80 child: Container(
86 - color: Theme.of(context).colorScheme.background,
87 - child: Form(
88 - key: _formKey,
89 - child: ScrollableWithBottomSection(
90 - contentPadding: EdgeInsets.only(bottom: 24),
91 - content: Container(
92 - padding: EdgeInsets.only(bottom: 32),
93 - decoration: BoxDecoration(
94 - borderRadius: BorderRadius.only(
95 - bottomLeft: Radius.circular(24),
96 - bottomRight: Radius.circular(24)
97 - ),
98 - gradient: LinearGradient(
99 - colors: [
100 - Theme.of(context).extension<ExchangePageTheme>()!.firstGradientBottomPanelColor,
101 - Theme.of(context).extension<ExchangePageTheme>()!.secondGradientBottomPanelColor,
102 - ],
103 - stops: [0.35, 1.0],
104 - begin: Alignment.topLeft,
105 - end: Alignment.bottomRight),
106 - ),
107 - child: FocusTraversalGroup(
108 - policy: OrderedTraversalPolicy(),
109 - child: Column(
110 - children: <Widget>[
111 - Container(
112 - decoration: BoxDecoration(
113 - borderRadius: BorderRadius.only(
114 - bottomLeft: Radius.circular(24),
115 - bottomRight: Radius.circular(24)
116 - ),
117 - gradient: LinearGradient(
118 - colors: [
119 - Theme.of(context).extension<ExchangePageTheme>()!.firstGradientTopPanelColor,
120 - Theme.of(context).extension<ExchangePageTheme>()!.secondGradientTopPanelColor,
121 - ],
122 - begin: Alignment.topLeft,
123 - end: Alignment.bottomRight),
124 - ),
125 - padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
126 - child: Observer(
127 - builder: (_) => ExchangeCard(
128 - amountFocusNode: _depositAmountFocus,
129 - key: depositKey,
130 - title: S.of(context).you_will_send,
131 - initialCurrency:
132 - exchangeViewModel.depositCurrency,
133 - initialWalletName: depositWalletName ?? '',
134 - initialAddress: exchangeViewModel
135 - .depositCurrency ==
136 - exchangeViewModel.wallet.currency
137 - ? exchangeViewModel.wallet.walletAddresses.address
138 - : exchangeViewModel.depositAddress,
139 - initialIsAmountEditable: true,
140 - initialIsAddressEditable: exchangeViewModel
141 - .isDepositAddressEnabled,
142 - isAmountEstimated: false,
143 - hasRefundAddress: true,
144 - isMoneroWallet: exchangeViewModel.isMoneroWallet,
145 - currencies: CryptoCurrency.all,
146 - onCurrencySelected: (currency) =>
147 - exchangeViewModel.changeDepositCurrency(
148 - currency: currency),
149 - imageArrow: arrowBottomPurple,
150 - currencyButtonColor: Colors.transparent,
151 - addressButtonsColor:
152 - Theme.of(context).extension<ExchangePageTheme>()!.textFieldButtonColor,
153 - borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor,
154 - currencyValueValidator: AmountValidator(
155 - currency: exchangeViewModel.depositCurrency),
156 - //addressTextFieldValidator: AddressValidator(
157 - // type: exchangeViewModel.depositCurrency),
81 + color: Theme.of(context).colorScheme.background,
82 + child: Form(
83 + key: _formKey,
84 + child: ScrollableWithBottomSection(
85 + contentPadding: EdgeInsets.only(bottom: 24),
86 + content: Container(
87 + padding: EdgeInsets.only(bottom: 32),
88 + decoration: BoxDecoration(
89 + borderRadius: BorderRadius.only(
90 + bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
91 + gradient: LinearGradient(colors: [
92 + Theme.of(context)
93 + .extension<ExchangePageTheme>()!
94 + .firstGradientBottomPanelColor,
95 + Theme.of(context)
96 + .extension<ExchangePageTheme>()!
97 + .secondGradientBottomPanelColor,
98 + ], stops: [
99 + 0.35,
100 + 1.0
101 + ], begin: Alignment.topLeft, end: Alignment.bottomRight),
102 + ),
103 + child: FocusTraversalGroup(
104 + policy: OrderedTraversalPolicy(),
105 + child: Column(
106 + children: <Widget>[
107 + Container(
108 + decoration: BoxDecoration(
109 + borderRadius: BorderRadius.only(
110 + bottomLeft: Radius.circular(24),
111 + bottomRight: Radius.circular(24)),
112 + gradient: LinearGradient(colors: [
113 + Theme.of(context)
114 + .extension<ExchangePageTheme>()!
115 + .firstGradientTopPanelColor,
116 + Theme.of(context)
117 + .extension<ExchangePageTheme>()!
118 + .secondGradientTopPanelColor,
119 + ], begin: Alignment.topLeft, end: Alignment.bottomRight),
120 + ),
121 + padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
122 + child: Observer(
123 + builder: (_) => ExchangeCard(
124 + amountFocusNode: _depositAmountFocus,
125 + key: depositKey,
126 + title: S.of(context).you_will_send,
127 + initialCurrency: exchangeViewModel.depositCurrency,
128 + initialWalletName: depositWalletName ?? '',
129 + initialAddress: exchangeViewModel.depositCurrency ==
130 + exchangeViewModel.wallet.currency
131 + ? exchangeViewModel.wallet.walletAddresses.address
132 + : exchangeViewModel.depositAddress,
133 + initialIsAmountEditable: true,
134 + initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
135 + isAmountEstimated: false,
136 + hasRefundAddress: true,
137 + isMoneroWallet: exchangeViewModel.isMoneroWallet,
138 + currencies: CryptoCurrency.all,
139 + onCurrencySelected: (currency) =>
140 + exchangeViewModel.changeDepositCurrency(currency: currency),
141 + imageArrow: arrowBottomPurple,
142 + currencyButtonColor: Colors.transparent,
143 + addressButtonsColor: Theme.of(context)
144 + .extension<ExchangePageTheme>()!
145 + .textFieldButtonColor,
146 + borderColor: Theme.of(context)
147 + .extension<ExchangePageTheme>()!
148 + .textFieldBorderBottomPanelColor,
149 + currencyValueValidator:
150 + AmountValidator(currency: exchangeViewModel.depositCurrency),
151 + //addressTextFieldValidator: AddressValidator(
152 + // type: exchangeViewModel.depositCurrency),
153 + ),
154 + ),
155 ),
159 - ),
156 + Padding(
157 + padding: EdgeInsets.only(top: 29, left: 24, right: 24),
158 + child: Observer(
159 + builder: (_) => ExchangeCard(
160 + amountFocusNode: _receiveAmountFocus,
161 + key: receiveKey,
162 + title: S.of(context).you_will_get,
163 + initialCurrency: exchangeViewModel.receiveCurrency,
164 + initialWalletName: receiveWalletName ?? '',
165 + initialAddress: exchangeViewModel.receiveCurrency ==
166 + exchangeViewModel.wallet.currency
167 + ? exchangeViewModel.wallet.walletAddresses.address
168 + : exchangeViewModel.receiveAddress,
169 + initialIsAmountEditable: false,
170 + isAmountEstimated: true,
171 + isMoneroWallet: exchangeViewModel.isMoneroWallet,
172 + currencies: exchangeViewModel.receiveCurrencies,
173 + onCurrencySelected: (currency) => exchangeViewModel
174 + .changeReceiveCurrency(currency: currency),
175 + imageArrow: arrowBottomCakeGreen,
176 + currencyButtonColor: Colors.transparent,
177 + addressButtonsColor: Theme.of(context)
178 + .extension<ExchangePageTheme>()!
179 + .textFieldButtonColor,
180 + borderColor: Theme.of(context)
181 + .extension<ExchangePageTheme>()!
182 + .textFieldBorderBottomPanelColor,
183 + currencyValueValidator: AmountValidator(
184 + currency: exchangeViewModel.receiveCurrency),
185 + //addressTextFieldValidator: AddressValidator(
186 + // type: exchangeViewModel.receiveCurrency),
187 + )),
188 + )
189 + ],
190 ),
161 - Padding(
162 - padding: EdgeInsets.only(top: 29, left: 24, right: 24),
163 - child: Observer(
164 - builder: (_) => ExchangeCard(
165 - amountFocusNode: _receiveAmountFocus,
166 - key: receiveKey,
167 - title: S.of(context).you_will_get,
168 - initialCurrency:
169 - exchangeViewModel.receiveCurrency,
170 - initialWalletName: receiveWalletName ?? '',
171 - initialAddress:
172 - exchangeViewModel.receiveCurrency ==
173 - exchangeViewModel.wallet.currency
174 - ? exchangeViewModel.wallet.walletAddresses.address
175 - : exchangeViewModel.receiveAddress,
176 - initialIsAmountEditable: false,
177 - isAmountEstimated: true,
178 - isMoneroWallet: exchangeViewModel.isMoneroWallet,
179 - currencies: exchangeViewModel.receiveCurrencies,
180 - onCurrencySelected: (currency) =>
181 - exchangeViewModel.changeReceiveCurrency(
182 - currency: currency),
183 - imageArrow: arrowBottomCakeGreen,
184 - currencyButtonColor: Colors.transparent,
185 - addressButtonsColor:
186 - Theme.of(context).extension<ExchangePageTheme>()!.textFieldButtonColor,
187 - borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor,
188 - currencyValueValidator: AmountValidator(
189 - currency: exchangeViewModel.receiveCurrency),
190 - //addressTextFieldValidator: AddressValidator(
191 - // type: exchangeViewModel.receiveCurrency),
192 - )),
193 - )
194 - ],
191 + ),
192 ),
196 - ),
197 - ),
198 - bottomSectionPadding:
199 - EdgeInsets.only(left: 24, right: 24, bottom: 24),
200 - bottomSection: Column(children: <Widget>[
193 + bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
194 + bottomSection: Column(children: <Widget>[
195 Padding(
196 padding: EdgeInsets.only(bottom: 15),
197 child: Observer(
@@ -217,36 +211,31 @@ class ExchangeTemplatePage extends BasePage {
211 ),
212 ),
213 PrimaryButton(
220 - onPressed: () {
221 - if (_formKey.currentState != null && _formKey.currentState!.validate()) {
222 - exchangeViewModel.addTemplate(
223 - amount: exchangeViewModel.depositAmount,
224 - depositCurrency:
225 - exchangeViewModel.depositCurrency.name,
226 - depositCurrencyTitle: exchangeViewModel
227 - .depositCurrency.title + ' ${exchangeViewModel.depositCurrency.tag ?? ''}',
228 - receiveCurrency:
229 - exchangeViewModel.receiveCurrency.name,
230 - receiveCurrencyTitle: exchangeViewModel
231 - .receiveCurrency.title + ' ${exchangeViewModel.receiveCurrency.tag ?? ''}',
232 - provider: exchangeViewModel.provider.toString(),
233 - depositAddress: exchangeViewModel.depositAddress,
234 - receiveAddress: exchangeViewModel.receiveAddress);
235 - exchangeViewModel.updateTemplate();
236 - Navigator.of(context).pop();
237 - }
238 - },
239 - text: S.of(context).save,
240 - color: Theme.of(context).primaryColor,
241 - textColor: Colors.white),
242 - ]),
243 - ))
244 - )
245 - );
214 + onPressed: () {
215 + if (_formKey.currentState != null && _formKey.currentState!.validate()) {
216 + exchangeViewModel.addTemplate(
217 + amount: exchangeViewModel.depositAmount,
218 + depositCurrency: exchangeViewModel.depositCurrency.name,
219 + depositCurrencyTitle: exchangeViewModel.depositCurrency.title +
220 + ' ${exchangeViewModel.depositCurrency.tag ?? ''}',
221 + receiveCurrency: exchangeViewModel.receiveCurrency.name,
222 + receiveCurrencyTitle: exchangeViewModel.receiveCurrency.title +
223 + ' ${exchangeViewModel.receiveCurrency.tag ?? ''}',
224 + provider: exchangeViewModel.provider.toString(),
225 + depositAddress: exchangeViewModel.depositAddress,
226 + receiveAddress: exchangeViewModel.receiveAddress);
227 + exchangeViewModel.updateTemplate();
228 + Navigator.of(context).pop();
229 + }
230 + },
231 + text: S.of(context).save,
232 + color: Theme.of(context).primaryColor,
233 + textColor: Colors.white),
234 + ]),
235 + ))));
236 }
237
248 - void _setReactions(
249 - BuildContext context, ExchangeViewModel exchangeViewModel) {
238 + void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) {
239 if (_isReactionsSet) {
240 return;
241 }
@@ -272,33 +261,27 @@ class ExchangeTemplatePage extends BasePage {
261 // key.currentState.changeLimits(min: min, max: max);
262 // }
263
275 - _onCurrencyChange(
276 - exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
277 - _onCurrencyChange(
278 - exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
264 + _onCurrencyChange(exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
265 + _onCurrencyChange(exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
266
267 reaction(
281 - (_) => exchangeViewModel.wallet.name,
282 - (String _) => _onWalletNameChange(
283 - exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
268 + (_) => exchangeViewModel.wallet.name,
269 + (String _) =>
270 + _onWalletNameChange(exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
271
272 reaction(
286 - (_) => exchangeViewModel.wallet.name,
287 - (String _) => _onWalletNameChange(
288 - exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
273 + (_) => exchangeViewModel.wallet.name,
274 + (String _) =>
275 + _onWalletNameChange(exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
276
290 - reaction(
291 - (_) => exchangeViewModel.receiveCurrency,
292 - (CryptoCurrency currency) =>
293 - _onCurrencyChange(currency, exchangeViewModel, receiveKey));
277 + reaction((_) => exchangeViewModel.receiveCurrency,
278 + (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, receiveKey));
279
295 - reaction(
296 - (_) => exchangeViewModel.depositCurrency,
297 - (CryptoCurrency currency) =>
298 - _onCurrencyChange(currency, exchangeViewModel, depositKey));
280 + reaction((_) => exchangeViewModel.depositCurrency,
281 + (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey));
282
283 reaction((_) => exchangeViewModel.depositAmount, (String amount) {
301 - if (depositKey.currentState!.amountController.text != amount) {
284 + if (depositKey.currentState!.amountController.text != amount && amount != S.of(context).all) {
285 depositKey.currentState!.amountController.text = amount;
286 }
287 });
@@ -309,10 +292,9 @@ class ExchangeTemplatePage extends BasePage {
292 }
293 });
294
312 - reaction((_) => exchangeViewModel.isDepositAddressEnabled,
313 - (bool isEnabled) {
314 - depositKey.currentState!.isAddressEditable(isEditable: isEnabled);
315 - });
295 + reaction((_) => exchangeViewModel.isDepositAddressEnabled, (bool isEnabled) {
296 + depositKey.currentState!.isAddressEditable(isEditable: isEnabled);
297 + });
298
299 reaction((_) => exchangeViewModel.receiveAmount, (String amount) {
300 if (receiveKey.currentState!.amountController.text != amount) {
@@ -353,30 +335,28 @@ class ExchangeTemplatePage extends BasePage {
335 receiveKey.currentState.changeLimits(min: null, max: null);
336 });*/
337
356 - depositAddressController.addListener(
357 - () => exchangeViewModel.depositAddress = depositAddressController.text);
338 + depositAddressController
339 + .addListener(() => exchangeViewModel.depositAddress = depositAddressController.text);
340
341 depositAmountController.addListener(() {
360 - if (depositAmountController.text != exchangeViewModel.depositAmount) {
361 - exchangeViewModel.changeDepositAmount(
362 - amount: depositAmountController.text);
342 + if (depositAmountController.text != exchangeViewModel.depositAmount &&
343 + exchangeViewModel.depositAmount != S.of(context).all) {
344 + exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
345 exchangeViewModel.isReceiveAmountEntered = false;
346 }
347 });
348
367 - receiveAddressController.addListener(
368 - () => exchangeViewModel.receiveAddress = receiveAddressController.text);
349 + receiveAddressController
350 + .addListener(() => exchangeViewModel.receiveAddress = receiveAddressController.text);
351
352 receiveAmountController.addListener(() {
353 if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
372 - exchangeViewModel.changeReceiveAmount(
373 - amount: receiveAmountController.text);
354 + exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text);
355 exchangeViewModel.isReceiveAmountEntered = true;
356 }
357 });
358
378 - reaction((_) => exchangeViewModel.wallet.walletAddresses.address,
379 - (String address) {
359 + reaction((_) => exchangeViewModel.wallet.walletAddresses.address, (String address) {
360 if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
361 depositKey.currentState!.changeAddress(address: address);
362 }
@@ -389,29 +369,26 @@ class ExchangeTemplatePage extends BasePage {
369 _isReactionsSet = true;
370 }
371
392 - void _onCurrencyChange(CryptoCurrency currency,
393 - ExchangeViewModel exchangeViewModel, GlobalKey<ExchangeCardState> key) {
372 + void _onCurrencyChange(CryptoCurrency currency, ExchangeViewModel exchangeViewModel,
373 + GlobalKey<ExchangeCardState> key) {
374 final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
375
376 key.currentState!.changeSelectedCurrency(currency);
397 - key.currentState!.changeWalletName(
398 - isCurrentTypeWallet ? exchangeViewModel.wallet.name : '');
377 + key.currentState!.changeWalletName(isCurrentTypeWallet ? exchangeViewModel.wallet.name : '');
378
379 key.currentState!.changeAddress(
401 - address: isCurrentTypeWallet
402 - ? exchangeViewModel.wallet.walletAddresses.address : '');
380 + address: isCurrentTypeWallet ? exchangeViewModel.wallet.walletAddresses.address : '');
381
382 key.currentState!.changeAmount(amount: '');
383 }
384
407 - void _onWalletNameChange(ExchangeViewModel exchangeViewModel,
408 - CryptoCurrency currency, GlobalKey<ExchangeCardState> key) {
385 + void _onWalletNameChange(ExchangeViewModel exchangeViewModel, CryptoCurrency currency,
386 + GlobalKey<ExchangeCardState> key) {
387 final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
388
389 if (isCurrentTypeWallet) {
390 key.currentState!.changeWalletName(exchangeViewModel.wallet.name);
413 - key.currentState!.addressController.text =
414 - exchangeViewModel.wallet.walletAddresses.address;
391 + key.currentState!.addressController.text = exchangeViewModel.wallet.walletAddresses.address;
392 } else if (key.currentState!.addressController.text ==
393 exchangeViewModel.wallet.walletAddresses.address) {
394 key.currentState!.changeWalletName('');
lib/src/screens/exchange/widgets/exchange_card.dart
+112 -130
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/core/amount_validator.dart';
2 import 'package:cake_wallet/entities/contact_base.dart';
3 import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
4 import 'package:cake_wallet/routes.dart';
@@ -37,6 +38,7 @@ class ExchangeCard extends StatefulWidget {
38 this.addressButtonsColor = Colors.transparent,
39 this.borderColor = Colors.transparent,
40 this.hasAllAmount = false,
41 + this.isAllAmountEnabled = false,
42 this.amountFocusNode,
43 this.addressFocusNode,
44 this.allAmount,
@@ -62,9 +64,11 @@ class ExchangeCard extends StatefulWidget {
64 final Color borderColor;
65 final FormFieldValidator<String>? currencyValueValidator;
66 final FormFieldValidator<String>? addressTextFieldValidator;
67 + final FormFieldValidator<String> allAmountValidator = AllAmountValidator();
68 final FocusNode? amountFocusNode;
69 final FocusNode? addressFocusNode;
70 final bool hasAllAmount;
71 + final bool isAllAmountEnabled;
72 final VoidCallback? allAmount;
73 final void Function(BuildContext context)? onPushPasteButton;
74 final void Function(BuildContext context)? onPushAddressBookButton;
@@ -76,15 +80,15 @@ class ExchangeCard extends StatefulWidget {
80
81 class ExchangeCardState extends State<ExchangeCard> {
82 ExchangeCardState()
79 - : _title = '',
80 - _min = '',
81 - _max = '',
82 - _isAmountEditable = false,
83 - _isAddressEditable = false,
84 - _walletName = '',
85 - _selectedCurrency = CryptoCurrency.btc,
86 - _isAmountEstimated = false,
87 - _isMoneroWallet = false;
83 + : _title = '',
84 + _min = '',
85 + _max = '',
86 + _isAmountEditable = false,
87 + _isAddressEditable = false,
88 + _walletName = '',
89 + _selectedCurrency = CryptoCurrency.btc,
90 + _isAmountEstimated = false,
91 + _isMoneroWallet = false;
92
93 final addressController = TextEditingController();
94 final amountController = TextEditingController();
@@ -160,6 +164,12 @@ class ExchangeCardState extends State<ExchangeCard> {
164
165 @override
166 Widget build(BuildContext context) {
167 + if (widget.isAllAmountEnabled) {
168 + WidgetsBinding.instance.addPostFrameCallback((_) {
169 + amountController.text = S.of(context).all;
170 + });
171 + }
172 +
173 final copyImage = Image.asset('assets/images/copy_content.png',
174 height: 16,
175 width: 16,
@@ -168,8 +178,7 @@ class ExchangeCardState extends State<ExchangeCard> {
178 return Container(
179 width: double.infinity,
180 color: Colors.transparent,
171 - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <
172 - Widget>[
181 + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
182 Row(
183 mainAxisAlignment: MainAxisAlignment.start,
184 children: <Widget>[
@@ -202,40 +211,38 @@ class ExchangeCardState extends State<ExchangeCard> {
211 ),
212 Text(_selectedCurrency.toString(),
213 style: TextStyle(
205 - fontWeight: FontWeight.w600,
206 - fontSize: 16,
207 - color: Colors.white))
214 + fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white))
215 ]),
216 ),
217 ),
211 - _selectedCurrency.tag != null ? Padding(
212 - padding: const EdgeInsets.only(right:3.0),
213 - child: Container(
214 - height: 32,
215 - decoration: BoxDecoration(
216 - color: widget.addressButtonsColor ??
217 - Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
218 - borderRadius:
219 - BorderRadius.all(Radius.circular(6))),
220 - child: Center(
221 - child: Padding(
222 - padding: const EdgeInsets.all(6.0),
223 - child: Text(_selectedCurrency.tag!,
224 - style: TextStyle(
225 - fontSize: 12,
226 - fontWeight: FontWeight.bold,
227 - color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor)),
218 + if (_selectedCurrency.tag != null)
219 + Padding(
220 + padding: const EdgeInsets.only(right: 3.0),
221 + child: Container(
222 + height: 32,
223 + decoration: BoxDecoration(
224 + color: widget.addressButtonsColor ??
225 + Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
226 + borderRadius: BorderRadius.all(Radius.circular(6))),
227 + child: Center(
228 + child: Padding(
229 + padding: const EdgeInsets.all(6.0),
230 + child: Text(_selectedCurrency.tag!,
231 + style: TextStyle(
232 + fontSize: 12,
233 + fontWeight: FontWeight.bold,
234 + color: Theme.of(context)
235 + .extension<SendPageTheme>()!
236 + .textFieldButtonIconColor)),
237 + ),
238 ),
239 ),
240 ),
231 - ) : Container(),
241 Padding(
242 padding: const EdgeInsets.only(right: 4.0),
243 child: Text(':',
244 style: TextStyle(
236 - fontWeight: FontWeight.w600,
237 - fontSize: 16,
238 - color: Colors.white)),
245 + fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white)),
246 ),
247 Expanded(
248 child: Row(
@@ -249,26 +256,27 @@ class ExchangeCardState extends State<ExchangeCard> {
256 controller: amountController,
257 enabled: _isAmountEditable,
258 textAlign: TextAlign.left,
252 - keyboardType: TextInputType.numberWithOptions(
253 - signed: false, decimal: true),
259 + keyboardType:
260 + TextInputType.numberWithOptions(signed: false, decimal: true),
261 inputFormatters: [
255 - FilteringTextInputFormatter.deny(
256 - RegExp('[\\-|\\ ]'))
262 + FilteringTextInputFormatter.deny(RegExp('[\\-|\\ ]'))
263 ],
264 hintText: '0.0000',
265 borderColor: Colors.transparent,
266 //widget.borderColor,
267 textStyle: TextStyle(
262 - fontSize: 16,
263 - fontWeight: FontWeight.w600,
264 - color: Colors.white),
268 + fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
269 placeholderTextStyle: TextStyle(
270 fontSize: 16,
271 fontWeight: FontWeight.w600,
268 - color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
269 - validator: _isAmountEditable
270 - ? widget.currencyValueValidator
271 - : null),
272 + color: Theme.of(context)
273 + .extension<ExchangePageTheme>()!
274 + .hintTextColor),
275 + validator: widget.hasAllAmount
276 + ? widget.allAmountValidator
277 + : _isAmountEditable
278 + ? widget.currencyValueValidator
279 + : null),
280 ),
281 ),
282 if (widget.hasAllAmount)
@@ -276,9 +284,10 @@ class ExchangeCardState extends State<ExchangeCard> {
284 height: 32,
285 width: 32,
286 decoration: BoxDecoration(
279 - color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
280 - borderRadius:
281 - BorderRadius.all(Radius.circular(6))),
287 + color: Theme.of(context)
288 + .extension<SendPageTheme>()!
289 + .textFieldButtonColor,
290 + borderRadius: BorderRadius.all(Radius.circular(6))),
291 child: InkWell(
292 onTap: () => widget.allAmount?.call(),
293 child: Center(
@@ -287,7 +296,9 @@ class ExchangeCardState extends State<ExchangeCard> {
296 style: TextStyle(
297 fontSize: 12,
298 fontWeight: FontWeight.bold,
290 - color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor)),
299 + color: Theme.of(context)
300 + .extension<SendPageTheme>()!
301 + .textFieldButtonIconColor)),
302 ),
303 ),
304 )
@@ -296,39 +307,30 @@ class ExchangeCardState extends State<ExchangeCard> {
307 ),
308 ],
309 )),
299 - Divider(
300 - height: 1,
301 - color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
310 + Divider(height: 1, color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
311 Padding(
312 padding: EdgeInsets.only(top: 5),
313 child: Container(
314 height: 15,
306 - child: Row(
307 - mainAxisAlignment: MainAxisAlignment.start,
308 - children: <Widget>[
309 - _min != null
310 - ? Text(
311 - S
312 - .of(context)
313 - .min_value(_min ?? '', _selectedCurrency.toString()),
314 - style: TextStyle(
315 - fontSize: 10,
316 - height: 1.2,
317 - color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
318 - )
319 - : Offstage(),
320 - _min != null ? SizedBox(width: 10) : Offstage(),
321 - _max != null
322 - ? Text(
323 - S
324 - .of(context)
325 - .max_value(_max ?? '', _selectedCurrency.toString()),
326 - style: TextStyle(
327 - fontSize: 10,
328 - height: 1.2,
329 - color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor))
330 - : Offstage(),
331 - ])),
315 + child: Row(mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[
316 + _min != null
317 + ? Text(
318 + S.of(context).min_value(_min ?? '', _selectedCurrency.toString()),
319 + style: TextStyle(
320 + fontSize: 10,
321 + height: 1.2,
322 + color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
323 + )
324 + : Offstage(),
325 + _min != null ? SizedBox(width: 10) : Offstage(),
326 + _max != null
327 + ? Text(S.of(context).max_value(_max ?? '', _selectedCurrency.toString()),
328 + style: TextStyle(
329 + fontSize: 10,
330 + height: 1.2,
331 + color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor))
332 + : Offstage(),
333 + ])),
334 ),
335 !_isAddressEditable && widget.hasRefundAddress
336 ? Padding(
@@ -343,7 +345,7 @@ class ExchangeCardState extends State<ExchangeCard> {
345 : Offstage(),
346 _isAddressEditable
347 ? FocusTraversalOrder(
346 - order: NumericFocusOrder(2),
348 + order: NumericFocusOrder(2),
349 child: Padding(
350 padding: EdgeInsets.only(top: 20),
351 child: AddressTextField(
@@ -352,27 +354,23 @@ class ExchangeCardState extends State<ExchangeCard> {
354 onURIScanned: (uri) {
355 final paymentRequest = PaymentRequest.fromUri(uri);
356 addressController.text = paymentRequest.address;
355 -
357 +
358 if (amountController.text.isNotEmpty) {
359 _showAmountPopup(context, paymentRequest);
360 return;
361 }
362 widget.amountFocusNode?.requestFocus();
361 - amountController.text = paymentRequest.amount;
363 + amountController.text = paymentRequest.amount;
364 },
363 - placeholder: widget.hasRefundAddress
364 - ? S.of(context).refund_address
365 - : null,
365 + placeholder: widget.hasRefundAddress ? S.of(context).refund_address : null,
366 options: [
367 AddressTextFieldOption.paste,
368 AddressTextFieldOption.qrCode,
369 AddressTextFieldOption.addressBook,
370 ],
371 isBorderExist: false,
372 - textStyle: TextStyle(
373 - fontSize: 16,
374 - fontWeight: FontWeight.w600,
375 - color: Colors.white),
372 + textStyle:
373 + TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
374 hintStyle: TextStyle(
375 fontSize: 16,
376 fontWeight: FontWeight.w600,
@@ -381,27 +379,22 @@ class ExchangeCardState extends State<ExchangeCard> {
379 validator: widget.addressTextFieldValidator,
380 onPushPasteButton: widget.onPushPasteButton,
381 onPushAddressBookButton: widget.onPushAddressBookButton,
384 - selectedCurrency: _selectedCurrency
385 - ),
386 -
382 + selectedCurrency: _selectedCurrency),
383 ),
388 - )
384 + )
385 : Padding(
386 padding: EdgeInsets.only(top: 10),
387 child: Builder(
388 builder: (context) => Stack(children: <Widget>[
393 - FocusTraversalOrder(
394 - order: NumericFocusOrder(3),
395 - child: BaseTextFormField(
396 - controller: addressController,
397 - borderColor: Colors.transparent,
398 - suffixIcon:
399 - SizedBox(width: _isMoneroWallet ? 80 : 36),
400 - textStyle: TextStyle(
401 - fontSize: 16,
402 - fontWeight: FontWeight.w600,
403 - color: Colors.white),
404 - validator: widget.addressTextFieldValidator),
389 + FocusTraversalOrder(
390 + order: NumericFocusOrder(3),
391 + child: BaseTextFormField(
392 + controller: addressController,
393 + borderColor: Colors.transparent,
394 + suffixIcon: SizedBox(width: _isMoneroWallet ? 80 : 36),
395 + textStyle: TextStyle(
396 + fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
397 + validator: widget.addressTextFieldValidator),
398 ),
399 Positioned(
400 top: 2,
@@ -421,33 +414,28 @@ class ExchangeCardState extends State<ExchangeCard> {
414 child: InkWell(
415 onTap: () async {
416 final contact =
424 - await Navigator.of(context)
425 - .pushNamed(
417 + await Navigator.of(context).pushNamed(
418 Routes.pickerAddressBook,
419 arguments: widget.initialCurrency,
420 );
421
430 - if (contact is ContactBase &&
431 - contact.address != null) {
422 + if (contact is ContactBase) {
423 setState(() =>
433 - addressController.text =
434 - contact.address);
435 - widget.onPushAddressBookButton
436 - ?.call(context);
424 + addressController.text = contact.address);
425 + widget.onPushAddressBookButton?.call(context);
426 }
427 },
428 child: Container(
429 padding: EdgeInsets.all(8),
430 decoration: BoxDecoration(
442 - color: widget
443 - .addressButtonsColor,
431 + color: widget.addressButtonsColor,
432 borderRadius:
445 - BorderRadius.all(
446 - Radius.circular(
447 - 6))),
433 + BorderRadius.all(Radius.circular(6))),
434 child: Image.asset(
435 'assets/images/open_book.png',
450 - color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor,
436 + color: Theme.of(context)
437 + .extension<SendPageTheme>()!
438 + .textFieldButtonIconColor,
439 )),
440 ),
441 )),
@@ -462,18 +450,13 @@ class ExchangeCardState extends State<ExchangeCard> {
450 label: S.of(context).copy_address,
451 child: InkWell(
452 onTap: () {
465 - Clipboard.setData(ClipboardData(
466 - text: addressController
467 - .text));
453 + Clipboard.setData(
454 + ClipboardData(text: addressController.text));
455 showBar<void>(
469 - context,
470 - S
471 - .of(context)
472 - .copied_to_clipboard);
456 + context, S.of(context).copied_to_clipboard);
457 },
458 child: Container(
475 - padding: EdgeInsets.fromLTRB(
476 - 8, 8, 0, 8),
459 + padding: EdgeInsets.fromLTRB(8, 8, 0, 8),
460 color: Colors.transparent,
461 child: copyImage),
462 ),
@@ -514,7 +497,6 @@ class ExchangeCardState extends State<ExchangeCard> {
497 Navigator.of(context).pop();
498 },
499 actionLeftButton: () => Navigator.of(dialogContext).pop());
517 - }
518 - );
500 + });
501 }
502 }
lib/src/screens/send/send_template_page.dart
+7 -5
@@ -1,7 +1,5 @@
1 import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
2 -import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
2 import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart';
4 -import 'package:cake_wallet/utils/payment_request.dart';
3 import 'package:cake_wallet/src/widgets/trail_button.dart';
4 import 'package:cake_wallet/view_model/send/template_view_model.dart';
5 import 'package:flutter_mobx/flutter_mobx.dart';
@@ -11,7 +9,6 @@ import 'package:cake_wallet/generated/i18n.dart';
9 import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
10 import 'package:cake_wallet/src/widgets/primary_button.dart';
11 import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
14 -import 'package:cake_wallet/src/screens/send/widgets/prefix_currency_icon_widget.dart';
12 import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
13 import 'package:cake_wallet/src/screens/send/widgets/send_template_card.dart';
14 import 'package:smooth_page_indicator/smooth_page_indicator.dart';
@@ -97,8 +94,13 @@ class SendTemplatePage extends BasePage {
94 radius: 6.0,
95 dotWidth: 6.0,
96 dotHeight: 6.0,
100 - dotColor: Theme.of(context).extension<SendPageTheme>()!.indicatorDotColor,
101 - activeDotColor: Theme.of(context).extension<DashboardPageTheme>()!.indicatorDotTheme.activeIndicatorColor))
97 + dotColor: Theme.of(context)
98 + .extension<SendPageTheme>()!
99 + .indicatorDotColor,
100 + activeDotColor: Theme.of(context)
101 + .extension<DashboardPageTheme>()!
102 + .indicatorDotTheme
103 + .activeIndicatorColor))
104 : Offstage();
105 },
106 ),
lib/src/screens/send/widgets/send_card.dart
+13 -10
@@ -80,15 +80,17 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
80 if (initialPaymentRequest != null &&
81 sendViewModel.walletCurrencyName != initialPaymentRequest!.scheme.toLowerCase()) {
82 WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
83 - showPopUp<void>(
84 - context: context,
85 - builder: (BuildContext context) {
86 - return AlertWithOneAction(
87 - alertTitle: S.of(context).error,
88 - alertContent: S.of(context).unmatched_currencies,
89 - buttonText: S.of(context).ok,
90 - buttonAction: () => Navigator.of(context).pop());
91 - });
83 + if (context.mounted) {
84 + showPopUp<void>(
85 + context: context,
86 + builder: (BuildContext context) {
87 + return AlertWithOneAction(
88 + alertTitle: S.of(context).error,
89 + alertContent: S.of(context).unmatched_currencies,
90 + buttonText: S.of(context).ok,
91 + buttonAction: () => Navigator.of(context).pop());
92 + });
93 + }
94 });
95 }
96 }
@@ -321,7 +323,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
323 ? sendViewModel.allAmountValidator
324 : sendViewModel.amountValidator,
325 ),
324 - if (!sendViewModel.isBatchSending && sendViewModel.shouldDisplaySendALL)
326 + if (!sendViewModel.isBatchSending &&
327 + sendViewModel.shouldDisplaySendALL)
328 Positioned(
329 top: 2,
330 right: 0,
lib/view_model/exchange/exchange_view_model.dart
+21 -6
@@ -59,6 +59,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
59 this.sharedPreferences,
60 this.contactListViewModel,
61 ) : _cryptoNumberFormat = NumberFormat(),
62 + isSendAllEnabled = false,
63 isFixedRateMode = false,
64 isReceiveAmountEntered = false,
65 depositAmount = '',
@@ -145,8 +146,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
146 ChangeNowExchangeProvider(settingsStore: _settingsStore),
147 SideShiftExchangeProvider(),
148 SimpleSwapExchangeProvider(),
148 - TrocadorExchangeProvider(useTorOnly: _useTorOnly,
149 - providerStates: _settingsStore.trocadorProviderStates),
149 + TrocadorExchangeProvider(
150 + useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
151 if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
152 ];
153
@@ -208,6 +209,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
209 @observable
210 bool isFixedRateMode;
211
212 + @observable
213 + bool isSendAllEnabled;
214 +
215 @observable
216 Limits limits;
217
@@ -533,10 +537,14 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
537 }
538
539 @action
536 - void calculateDepositAllAmount() {
537 - if (wallet.type == WalletType.bitcoin ||
538 - wallet.type == WalletType.litecoin ||
539 - wallet.type == WalletType.bitcoinCash) {
540 + void enableSendAllAmount() {
541 + isSendAllEnabled = true;
542 + calculateDepositAllAmount();
543 + }
544 +
545 + @action
546 + Future<void> calculateDepositAllAmount() async {
547 + if (wallet.type == WalletType.litecoin || wallet.type == WalletType.bitcoinCash) {
548 final availableBalance = wallet.balance[wallet.currency]!.available;
549 final priority = _settingsStore.priority[wallet.type]!;
550 final fee = wallet.calculateEstimatedFee(priority, null);
@@ -545,6 +553,13 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
553
554 final amount = availableBalance - fee;
555 changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
556 + } else if (wallet.type == WalletType.bitcoin) {
557 + final priority = _settingsStore.priority[wallet.type]!;
558 +
559 + final amount = await bitcoin!.estimateFakeSendAllTxAmount(
560 + wallet, bitcoin!.deserializeBitcoinTransactionPriority(priority.raw));
561 +
562 + changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
563 }
564 }
565
macos/Podfile.lock
+1 -1
@@ -125,4 +125,4 @@ SPEC CHECKSUMS:
125
126 PODFILE CHECKSUM: 65ec1541137fb5b35d00490dec1bb48d4d9586bb
127
128 -COCOAPODS: 1.12.1
128 +COCOAPODS: 1.15.2
pubspec_base.yaml
+1 -1
@@ -110,7 +110,7 @@ dependencies:
110 bitcoin_base:
111 git:
112 url: https://github.com/cake-tech/bitcoin_base.git
113 - ref: cake-update-v1
113 + ref: cake-update-v2
114
115 dev_dependencies:
116 flutter_test:
scripts/android/app_env.sh
+2 -2
@@ -22,8 +22,8 @@ MONERO_COM_PACKAGE="com.monero.app"
22 MONERO_COM_SCHEME="monero.com"
23
24 CAKEWALLET_NAME="Cake Wallet"
25 -CAKEWALLET_VERSION="4.15.0"
26 -CAKEWALLET_BUILD_NUMBER=198
25 +CAKEWALLET_VERSION="4.15.1"
26 +CAKEWALLET_BUILD_NUMBER=199
27 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
28 CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
29 CAKEWALLET_SCHEME="cakewallet"
scripts/ios/app_env.sh
+2 -2
@@ -18,8 +18,8 @@ MONERO_COM_BUILD_NUMBER=77
18 MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
19
20 CAKEWALLET_NAME="Cake Wallet"
21 -CAKEWALLET_VERSION="4.15.0"
22 -CAKEWALLET_BUILD_NUMBER=217
21 +CAKEWALLET_VERSION="4.15.1"
22 +CAKEWALLET_BUILD_NUMBER=218
23 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
24
25 HAVEN_NAME="Haven"
scripts/macos/app_env.sh
+2 -2
@@ -21,8 +21,8 @@ MONERO_COM_BUILD_NUMBER=10
21 MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
22
23 CAKEWALLET_NAME="Cake Wallet"
24 -CAKEWALLET_VERSION="1.8.0"
25 -CAKEWALLET_BUILD_NUMBER=57
24 +CAKEWALLET_VERSION="1.8.1"
25 +CAKEWALLET_BUILD_NUMBER=58
26 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
27
28 if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then
tool/configure.dart
+1 -1
@@ -70,7 +70,6 @@ import 'package:cw_core/output_info.dart';
70 import 'package:cw_core/unspent_coins_info.dart';
71 import 'package:cw_core/wallet_service.dart';
72 import 'package:cake_wallet/view_model/send/output.dart';
73 -import 'package:cw_core/wallet_type.dart';
73 import 'package:hive/hive.dart';
74 import 'package:bitcoin_base/bitcoin_base.dart';""";
75 const bitcoinCWHeaders = """
@@ -127,6 +126,7 @@ abstract class Bitcoin {
126 List<String> getAddresses(Object wallet);
127 String getAddress(Object wallet);
128
129 + Future<int> estimateFakeSendAllTxAmount(Object wallet, TransactionPriority priority);
130 List<ElectrumSubAddress> getSubAddresses(Object wallet);
131
132 String formatterBitcoinAmountToString({required int amount});