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});