This commit is contained in:
Matthew Fosse 2024-03-21 09:25:02 -07:00
commit 8fac4e68a4
67 changed files with 614 additions and 380 deletions

View file

@ -1,2 +1,4 @@
Monero enhancements
In-App live status page for the app services In-App live status page for the app services
Add Exolix exchange provider
Bug fixes and enhancements Bug fixes and enhancements

View file

@ -1,2 +1 @@
In-App live status page for the app services
Bug fixes and enhancements Bug fixes and enhancements

View file

@ -3,6 +3,9 @@ import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin;
List<int> addressToOutputScript(String address, bitcoin.BasedUtxoNetwork network) { List<int> addressToOutputScript(String address, bitcoin.BasedUtxoNetwork network) {
try { try {
if (network == bitcoin.BitcoinCashNetwork.mainnet) {
return bitcoin.BitcoinCashAddress(address).baseAddress.toScriptPubKey().toBytes();
}
return bitcoin.addressToOutputScript(address: address, network: network); return bitcoin.addressToOutputScript(address: address, network: network);
} catch (err) { } catch (err) {
print(err); print(err);

View file

@ -1,5 +1,4 @@
import 'dart:convert'; import 'dart:convert';
import 'package:bitbox/bitbox.dart' as bitbox;
import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/script_hash.dart' as sh; import 'package:cw_bitcoin/script_hash.dart' as sh;
@ -20,10 +19,9 @@ class BitcoinAddressRecord {
_balance = balance, _balance = balance,
_name = name, _name = name,
_isUsed = isUsed, _isUsed = isUsed,
scriptHash = scriptHash = scriptHash ?? sh.scriptHash(address, network: network);
scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null);
factory BitcoinAddressRecord.fromJSON(String jsonSource, BasedUtxoNetwork? network) { factory BitcoinAddressRecord.fromJSON(String jsonSource, BasedUtxoNetwork network) {
final decoded = json.decode(jsonSource) as Map; final decoded = json.decode(jsonSource) as Map;
return BitcoinAddressRecord( return BitcoinAddressRecord(
@ -39,9 +37,7 @@ class BitcoinAddressRecord {
.firstWhere((type) => type.toString() == decoded['type'] as String) .firstWhere((type) => type.toString() == decoded['type'] as String)
: SegwitAddresType.p2wpkh, : SegwitAddresType.p2wpkh,
scriptHash: decoded['scriptHash'] as String?, scriptHash: decoded['scriptHash'] as String?,
network: (decoded['network'] as String?) == null network: network,
? network
: BasedUtxoNetwork.fromName(decoded['network'] as String),
); );
} }
@ -56,7 +52,7 @@ class BitcoinAddressRecord {
String _name; String _name;
bool _isUsed; bool _isUsed;
String? scriptHash; String? scriptHash;
BasedUtxoNetwork? network; BasedUtxoNetwork network;
int get txCount => _txCount; int get txCount => _txCount;
@ -76,8 +72,6 @@ class BitcoinAddressRecord {
@override @override
int get hashCode => address.hashCode; int get hashCode => address.hashCode;
String get cashAddr => bitbox.Address.toCashAddress(address);
BitcoinAddressType type; BitcoinAddressType type;
String updateScriptHash(BasedUtxoNetwork network) { String updateScriptHash(BasedUtxoNetwork network) {
@ -95,6 +89,5 @@ class BitcoinAddressRecord {
'balance': balance, 'balance': balance,
'type': type.toString(), 'type': type.toString(),
'scriptHash': scriptHash, 'scriptHash': scriptHash,
'network': network?.value,
}); });
} }

View file

@ -93,8 +93,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password, required String password,
}) async { }) async {
final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, final network = walletInfo.network != null
walletInfo.network != null ? BasedUtxoNetwork.fromName(walletInfo.network!) : null); ? BasedUtxoNetwork.fromName(walletInfo.network!)
: BitcoinNetwork.mainnet;
final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network);
return BitcoinWallet( return BitcoinWallet(
mnemonic: snp.mnemonic, mnemonic: snp.mnemonic,
@ -107,7 +109,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialRegularAddressIndex: snp.regularAddressIndex, initialRegularAddressIndex: snp.regularAddressIndex,
initialChangeAddressIndex: snp.changeAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex,
addressPageType: snp.addressPageType, addressPageType: snp.addressPageType,
networkParam: snp.network, networkParam: network,
); );
} }
} }

View file

@ -75,11 +75,7 @@ abstract class ElectrumWalletBase
} }
: {}), : {}),
this.unspentCoinsInfo = unspentCoinsInfo, this.unspentCoinsInfo = unspentCoinsInfo,
this.network = networkType == bitcoin.bitcoin this.network = _getNetwork(networkType, currency),
? BitcoinNetwork.mainnet
: networkType == litecoinNetwork
? LitecoinNetwork.mainnet
: BitcoinNetwork.testnet,
this.isTestnet = networkType == bitcoin.testnet, this.isTestnet = networkType == bitcoin.testnet,
super(walletInfo) { super(walletInfo) {
this.electrumClient = electrumClient ?? ElectrumClient(); this.electrumClient = electrumClient ?? ElectrumClient();
@ -192,12 +188,13 @@ abstract class ElectrumWalletBase
} }
} }
Future<EstimatedTxResult> _estimateTxFeeAndInputsToUse( Future<EstimatedTxResult> estimateTxFeeAndInputsToUse(
int credentialsAmount, int credentialsAmount,
bool sendAll, bool sendAll,
List<BitcoinBaseAddress> outputAddresses, List<BitcoinBaseAddress> outputAddresses,
List<BitcoinOutput> outputs, List<BitcoinOutput> outputs,
BitcoinTransactionCredentials transactionCredentials, int? feeRate,
BitcoinTransactionPriority? priority,
{int? inputsCount}) async { {int? inputsCount}) async {
final utxos = <UtxoWithAddress>[]; final utxos = <UtxoWithAddress>[];
List<ECPrivate> privateKeys = []; List<ECPrivate> privateKeys = [];
@ -212,7 +209,7 @@ abstract class ElectrumWalletBase
allInputsAmount += utx.value; allInputsAmount += utx.value;
leftAmount = leftAmount - utx.value; leftAmount = leftAmount - utx.value;
final address = _addressTypeFromStr(utx.address, network); final address = addressTypeFromStr(utx.address, network);
final privkey = generateECPrivate( final privkey = generateECPrivate(
hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd, hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
index: utx.bitcoinAddressRecord.index, index: utx.bitcoinAddressRecord.index,
@ -249,7 +246,7 @@ abstract class ElectrumWalletBase
if (!sendAll) { if (!sendAll) {
if (changeValue > 0) { if (changeValue > 0) {
final changeAddress = await walletAddresses.getChangeAddress(); final changeAddress = await walletAddresses.getChangeAddress();
final address = _addressTypeFromStr(changeAddress, network); final address = addressTypeFromStr(changeAddress, network);
outputAddresses.add(address); outputAddresses.add(address);
outputs.add(BitcoinOutput(address: address, value: BigInt.from(changeValue))); outputs.add(BitcoinOutput(address: address, value: BigInt.from(changeValue)));
} }
@ -258,9 +255,9 @@ abstract class ElectrumWalletBase
final estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize( final estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize(
utxos: utxos, outputs: outputs, network: network); utxos: utxos, outputs: outputs, network: network);
final fee = transactionCredentials.feeRate != null int fee = feeRate != null
? feeAmountWithFeeRate(transactionCredentials.feeRate!, 0, 0, size: estimatedSize) ? feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize)
: feeAmountForPriority(transactionCredentials.priority!, 0, 0, size: estimatedSize); : feeAmountForPriority(priority!, 0, 0, size: estimatedSize);
if (fee == 0) { if (fee == 0) {
throw BitcoinTransactionWrongBalanceException(currency); throw BitcoinTransactionWrongBalanceException(currency);
@ -297,8 +294,8 @@ abstract class ElectrumWalletBase
outputs.removeLast(); outputs.removeLast();
} }
return _estimateTxFeeAndInputsToUse( return estimateTxFeeAndInputsToUse(
credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials, credentialsAmount, sendAll, outputAddresses, outputs, feeRate, priority,
inputsCount: utxos.length + 1); inputsCount: utxos.length + 1);
} }
} }
@ -319,7 +316,7 @@ abstract class ElectrumWalletBase
for (final out in transactionCredentials.outputs) { for (final out in transactionCredentials.outputs) {
final outputAddress = out.isParsedAddress ? out.extractedAddress! : out.address; final outputAddress = out.isParsedAddress ? out.extractedAddress! : out.address;
final address = _addressTypeFromStr(outputAddress, network); final address = addressTypeFromStr(outputAddress, network);
outputAddresses.add(address); outputAddresses.add(address);
@ -344,8 +341,14 @@ abstract class ElectrumWalletBase
} }
} }
final estimatedTx = await _estimateTxFeeAndInputsToUse( final estimatedTx = await estimateTxFeeAndInputsToUse(
credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials); credentialsAmount,
sendAll,
outputAddresses,
outputs,
transactionCredentials.feeRate,
transactionCredentials.priority,
);
final txb = BitcoinTransactionBuilder( final txb = BitcoinTransactionBuilder(
utxos: estimatedTx.utxos, utxos: estimatedTx.utxos,
@ -391,7 +394,6 @@ abstract class ElectrumWalletBase
? SegwitAddresType.p2wpkh.toString() ? SegwitAddresType.p2wpkh.toString()
: walletInfo.addressPageType.toString(), : walletInfo.addressPageType.toString(),
'balance': balance[currency]?.toJSON(), 'balance': balance[currency]?.toJSON(),
'network_type': network == BitcoinNetwork.testnet ? 'testnet' : 'mainnet',
}); });
int feeRate(TransactionPriority priority) { int feeRate(TransactionPriority priority) {
@ -852,6 +854,22 @@ abstract class ElectrumWalletBase
final HD = index == null ? hd : hd.derive(index); final HD = index == null ? hd : hd.derive(index);
return base64Encode(HD.signMessage(message)); return base64Encode(HD.signMessage(message));
} }
static BasedUtxoNetwork _getNetwork(bitcoin.NetworkType networkType, CryptoCurrency? currency) {
if (networkType == bitcoin.bitcoin && currency == CryptoCurrency.bch) {
return BitcoinCashNetwork.mainnet;
}
if (networkType == litecoinNetwork) {
return LitecoinNetwork.mainnet;
}
if (networkType == bitcoin.testnet) {
return BitcoinNetwork.testnet;
}
return BitcoinNetwork.mainnet;
}
} }
class EstimateTxParams { class EstimateTxParams {
@ -879,7 +897,7 @@ class EstimatedTxResult {
final int amount; final int amount;
} }
BitcoinBaseAddress _addressTypeFromStr(String address, BasedUtxoNetwork network) { BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
if (P2pkhAddress.regex.hasMatch(address)) { if (P2pkhAddress.regex.hasMatch(address)) {
return P2pkhAddress.fromAddress(address: address, network: network); return P2pkhAddress.fromAddress(address: address, network: network);
} else if (P2shAddress.regex.hasMatch(address)) { } else if (P2shAddress.regex.hasMatch(address)) {

View file

@ -1,6 +1,5 @@
import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:bitbox/bitbox.dart' as bitbox;
import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/electrum.dart'; import 'package:cw_bitcoin/electrum.dart';
import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_addresses.dart';
@ -30,6 +29,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
Map<String, int>? initialRegularAddressIndex, Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex, Map<String, int>? initialChangeAddressIndex,
BitcoinAddressType? initialAddressPageType,
}) : _addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()), }) : _addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
addressesByReceiveType = addressesByReceiveType =
ObservableList<BitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()), ObservableList<BitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()),
@ -41,9 +41,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
.toSet()), .toSet()),
currentReceiveAddressIndexByType = initialRegularAddressIndex ?? {}, currentReceiveAddressIndexByType = initialRegularAddressIndex ?? {},
currentChangeAddressIndexByType = initialChangeAddressIndex ?? {}, currentChangeAddressIndexByType = initialChangeAddressIndex ?? {},
_addressPageType = walletInfo.addressPageType != null _addressPageType = initialAddressPageType ??
(walletInfo.addressPageType != null
? BitcoinAddressType.fromValue(walletInfo.addressPageType!) ? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
: SegwitAddresType.p2wpkh, : SegwitAddresType.p2wpkh),
super(walletInfo) { super(walletInfo) {
updateAddressesByMatch(); updateAddressesByMatch();
} }
@ -52,10 +53,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
static const defaultChangeAddressesCount = 17; static const defaultChangeAddressesCount = 17;
static const gap = 20; static const gap = 20;
static String toCashAddr(String address) => bitbox.Address.toCashAddress(address);
static String toLegacy(String address) => bitbox.Address.toLegacyAddress(address);
final ObservableList<BitcoinAddressRecord> _addresses; final ObservableList<BitcoinAddressRecord> _addresses;
// Matched by addressPageType // Matched by addressPageType
late ObservableList<BitcoinAddressRecord> addressesByReceiveType; late ObservableList<BitcoinAddressRecord> addressesByReceiveType;
@ -67,7 +64,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final bitcoin.HDWallet sideHd; final bitcoin.HDWallet sideHd;
@observable @observable
BitcoinAddressType _addressPageType = SegwitAddresType.p2wpkh; late BitcoinAddressType _addressPageType;
@computed @computed
BitcoinAddressType get addressPageType => _addressPageType; BitcoinAddressType get addressPageType => _addressPageType;
@ -97,7 +94,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
} }
} }
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(receiveAddress) : receiveAddress; return receiveAddress;
} }
@observable @observable
@ -105,9 +102,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@override @override
set address(String addr) { set address(String addr) {
if (addr.startsWith('bitcoincash:')) {
addr = toLegacy(addr);
}
final addressRecord = _addresses.firstWhere((addressRecord) => addressRecord.address == addr); final addressRecord = _addresses.firstWhere((addressRecord) => addressRecord.address == addr);
previousAddressRecord = addressRecord; previousAddressRecord = addressRecord;
@ -155,11 +149,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@override @override
Future<void> init() async { Future<void> init() async {
if (walletInfo.type == WalletType.bitcoinCash) {
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
} else if (walletInfo.type == WalletType.litecoin) {
await _generateInitialAddresses();
} else if (walletInfo.type == WalletType.bitcoin) {
await _generateInitialAddresses(); await _generateInitialAddresses();
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh); await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
await _generateInitialAddresses(type: SegwitAddresType.p2tr); await _generateInitialAddresses(type: SegwitAddresType.p2tr);
await _generateInitialAddresses(type: SegwitAddresType.p2wsh); await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
}
updateAddressesByMatch(); updateAddressesByMatch();
updateReceiveAddresses(); updateReceiveAddresses();
updateChangeAddresses(); updateChangeAddresses();
@ -220,7 +220,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
Future<void> updateAddressesInBox() async { Future<void> updateAddressesInBox() async {
try { try {
addressesMap.clear(); addressesMap.clear();
addressesMap[address] = ''; _addresses.forEach((addressRecord) {
addressesMap[addressRecord.address] = addressRecord.name;
});
await saveAddressesInBox(); await saveAddressesInBox();
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
@ -229,9 +231,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@action @action
void updateAddress(String address, String label) { void updateAddress(String address, String label) {
if (address.startsWith('bitcoincash:')) {
address = toLegacy(address);
}
final addressRecord = final addressRecord =
_addresses.firstWhere((addressRecord) => addressRecord.address == address); _addresses.firstWhere((addressRecord) => addressRecord.address == address);
addressRecord.setNewName(label); addressRecord.setNewName(label);
@ -261,7 +260,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
addressRecord.isHidden && addressRecord.isHidden &&
!addressRecord.isUsed && !addressRecord.isUsed &&
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type // TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
addressRecord.type == SegwitAddresType.p2wpkh); (walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddresType.p2wpkh));
changeAddresses.addAll(newAddresses); changeAddresses.addAll(newAddresses);
} }

View file

@ -17,14 +17,12 @@ class ElectrumWalletSnapshot {
required this.regularAddressIndex, required this.regularAddressIndex,
required this.changeAddressIndex, required this.changeAddressIndex,
required this.addressPageType, required this.addressPageType,
required this.network,
}); });
final String name; final String name;
final String password; final String password;
final WalletType type; final WalletType type;
final String addressPageType; final String? addressPageType;
final BasedUtxoNetwork network;
String mnemonic; String mnemonic;
List<BitcoinAddressRecord> addresses; List<BitcoinAddressRecord> addresses;
@ -32,7 +30,8 @@ class ElectrumWalletSnapshot {
Map<String, int> regularAddressIndex; Map<String, int> regularAddressIndex;
Map<String, int> changeAddressIndex; Map<String, int> changeAddressIndex;
static Future<ElectrumWalletSnapshot> load(String name, WalletType type, String password, BasedUtxoNetwork? network) async { static Future<ElectrumWalletSnapshot> load(
String name, WalletType type, String password, BasedUtxoNetwork network) async {
final path = await pathForWallet(name: name, type: type); final path = await pathForWallet(name: name, type: type);
final jsonSource = await read(path: path, password: password); final jsonSource = await read(path: path, password: password);
final data = json.decode(jsonSource) as Map; final data = json.decode(jsonSource) as Map;
@ -71,8 +70,7 @@ class ElectrumWalletSnapshot {
balance: balance, balance: balance,
regularAddressIndex: regularAddressIndexByType, regularAddressIndex: regularAddressIndexByType,
changeAddressIndex: changeAddressIndexByType, changeAddressIndex: changeAddressIndexByType,
addressPageType: data['address_page_type'] as String? ?? SegwitAddresType.p2wpkh.toString(), addressPageType: data['address_page_type'] as String?,
network: data['network_type'] == 'testnet' ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet,
); );
} }
} }

View file

@ -1,8 +1,9 @@
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:cw_bitcoin/address_to_output_script.dart';
import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin;
String scriptHash(String address, {required BasedUtxoNetwork network}) { String scriptHash(String address, {required bitcoin.BasedUtxoNetwork network}) {
final outputScript = addressToOutputScript(address: address, network: network); final outputScript = addressToOutputScript(address, network);
final parts = sha256.convert(outputScript).toString().split(''); final parts = sha256.convert(outputScript).toString().split('');
var res = ''; var res = '';

View file

@ -79,11 +79,11 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: cake-update-v1 ref: cake-update-v2
resolved-ref: "9611e9db77e92a8434e918cdfb620068f6fcb1aa" resolved-ref: "3fd81d238b990bb767fc7a4fdd5053a22a142e2e"
url: "https://github.com/cake-tech/bitcoin_base.git" url: "https://github.com/cake-tech/bitcoin_base.git"
source: git source: git
version: "4.0.0" version: "4.2.0"
bitcoin_flutter: bitcoin_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
@ -97,10 +97,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: blockchain_utils name: blockchain_utils
sha256: "9701dfaa74caad4daae1785f1ec4445cf7fb94e45620bc3a4aca1b9b281dc6c9" sha256: "38ef5f4a22441ac4370aed9071dc71c460acffc37c79b344533f67d15f24c13c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.6.0" version: "2.1.1"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:

View file

@ -37,8 +37,8 @@ dependencies:
bitcoin_base: bitcoin_base:
git: git:
url: https://github.com/cake-tech/bitcoin_base.git url: https://github.com/cake-tech/bitcoin_base.git
ref: cake-update-v1 ref: cake-update-v2
blockchain_utils: ^1.6.0 blockchain_utils: ^2.1.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View file

@ -34,7 +34,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
required WalletInfo walletInfo, required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes, required Uint8List seedBytes,
String? addressPageType, BitcoinAddressType? addressPageType,
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
Map<String, int>? initialRegularAddressIndex, Map<String, int>? initialRegularAddressIndex,
@ -59,6 +59,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
mainHd: hd, mainHd: hd,
sideHd: bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/1"), sideHd: bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/1"),
network: network, network: network,
initialAddressPageType: addressPageType,
); );
autorun((_) { autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
@ -85,7 +86,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
seedBytes: await Mnemonic.toSeed(mnemonic), seedBytes: await Mnemonic.toSeed(mnemonic),
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex,
addressPageType: addressPageType, addressPageType: P2pkhAddressType.p2pkh,
); );
} }
@ -102,12 +103,31 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp.addresses, initialAddresses: snp.addresses.map((addr) {
try {
BitcoinCashAddress(addr.address);
return BitcoinAddressRecord(
addr.address,
index: addr.index,
isHidden: addr.isHidden,
type: P2pkhAddressType.p2pkh,
network: BitcoinCashNetwork.mainnet,
);
} catch (_) {
return BitcoinAddressRecord(
AddressUtils.getCashAddrFormat(addr.address),
index: addr.index,
isHidden: addr.isHidden,
type: P2pkhAddressType.p2pkh,
network: BitcoinCashNetwork.mainnet,
);
}
}).toList(),
initialBalance: snp.balance, initialBalance: snp.balance,
seedBytes: await Mnemonic.toSeed(snp.mnemonic), seedBytes: await Mnemonic.toSeed(snp.mnemonic),
initialRegularAddressIndex: snp.regularAddressIndex, initialRegularAddressIndex: snp.regularAddressIndex,
initialChangeAddressIndex: snp.changeAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex,
addressPageType: snp.addressPageType, addressPageType: P2pkhAddressType.p2pkh,
); );
} }

View file

@ -19,6 +19,7 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
super.initialAddresses, super.initialAddresses,
super.initialRegularAddressIndex, super.initialRegularAddressIndex,
super.initialChangeAddressIndex, super.initialChangeAddressIndex,
super.initialAddressPageType,
}) : super(walletInfo); }) : super(walletInfo);
@override @override

View file

@ -32,7 +32,7 @@ dependencies:
bitcoin_base: bitcoin_base:
git: git:
url: https://github.com/cake-tech/bitcoin_base.git url: https://github.com/cake-tech/bitcoin_base.git
ref: cake-update-v1 ref: cake-update-v2

View file

@ -574,13 +574,7 @@ abstract class MoneroWalletBase
int height = 0; int height = 0;
try { try {
height = _getHeightByDate(walletInfo.date); height = _getHeightByDate(walletInfo.date);
} catch (e, s) { } catch (_) {}
onError?.call(FlutterErrorDetails(
exception: e,
stack: s,
library: this.runtimeType.toString(),
));
}
monero_wallet.setRecoveringFromSeed(isRecovery: true); monero_wallet.setRecoveringFromSeed(isRecovery: true);
monero_wallet.setRefreshFromBlockHeight(height: height); monero_wallet.setRefreshFromBlockHeight(height: height);

View file

@ -277,7 +277,7 @@ SPEC CHECKSUMS:
flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0 flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 fluttertoast: 48c57db1b71b0ce9e6bba9f31c940ff4b001293c
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
local_auth_ios: 1ba1475238daa33a6ffa2a29242558437be435ac local_auth_ios: 1ba1475238daa33a6ffa2a29242558437be435ac
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
@ -302,4 +302,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: fcb1b8418441a35b438585c9dd8374e722e6c6ca PODFILE CHECKSUM: fcb1b8418441a35b438585c9dd8374e722e6c6ca
COCOAPODS: 1.12.1 COCOAPODS: 1.15.2

View file

@ -113,13 +113,39 @@ class CWBitcoin extends Bitcoin {
.map((BitcoinAddressRecord addr) => ElectrumSubAddress( .map((BitcoinAddressRecord addr) => ElectrumSubAddress(
id: addr.index, id: addr.index,
name: addr.name, name: addr.name,
address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address, address: addr.address,
txCount: addr.txCount, txCount: addr.txCount,
balance: addr.balance, balance: addr.balance,
isChange: addr.isHidden)) isChange: addr.isHidden))
.toList(); .toList();
} }
@override
Future<int> estimateFakeSendAllTxAmount(Object wallet, TransactionPriority priority) async {
final electrumWallet = wallet as ElectrumWallet;
final sk = ECPrivate.random();
final p2shAddr = sk.getPublic().toP2pkhInP2sh();
final p2wpkhAddr = sk.getPublic().toP2wpkhAddress();
try {
final estimatedTx = await electrumWallet.estimateTxFeeAndInputsToUse(
0,
true,
// Deposit address + change address
[p2shAddr, p2wpkhAddr],
[
BitcoinOutput(address: p2shAddr, value: BigInt.zero),
BitcoinOutput(address: p2wpkhAddr, value: BigInt.zero)
],
null,
priority as BitcoinTransactionPriority);
return estimatedTx.amount;
} catch (_) {
return 0;
}
}
@override @override
String getAddress(Object wallet) { String getAddress(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet; final bitcoinWallet = wallet as ElectrumWallet;

View file

@ -81,7 +81,7 @@ class MoonPaySellProvider extends BuyProvider {
'', '',
<String, dynamic>{ <String, dynamic>{
'apiKey': _apiKey, 'apiKey': _apiKey,
'defaultBaseCurrencyCode': currency.toString().toLowerCase(), 'defaultBaseCurrencyCode': _normalizeCurrency(currency),
'refundWalletAddress': refundWalletAddress, 'refundWalletAddress': refundWalletAddress,
}..addAll(customParams), }..addAll(customParams),
); );
@ -134,6 +134,14 @@ class MoonPaySellProvider extends BuyProvider {
); );
} }
} }
String _normalizeCurrency(CryptoCurrency currency) {
if (currency == CryptoCurrency.maticpoly) {
return "MATIC_POLYGON";
}
return currency.toString().toLowerCase();
}
} }
class MoonPayBuyProvider extends BuyProvider { class MoonPayBuyProvider extends BuyProvider {

View file

@ -276,7 +276,6 @@ class AddressValidator extends TextValidator {
'|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{40,80})([^0-9a-zA-Z]|\$)' //P2wshAddress type '|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{40,80})([^0-9a-zA-Z]|\$)' //P2wshAddress type
'|([^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 '|([^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
case CryptoCurrency.btcln: case CryptoCurrency.btcln:
// bolt11:
return '(lnbc|LNBC)([0-9]{1,}[a-zA-Z0-9]+)([^0-9a-zA-Z]|\$)'; return '(lnbc|LNBC)([0-9]{1,}[a-zA-Z0-9]+)([^0-9a-zA-Z]|\$)';
case CryptoCurrency.ltc: case CryptoCurrency.ltc:
return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)' return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'

View file

@ -35,6 +35,7 @@ const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002';
const nanoDefaultNodeUri = 'rpc.nano.to'; const nanoDefaultNodeUri = 'rpc.nano.to';
const nanoDefaultPowNodeUri = 'rpc.nano.to'; const nanoDefaultPowNodeUri = 'rpc.nano.to';
const solanaDefaultNodeUri = 'rpc.ankr.com'; const solanaDefaultNodeUri = 'rpc.ankr.com';
const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002';
Future<void> defaultSettingsMigration( Future<void> defaultSettingsMigration(
{required int version, {required int version,
@ -201,9 +202,15 @@ Future<void> defaultSettingsMigration(
await changeSolanaCurrentNodeToDefault( await changeSolanaCurrentNodeToDefault(
sharedPreferences: sharedPreferences, nodes: nodes); sharedPreferences: sharedPreferences, nodes: nodes);
break; break;
case 28: case 28:
await _updateMoneroPriority(sharedPreferences); await _updateMoneroPriority(sharedPreferences);
break; break;
case 29:
await changeDefaultBitcoinNode(nodes, sharedPreferences);
break;
default: default:
break; break;
} }
@ -702,6 +709,26 @@ Future<void> changeDefaultMoneroNode(
} }
} }
Future<void> changeDefaultBitcoinNode(
Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
const cakeWalletBitcoinNodeUriPattern = '.cakewallet.com';
final currentBitcoinNodeId =
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
final currentBitcoinNode =
nodeSource.values.firstWhere((node) => node.key == currentBitcoinNodeId);
final needToReplaceCurrentBitcoinNode =
currentBitcoinNode.uri.toString().contains(cakeWalletBitcoinNodeUriPattern);
final newCakeWalletBitcoinNode = Node(uri: newCakeWalletBitcoinUri, type: WalletType.bitcoin);
await nodeSource.add(newCakeWalletBitcoinNode);
if (needToReplaceCurrentBitcoinNode) {
await sharedPreferences.setInt(
PreferencesKey.currentBitcoinElectrumSererIdKey, newCakeWalletBitcoinNode.key as int);
}
}
Future<void> checkCurrentNodes( Future<void> checkCurrentNodes(
Box<Node> nodeSource, Box<Node> powNodeSource, SharedPreferences sharedPreferences) async { Box<Node> nodeSource, Box<Node> powNodeSource, SharedPreferences sharedPreferences) async {
final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey); final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);

View file

@ -90,7 +90,12 @@ class ProvidersHelper {
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
return [ProviderType.askEachTime, ProviderType.moonpaySell]; return [ProviderType.askEachTime, ProviderType.moonpaySell];
case WalletType.polygon: case WalletType.polygon:
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.dfx]; return [
ProviderType.askEachTime,
ProviderType.onramper,
ProviderType.moonpaySell,
ProviderType.dfx,
];
case WalletType.solana: case WalletType.solana:
return [ return [
ProviderType.askEachTime, ProviderType.askEachTime,

View file

@ -66,6 +66,7 @@ class ExolixExchangeProvider extends ExchangeProvider {
final params = <String, String>{ final params = <String, String>{
'rateType': _getRateType(isFixedRateMode), 'rateType': _getRateType(isFixedRateMode),
'amount': '1', 'amount': '1',
'apiToken': apiKey,
}; };
if (isFixedRateMode) { if (isFixedRateMode) {
params['coinFrom'] = _normalizeCurrency(to); params['coinFrom'] = _normalizeCurrency(to);

View file

@ -163,7 +163,7 @@ Future<void> initializeAppConfigs() async {
transactionDescriptions: transactionDescriptions, transactionDescriptions: transactionDescriptions,
secureStorage: secureStorage, secureStorage: secureStorage,
anonpayInvoiceInfo: anonpayInvoiceInfo, anonpayInvoiceInfo: anonpayInvoiceInfo,
initialMigrationVersion: 28); initialMigrationVersion: 29);
} }
Future<void> initialSetup( Future<void> initialSetup(

View file

@ -384,7 +384,7 @@ class ExchangePage extends BasePage {
(CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey)); (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey));
reaction((_) => exchangeViewModel.depositAmount, (String amount) { reaction((_) => exchangeViewModel.depositAmount, (String amount) {
if (depositKey.currentState!.amountController.text != amount) { if (depositKey.currentState!.amountController.text != amount && amount != S.of(context).all) {
depositKey.currentState!.amountController.text = amount; depositKey.currentState!.amountController.text = amount;
} }
}); });
@ -467,7 +467,9 @@ class ExchangePage extends BasePage {
.addListener(() => exchangeViewModel.depositAddress = depositAddressController.text); .addListener(() => exchangeViewModel.depositAddress = depositAddressController.text);
depositAmountController.addListener(() { depositAmountController.addListener(() {
if (depositAmountController.text != exchangeViewModel.depositAmount) { if (depositAmountController.text != exchangeViewModel.depositAmount &&
depositAmountController.text != S.of(context).all) {
exchangeViewModel.isSendAllEnabled = false;
_depositAmountDebounce.run(() { _depositAmountDebounce.run(() {
exchangeViewModel.changeDepositAmount(amount: depositAmountController.text); exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
exchangeViewModel.isReceiveAmountEntered = false; exchangeViewModel.isReceiveAmountEntered = false;
@ -589,8 +591,9 @@ class ExchangePage extends BasePage {
onDispose: disposeBestRateSync, onDispose: disposeBestRateSync,
hasAllAmount: exchangeViewModel.hasAllAmount, hasAllAmount: exchangeViewModel.hasAllAmount,
allAmount: exchangeViewModel.hasAllAmount allAmount: exchangeViewModel.hasAllAmount
? () => exchangeViewModel.calculateDepositAllAmount() ? () => exchangeViewModel.enableSendAllAmount()
: null, : null,
isAllAmountEnabled: exchangeViewModel.isSendAllEnabled,
amountFocusNode: _depositAmountFocus, amountFocusNode: _depositAmountFocus,
addressFocusNode: _depositAddressFocus, addressFocusNode: _depositAddressFocus,
key: depositKey, key: depositKey,
@ -627,10 +630,12 @@ class ExchangePage extends BasePage {
}, },
imageArrow: arrowBottomPurple, imageArrow: arrowBottomPurple,
currencyButtonColor: Colors.transparent, currencyButtonColor: Colors.transparent,
addressButtonsColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor, addressButtonsColor:
borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderTopPanelColor, Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
borderColor:
Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderTopPanelColor,
currencyValueValidator: (value) { currencyValueValidator: (value) {
return !exchangeViewModel.isFixedRateMode return !exchangeViewModel.isFixedRateMode && value != S.of(context).all
? AmountValidator( ? AmountValidator(
isAutovalidate: true, isAutovalidate: true,
currency: exchangeViewModel.depositCurrency, currency: exchangeViewModel.depositCurrency,
@ -674,8 +679,10 @@ class ExchangePage extends BasePage {
exchangeViewModel.changeReceiveCurrency(currency: currency), exchangeViewModel.changeReceiveCurrency(currency: currency),
imageArrow: arrowBottomCakeGreen, imageArrow: arrowBottomCakeGreen,
currencyButtonColor: Colors.transparent, currencyButtonColor: Colors.transparent,
addressButtonsColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor, addressButtonsColor:
borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor, Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
borderColor:
Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor,
currencyValueValidator: (value) { currencyValueValidator: (value) {
return exchangeViewModel.isFixedRateMode return exchangeViewModel.isFixedRateMode
? AmountValidator( ? AmountValidator(

View file

@ -56,17 +56,14 @@ class ExchangeTemplatePage extends BasePage {
height: 8, height: 8,
); );
final depositWalletName = final depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
exchangeViewModel.depositCurrency == CryptoCurrency.xmr
? exchangeViewModel.wallet.name ? exchangeViewModel.wallet.name
: null; : null;
final receiveWalletName = final receiveWalletName = exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
? exchangeViewModel.wallet.name ? exchangeViewModel.wallet.name
: null; : null;
WidgetsBinding.instance WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
return KeyboardActions( return KeyboardActions(
disableScroll: true, disableScroll: true,
@ -76,11 +73,9 @@ class ExchangeTemplatePage extends BasePage {
nextFocus: false, nextFocus: false,
actions: [ actions: [
KeyboardActionsItem( KeyboardActionsItem(
focusNode: _depositAmountFocus, focusNode: _depositAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()]),
toolbarButtons: [(_) => KeyboardDoneButton()]),
KeyboardActionsItem( KeyboardActionsItem(
focusNode: _receiveAmountFocus, focusNode: _receiveAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()])
toolbarButtons: [(_) => KeyboardDoneButton()])
]), ]),
child: Container( child: Container(
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
@ -92,17 +87,18 @@ class ExchangeTemplatePage extends BasePage {
padding: EdgeInsets.only(bottom: 32), padding: EdgeInsets.only(bottom: 32),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
bottomRight: Radius.circular(24) gradient: LinearGradient(colors: [
), Theme.of(context)
gradient: LinearGradient( .extension<ExchangePageTheme>()!
colors: [ .firstGradientBottomPanelColor,
Theme.of(context).extension<ExchangePageTheme>()!.firstGradientBottomPanelColor, Theme.of(context)
Theme.of(context).extension<ExchangePageTheme>()!.secondGradientBottomPanelColor, .extension<ExchangePageTheme>()!
], .secondGradientBottomPanelColor,
stops: [0.35, 1.0], ], stops: [
begin: Alignment.topLeft, 0.35,
end: Alignment.bottomRight), 1.0
], begin: Alignment.topLeft, end: Alignment.bottomRight),
), ),
child: FocusTraversalGroup( child: FocusTraversalGroup(
policy: OrderedTraversalPolicy(), policy: OrderedTraversalPolicy(),
@ -112,15 +108,15 @@ class ExchangeTemplatePage extends BasePage {
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24) bottomRight: Radius.circular(24)),
), gradient: LinearGradient(colors: [
gradient: LinearGradient( Theme.of(context)
colors: [ .extension<ExchangePageTheme>()!
Theme.of(context).extension<ExchangePageTheme>()!.firstGradientTopPanelColor, .firstGradientTopPanelColor,
Theme.of(context).extension<ExchangePageTheme>()!.secondGradientTopPanelColor, Theme.of(context)
], .extension<ExchangePageTheme>()!
begin: Alignment.topLeft, .secondGradientTopPanelColor,
end: Alignment.bottomRight), ], begin: Alignment.topLeft, end: Alignment.bottomRight),
), ),
padding: EdgeInsets.fromLTRB(24, 100, 24, 32), padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
child: Observer( child: Observer(
@ -128,31 +124,30 @@ class ExchangeTemplatePage extends BasePage {
amountFocusNode: _depositAmountFocus, amountFocusNode: _depositAmountFocus,
key: depositKey, key: depositKey,
title: S.of(context).you_will_send, title: S.of(context).you_will_send,
initialCurrency: initialCurrency: exchangeViewModel.depositCurrency,
exchangeViewModel.depositCurrency,
initialWalletName: depositWalletName ?? '', initialWalletName: depositWalletName ?? '',
initialAddress: exchangeViewModel initialAddress: exchangeViewModel.depositCurrency ==
.depositCurrency ==
exchangeViewModel.wallet.currency exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.walletAddresses.address ? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.depositAddress, : exchangeViewModel.depositAddress,
initialIsAmountEditable: true, initialIsAmountEditable: true,
initialIsAddressEditable: exchangeViewModel initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
.isDepositAddressEnabled,
isAmountEstimated: false, isAmountEstimated: false,
hasRefundAddress: true, hasRefundAddress: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet, isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: CryptoCurrency.all, currencies: CryptoCurrency.all,
onCurrencySelected: (currency) => onCurrencySelected: (currency) =>
exchangeViewModel.changeDepositCurrency( exchangeViewModel.changeDepositCurrency(currency: currency),
currency: currency),
imageArrow: arrowBottomPurple, imageArrow: arrowBottomPurple,
currencyButtonColor: Colors.transparent, currencyButtonColor: Colors.transparent,
addressButtonsColor: addressButtonsColor: Theme.of(context)
Theme.of(context).extension<ExchangePageTheme>()!.textFieldButtonColor, .extension<ExchangePageTheme>()!
borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor, .textFieldButtonColor,
currencyValueValidator: AmountValidator( borderColor: Theme.of(context)
currency: exchangeViewModel.depositCurrency), .extension<ExchangePageTheme>()!
.textFieldBorderBottomPanelColor,
currencyValueValidator:
AmountValidator(currency: exchangeViewModel.depositCurrency),
//addressTextFieldValidator: AddressValidator( //addressTextFieldValidator: AddressValidator(
// type: exchangeViewModel.depositCurrency), // type: exchangeViewModel.depositCurrency),
), ),
@ -165,11 +160,9 @@ class ExchangeTemplatePage extends BasePage {
amountFocusNode: _receiveAmountFocus, amountFocusNode: _receiveAmountFocus,
key: receiveKey, key: receiveKey,
title: S.of(context).you_will_get, title: S.of(context).you_will_get,
initialCurrency: initialCurrency: exchangeViewModel.receiveCurrency,
exchangeViewModel.receiveCurrency,
initialWalletName: receiveWalletName ?? '', initialWalletName: receiveWalletName ?? '',
initialAddress: initialAddress: exchangeViewModel.receiveCurrency ==
exchangeViewModel.receiveCurrency ==
exchangeViewModel.wallet.currency exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.walletAddresses.address ? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress, : exchangeViewModel.receiveAddress,
@ -177,14 +170,16 @@ class ExchangeTemplatePage extends BasePage {
isAmountEstimated: true, isAmountEstimated: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet, isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: exchangeViewModel.receiveCurrencies, currencies: exchangeViewModel.receiveCurrencies,
onCurrencySelected: (currency) => onCurrencySelected: (currency) => exchangeViewModel
exchangeViewModel.changeReceiveCurrency( .changeReceiveCurrency(currency: currency),
currency: currency),
imageArrow: arrowBottomCakeGreen, imageArrow: arrowBottomCakeGreen,
currencyButtonColor: Colors.transparent, currencyButtonColor: Colors.transparent,
addressButtonsColor: addressButtonsColor: Theme.of(context)
Theme.of(context).extension<ExchangePageTheme>()!.textFieldButtonColor, .extension<ExchangePageTheme>()!
borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor, .textFieldButtonColor,
borderColor: Theme.of(context)
.extension<ExchangePageTheme>()!
.textFieldBorderBottomPanelColor,
currencyValueValidator: AmountValidator( currencyValueValidator: AmountValidator(
currency: exchangeViewModel.receiveCurrency), currency: exchangeViewModel.receiveCurrency),
//addressTextFieldValidator: AddressValidator( //addressTextFieldValidator: AddressValidator(
@ -195,8 +190,7 @@ class ExchangeTemplatePage extends BasePage {
), ),
), ),
), ),
bottomSectionPadding: bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Column(children: <Widget>[ bottomSection: Column(children: <Widget>[
Padding( Padding(
padding: EdgeInsets.only(bottom: 15), padding: EdgeInsets.only(bottom: 15),
@ -221,14 +215,12 @@ class ExchangeTemplatePage extends BasePage {
if (_formKey.currentState != null && _formKey.currentState!.validate()) { if (_formKey.currentState != null && _formKey.currentState!.validate()) {
exchangeViewModel.addTemplate( exchangeViewModel.addTemplate(
amount: exchangeViewModel.depositAmount, amount: exchangeViewModel.depositAmount,
depositCurrency: depositCurrency: exchangeViewModel.depositCurrency.name,
exchangeViewModel.depositCurrency.name, depositCurrencyTitle: exchangeViewModel.depositCurrency.title +
depositCurrencyTitle: exchangeViewModel ' ${exchangeViewModel.depositCurrency.tag ?? ''}',
.depositCurrency.title + ' ${exchangeViewModel.depositCurrency.tag ?? ''}', receiveCurrency: exchangeViewModel.receiveCurrency.name,
receiveCurrency: receiveCurrencyTitle: exchangeViewModel.receiveCurrency.title +
exchangeViewModel.receiveCurrency.name, ' ${exchangeViewModel.receiveCurrency.tag ?? ''}',
receiveCurrencyTitle: exchangeViewModel
.receiveCurrency.title + ' ${exchangeViewModel.receiveCurrency.tag ?? ''}',
provider: exchangeViewModel.provider.toString(), provider: exchangeViewModel.provider.toString(),
depositAddress: exchangeViewModel.depositAddress, depositAddress: exchangeViewModel.depositAddress,
receiveAddress: exchangeViewModel.receiveAddress); receiveAddress: exchangeViewModel.receiveAddress);
@ -240,13 +232,10 @@ class ExchangeTemplatePage extends BasePage {
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
textColor: Colors.white), textColor: Colors.white),
]), ]),
)) ))));
)
);
} }
void _setReactions( void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) {
BuildContext context, ExchangeViewModel exchangeViewModel) {
if (_isReactionsSet) { if (_isReactionsSet) {
return; return;
} }
@ -272,33 +261,27 @@ class ExchangeTemplatePage extends BasePage {
// key.currentState.changeLimits(min: min, max: max); // key.currentState.changeLimits(min: min, max: max);
// } // }
_onCurrencyChange( _onCurrencyChange(exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey); _onCurrencyChange(exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
_onCurrencyChange(
exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
reaction( reaction(
(_) => exchangeViewModel.wallet.name, (_) => exchangeViewModel.wallet.name,
(String _) => _onWalletNameChange( (String _) =>
exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey)); _onWalletNameChange(exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
reaction( reaction(
(_) => exchangeViewModel.wallet.name, (_) => exchangeViewModel.wallet.name,
(String _) => _onWalletNameChange( (String _) =>
exchangeViewModel, exchangeViewModel.depositCurrency, depositKey)); _onWalletNameChange(exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
reaction( reaction((_) => exchangeViewModel.receiveCurrency,
(_) => exchangeViewModel.receiveCurrency, (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, receiveKey));
(CryptoCurrency currency) =>
_onCurrencyChange(currency, exchangeViewModel, receiveKey));
reaction( reaction((_) => exchangeViewModel.depositCurrency,
(_) => exchangeViewModel.depositCurrency, (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey));
(CryptoCurrency currency) =>
_onCurrencyChange(currency, exchangeViewModel, depositKey));
reaction((_) => exchangeViewModel.depositAmount, (String amount) { reaction((_) => exchangeViewModel.depositAmount, (String amount) {
if (depositKey.currentState!.amountController.text != amount) { if (depositKey.currentState!.amountController.text != amount && amount != S.of(context).all) {
depositKey.currentState!.amountController.text = amount; depositKey.currentState!.amountController.text = amount;
} }
}); });
@ -309,8 +292,7 @@ class ExchangeTemplatePage extends BasePage {
} }
}); });
reaction((_) => exchangeViewModel.isDepositAddressEnabled, reaction((_) => exchangeViewModel.isDepositAddressEnabled, (bool isEnabled) {
(bool isEnabled) {
depositKey.currentState!.isAddressEditable(isEditable: isEnabled); depositKey.currentState!.isAddressEditable(isEditable: isEnabled);
}); });
@ -353,30 +335,28 @@ class ExchangeTemplatePage extends BasePage {
receiveKey.currentState.changeLimits(min: null, max: null); receiveKey.currentState.changeLimits(min: null, max: null);
});*/ });*/
depositAddressController.addListener( depositAddressController
() => exchangeViewModel.depositAddress = depositAddressController.text); .addListener(() => exchangeViewModel.depositAddress = depositAddressController.text);
depositAmountController.addListener(() { depositAmountController.addListener(() {
if (depositAmountController.text != exchangeViewModel.depositAmount) { if (depositAmountController.text != exchangeViewModel.depositAmount &&
exchangeViewModel.changeDepositAmount( exchangeViewModel.depositAmount != S.of(context).all) {
amount: depositAmountController.text); exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
exchangeViewModel.isReceiveAmountEntered = false; exchangeViewModel.isReceiveAmountEntered = false;
} }
}); });
receiveAddressController.addListener( receiveAddressController
() => exchangeViewModel.receiveAddress = receiveAddressController.text); .addListener(() => exchangeViewModel.receiveAddress = receiveAddressController.text);
receiveAmountController.addListener(() { receiveAmountController.addListener(() {
if (receiveAmountController.text != exchangeViewModel.receiveAmount) { if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
exchangeViewModel.changeReceiveAmount( exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text);
amount: receiveAmountController.text);
exchangeViewModel.isReceiveAmountEntered = true; exchangeViewModel.isReceiveAmountEntered = true;
} }
}); });
reaction((_) => exchangeViewModel.wallet.walletAddresses.address, reaction((_) => exchangeViewModel.wallet.walletAddresses.address, (String address) {
(String address) {
if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) { if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
depositKey.currentState!.changeAddress(address: address); depositKey.currentState!.changeAddress(address: address);
} }
@ -389,29 +369,26 @@ class ExchangeTemplatePage extends BasePage {
_isReactionsSet = true; _isReactionsSet = true;
} }
void _onCurrencyChange(CryptoCurrency currency, void _onCurrencyChange(CryptoCurrency currency, ExchangeViewModel exchangeViewModel,
ExchangeViewModel exchangeViewModel, GlobalKey<ExchangeCardState> key) { GlobalKey<ExchangeCardState> key) {
final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency; final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
key.currentState!.changeSelectedCurrency(currency); key.currentState!.changeSelectedCurrency(currency);
key.currentState!.changeWalletName( key.currentState!.changeWalletName(isCurrentTypeWallet ? exchangeViewModel.wallet.name : '');
isCurrentTypeWallet ? exchangeViewModel.wallet.name : '');
key.currentState!.changeAddress( key.currentState!.changeAddress(
address: isCurrentTypeWallet address: isCurrentTypeWallet ? exchangeViewModel.wallet.walletAddresses.address : '');
? exchangeViewModel.wallet.walletAddresses.address : '');
key.currentState!.changeAmount(amount: ''); key.currentState!.changeAmount(amount: '');
} }
void _onWalletNameChange(ExchangeViewModel exchangeViewModel, void _onWalletNameChange(ExchangeViewModel exchangeViewModel, CryptoCurrency currency,
CryptoCurrency currency, GlobalKey<ExchangeCardState> key) { GlobalKey<ExchangeCardState> key) {
final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency; final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
if (isCurrentTypeWallet) { if (isCurrentTypeWallet) {
key.currentState!.changeWalletName(exchangeViewModel.wallet.name); key.currentState!.changeWalletName(exchangeViewModel.wallet.name);
key.currentState!.addressController.text = key.currentState!.addressController.text = exchangeViewModel.wallet.walletAddresses.address;
exchangeViewModel.wallet.walletAddresses.address;
} else if (key.currentState!.addressController.text == } else if (key.currentState!.addressController.text ==
exchangeViewModel.wallet.walletAddresses.address) { exchangeViewModel.wallet.walletAddresses.address) {
key.currentState!.changeWalletName(''); key.currentState!.changeWalletName('');

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/core/amount_validator.dart';
import 'package:cake_wallet/entities/contact_base.dart'; import 'package:cake_wallet/entities/contact_base.dart';
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart'; import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
@ -38,6 +39,7 @@ class ExchangeCard extends StatefulWidget {
this.addressButtonsColor = Colors.transparent, this.addressButtonsColor = Colors.transparent,
this.borderColor = Colors.transparent, this.borderColor = Colors.transparent,
this.hasAllAmount = false, this.hasAllAmount = false,
this.isAllAmountEnabled = false,
this.amountFocusNode, this.amountFocusNode,
this.addressFocusNode, this.addressFocusNode,
this.allAmount, this.allAmount,
@ -64,9 +66,11 @@ class ExchangeCard extends StatefulWidget {
final Color borderColor; final Color borderColor;
final FormFieldValidator<String>? currencyValueValidator; final FormFieldValidator<String>? currencyValueValidator;
final FormFieldValidator<String>? addressTextFieldValidator; final FormFieldValidator<String>? addressTextFieldValidator;
final FormFieldValidator<String> allAmountValidator = AllAmountValidator();
final FocusNode? amountFocusNode; final FocusNode? amountFocusNode;
final FocusNode? addressFocusNode; final FocusNode? addressFocusNode;
final bool hasAllAmount; final bool hasAllAmount;
final bool isAllAmountEnabled;
final VoidCallback? allAmount; final VoidCallback? allAmount;
final void Function(BuildContext context)? onPushPasteButton; final void Function(BuildContext context)? onPushPasteButton;
final void Function(BuildContext context)? onPushAddressBookButton; final void Function(BuildContext context)? onPushAddressBookButton;
@ -162,6 +166,12 @@ class ExchangeCardState extends State<ExchangeCard> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (widget.isAllAmountEnabled) {
WidgetsBinding.instance.addPostFrameCallback((_) {
amountController.text = S.of(context).all;
});
}
final copyImage = Image.asset('assets/images/copy_content.png', final copyImage = Image.asset('assets/images/copy_content.png',
height: 16, height: 16,
width: 16, width: 16,
@ -207,16 +217,14 @@ class ExchangeCardState extends State<ExchangeCard> {
]), ]),
), ),
), ),
_selectedCurrency.tag != null if (_selectedCurrency.tag != null)
? Padding( Padding(
padding: const EdgeInsets.only(right: 3.0), padding: const EdgeInsets.only(right: 3.0),
child: Container( child: Container(
height: 32, height: 32,
decoration: BoxDecoration( decoration: BoxDecoration(
color: widget.addressButtonsColor ?? color: widget.addressButtonsColor ??
Theme.of(context) Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
.extension<SendPageTheme>()!
.textFieldButtonColor,
borderRadius: BorderRadius.all(Radius.circular(6))), borderRadius: BorderRadius.all(Radius.circular(6))),
child: Center( child: Center(
child: Padding( child: Padding(
@ -231,8 +239,7 @@ class ExchangeCardState extends State<ExchangeCard> {
), ),
), ),
), ),
) ),
: Container(),
Padding( Padding(
padding: const EdgeInsets.only(right: 4.0), padding: const EdgeInsets.only(right: 4.0),
child: Text(':', child: Text(':',
@ -413,8 +420,7 @@ class ExchangeCardState extends State<ExchangeCard> {
arguments: widget.initialCurrency, arguments: widget.initialCurrency,
); );
if (contact is ContactBase && if (contact is ContactBase) {
contact.address != null) {
setState(() => setState(() =>
addressController.text = contact.address); addressController.text = contact.address);
widget.onPushAddressBookButton?.call(context); widget.onPushAddressBookButton?.call(context);

View file

@ -5,6 +5,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart';
@ -12,6 +13,7 @@ import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/authentication_store.dart'; import 'package:cake_wallet/store/authentication_store.dart';
import 'package:cake_wallet/entities/qr_scanner.dart'; import 'package:cake_wallet/entities/qr_scanner.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:mobx/mobx.dart';
import 'package:uni_links/uni_links.dart'; import 'package:uni_links/uni_links.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
@ -49,6 +51,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
bool _requestAuth; bool _requestAuth;
StreamSubscription<Uri?>? stream; StreamSubscription<Uri?>? stream;
ReactionDisposer? _walletReactionDisposer;
Uri? launchUri; Uri? launchUri;
@override @override
@ -72,6 +75,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
@override @override
void dispose() { void dispose() {
stream?.cancel(); stream?.cancel();
_walletReactionDisposer?.call();
super.dispose(); super.dispose();
} }
@ -169,11 +173,21 @@ class RootState extends State<Root> with WidgetsBindingObserver {
); );
}); });
} else if (_isValidPaymentUri()) { } else if (_isValidPaymentUri()) {
if (widget.authenticationStore.state == AuthenticationState.uninitialized) {
launchUri = null;
} else {
if (widget.appStore.wallet == null) {
waitForWalletInstance(context, launchUri!);
launchUri = null;
} else {
widget.navigatorKey.currentState?.pushNamed( widget.navigatorKey.currentState?.pushNamed(
Routes.send, Routes.send,
arguments: PaymentRequest.fromUri(launchUri), arguments: PaymentRequest.fromUri(launchUri),
); );
launchUri = null; launchUri = null;
}
}
launchUri = null;
} else if (isWalletConnectLink) { } else if (isWalletConnectLink) {
if (isEVMCompatibleChain(widget.appStore.wallet!.type)) { if (isEVMCompatibleChain(widget.appStore.wallet!.type)) {
widget.navigatorKey.currentState?.pushNamed( widget.navigatorKey.currentState?.pushNamed(
@ -233,4 +247,24 @@ class RootState extends State<Root> with WidgetsBindingObserver {
fontSize: 16.0, fontSize: 16.0,
); );
} }
void waitForWalletInstance(BuildContext context, Uri tempLaunchUri) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) {
_walletReactionDisposer = reaction(
(_) => widget.appStore.wallet,
(WalletBase? wallet) {
if (wallet != null) {
widget.navigatorKey.currentState?.pushNamed(
Routes.send,
arguments: PaymentRequest.fromUri(tempLaunchUri),
);
_walletReactionDisposer?.call();
_walletReactionDisposer = null;
}
},
);
}
});
}
} }

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/template.dart'; import 'package:cake_wallet/entities/template.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart';
@ -48,6 +49,7 @@ class SendPage extends BasePage {
final PaymentRequest? initialPaymentRequest; final PaymentRequest? initialPaymentRequest;
bool _effectsInstalled = false; bool _effectsInstalled = false;
ContactRecord? newContactAddress;
@override @override
String get title => S.current.send; String get title => S.current.send;
@ -443,14 +445,41 @@ class SendPage extends BasePage {
} }
if (state is TransactionCommitted) { if (state is TransactionCommitted) {
String alertContent; newContactAddress =
if (sendViewModel.walletType == WalletType.solana) { newContactAddress ?? sendViewModel.newContactAddress();
alertContent =
'${S.of(_dialogContext).send_success(sendViewModel.selectedCryptoCurrency.toString())}. ${S.of(_dialogContext).waitFewSecondForTxUpdate}'; final successMessage = S.of(_dialogContext).send_success(
} else {
alertContent = S.of(_dialogContext).send_success(
sendViewModel.selectedCryptoCurrency.toString()); sendViewModel.selectedCryptoCurrency.toString());
}
final waitMessage = sendViewModel.walletType == WalletType.solana
? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}' : '';
final newContactMessage = newContactAddress != null
? '\n${S.of(context).add_contact_to_address_book}' : '';
final alertContent =
"$successMessage$waitMessage$newContactMessage";
if (newContactAddress != null) {
return AlertWithTwoActions(
alertTitle: '',
alertContent: alertContent,
rightButtonText: S.of(_dialogContext).add_contact,
leftButtonText: S.of(_dialogContext).ignor,
actionRightButton: () {
Navigator.of(_dialogContext).pop();
RequestReviewHandler.requestReview();
Navigator.of(context).pushNamed(
Routes.addressBookAddContact,
arguments: newContactAddress);
newContactAddress = null;
},
actionLeftButton: () {
Navigator.of(_dialogContext).pop();
RequestReviewHandler.requestReview();
newContactAddress = null;
});
} else {
return AlertWithOneAction( return AlertWithOneAction(
alertTitle: '', alertTitle: '',
alertContent: alertContent, alertContent: alertContent,
@ -460,6 +489,7 @@ class SendPage extends BasePage {
RequestReviewHandler.requestReview(); RequestReviewHandler.requestReview();
}); });
} }
}
return Offstage(); return Offstage();
}); });

View file

@ -1,7 +1,5 @@
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart'; import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/src/widgets/trail_button.dart'; import 'package:cake_wallet/src/widgets/trail_button.dart';
import 'package:cake_wallet/view_model/send/template_view_model.dart'; import 'package:cake_wallet/view_model/send/template_view_model.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
@ -11,7 +9,6 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/send/send_template_view_model.dart'; import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/screens/send/widgets/prefix_currency_icon_widget.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/src/screens/send/widgets/send_template_card.dart'; import 'package:cake_wallet/src/screens/send/widgets/send_template_card.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart';
@ -97,8 +94,13 @@ class SendTemplatePage extends BasePage {
radius: 6.0, radius: 6.0,
dotWidth: 6.0, dotWidth: 6.0,
dotHeight: 6.0, dotHeight: 6.0,
dotColor: Theme.of(context).extension<SendPageTheme>()!.indicatorDotColor, dotColor: Theme.of(context)
activeDotColor: Theme.of(context).extension<DashboardPageTheme>()!.indicatorDotTheme.activeIndicatorColor)) .extension<SendPageTheme>()!
.indicatorDotColor,
activeDotColor: Theme.of(context)
.extension<DashboardPageTheme>()!
.indicatorDotTheme
.activeIndicatorColor))
: Offstage(); : Offstage();
}, },
), ),

View file

@ -80,6 +80,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
if (initialPaymentRequest != null && if (initialPaymentRequest != null &&
sendViewModel.walletCurrencyName != initialPaymentRequest!.scheme.toLowerCase()) { sendViewModel.walletCurrencyName != initialPaymentRequest!.scheme.toLowerCase()) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (context.mounted) {
showPopUp<void>( showPopUp<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -89,6 +90,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
buttonText: S.of(context).ok, buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()); buttonAction: () => Navigator.of(context).pop());
}); });
}
}); });
} }
} }
@ -321,7 +323,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
? sendViewModel.allAmountValidator ? sendViewModel.allAmountValidator
: sendViewModel.amountValidator, : sendViewModel.amountValidator,
), ),
if (!sendViewModel.isBatchSending && sendViewModel.shouldDisplaySendALL) if (!sendViewModel.isBatchSending &&
sendViewModel.shouldDisplaySendALL)
Positioned( Positioned(
top: 2, top: 2,
right: 0, right: 0,

View file

@ -10,17 +10,24 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class ServicesUpdatesWidget extends StatelessWidget { class ServicesUpdatesWidget extends StatefulWidget {
final Future<ServicesResponse> servicesResponse; final Future<ServicesResponse> servicesResponse;
const ServicesUpdatesWidget(this.servicesResponse, {super.key}); const ServicesUpdatesWidget(this.servicesResponse, {super.key});
@override
State<ServicesUpdatesWidget> createState() => _ServicesUpdatesWidgetState();
}
class _ServicesUpdatesWidgetState extends State<ServicesUpdatesWidget> {
bool wasOpened = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: FutureBuilder<ServicesResponse>( child: FutureBuilder<ServicesResponse>(
future: servicesResponse, future: widget.servicesResponse,
builder: (context, state) { builder: (context, state) {
return InkWell( return InkWell(
onTap: state.hasData onTap: state.hasData
@ -30,6 +37,8 @@ class ServicesUpdatesWidget extends StatelessWidget {
.get<SharedPreferences>() .get<SharedPreferences>()
.setString(PreferencesKey.serviceStatusShaKey, state.data!.currentSha); .setString(PreferencesKey.serviceStatusShaKey, state.data!.currentSha);
setState(() => wasOpened = true);
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(

View file

@ -1,5 +1,5 @@
class FeatureFlag { class FeatureFlag {
static const bool isCakePayEnabled = false; static const bool isCakePayEnabled = false;
static const bool isExolixEnabled = false; static const bool isExolixEnabled = true;
static const bool isInAppTorEnabled = false; static const bool isInAppTorEnabled = false;
} }

View file

@ -48,11 +48,11 @@ abstract class ContactViewModelBase with Store {
currency = null; currency = null;
} }
Future save() async { Future<void> save() async {
try { try {
state = IsExecutingState(); state = IsExecutingState();
if (_contact != null) { if (_contact != null && _contact!.original.isInBox) {
_contact?.name = name; _contact?.name = name;
_contact?.address = address; _contact?.address = address;
_contact?.type = currency!; _contact?.type = currency!;

View file

@ -59,6 +59,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
this.sharedPreferences, this.sharedPreferences,
this.contactListViewModel, this.contactListViewModel,
) : _cryptoNumberFormat = NumberFormat(), ) : _cryptoNumberFormat = NumberFormat(),
isSendAllEnabled = false,
isFixedRateMode = false, isFixedRateMode = false,
isReceiveAmountEntered = false, isReceiveAmountEntered = false,
depositAmount = '', depositAmount = '',
@ -208,6 +209,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@observable @observable
bool isFixedRateMode; bool isFixedRateMode;
@observable
bool isSendAllEnabled;
@observable @observable
Limits limits; Limits limits;
@ -538,10 +542,14 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
} }
@action @action
void calculateDepositAllAmount() { void enableSendAllAmount() {
if (wallet.type == WalletType.bitcoin || isSendAllEnabled = true;
wallet.type == WalletType.litecoin || calculateDepositAllAmount();
wallet.type == WalletType.bitcoinCash) { }
@action
Future<void> calculateDepositAllAmount() async {
if (wallet.type == WalletType.litecoin || wallet.type == WalletType.bitcoinCash) {
final availableBalance = wallet.balance[wallet.currency]!.available; final availableBalance = wallet.balance[wallet.currency]!.available;
final priority = _settingsStore.priority[wallet.type]!; final priority = _settingsStore.priority[wallet.type]!;
final fee = wallet.calculateEstimatedFee(priority, null); final fee = wallet.calculateEstimatedFee(priority, null);
@ -550,6 +558,13 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
final amount = availableBalance - fee; final amount = availableBalance - fee;
changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount)); changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
} else if (wallet.type == WalletType.bitcoin) {
final priority = _settingsStore.priority[wallet.type]!;
final amount = await bitcoin!.estimateFakeSendAllTxAmount(
wallet, bitcoin!.deserializeBitcoinTransactionPriority(priority.raw));
changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
} }
} }

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
@ -420,6 +421,34 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
} }
} }
ContactRecord? newContactAddress () {
final Set<String> contactAddresses =
Set.from(contactListViewModel.contacts.map((contact) => contact.address))
..addAll(contactListViewModel.walletContacts.map((contact) => contact.address));
for (var output in outputs) {
String address;
if (output.isParsedAddress) {
address = output.parsedAddress.addresses.first;
} else {
address = output.address;
}
if (address.isNotEmpty && !contactAddresses.contains(address)) {
return ContactRecord(
contactListViewModel.contactSource,
Contact(
name: '',
address: address,
type: selectedCryptoCurrency,
));
}
}
return null;
}
String translateErrorMessage( String translateErrorMessage(
String error, String error,
WalletType walletType, WalletType walletType,

View file

@ -125,4 +125,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 65ec1541137fb5b35d00490dec1bb48d4d9586bb PODFILE CHECKSUM: 65ec1541137fb5b35d00490dec1bb48d4d9586bb
COCOAPODS: 1.12.1 COCOAPODS: 1.15.2

View file

@ -110,7 +110,7 @@ dependencies:
bitcoin_base: bitcoin_base:
git: git:
url: https://github.com/cake-tech/bitcoin_base.git url: https://github.com/cake-tech/bitcoin_base.git
ref: cake-update-v1 ref: cake-update-v2
breez_sdk: breez_sdk:
git: git:

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "ﺎﻨﻫ ﺔﻄﺸﻨﻟﺍ ﺕﻻﺎﺼﺗﻻﺍ ﺮﻬﻈﺘﺳ", "activeConnectionsPrompt": "ﺎﻨﻫ ﺔﻄﺸﻨﻟﺍ ﺕﻻﺎﺼﺗﻻﺍ ﺮﻬﻈﺘﺳ",
"add": "إضافة", "add": "إضافة",
"add_contact": "ﻝﺎﺼﺗﺍ ﺔﻬﺟ ﺔﻓﺎﺿﺇ", "add_contact": "ﻝﺎﺼﺗﺍ ﺔﻬﺟ ﺔﻓﺎﺿﺇ",
"add_contact_to_address_book": "هل ترغب في إضافة جهة الاتصال هذه إلى دفتر العناوين الخاص بك؟",
"add_custom_node": "إضافة عقدة مخصصة جديدة", "add_custom_node": "إضافة عقدة مخصصة جديدة",
"add_custom_redemption": "إضافة استرداد مخصص", "add_custom_redemption": "إضافة استرداد مخصص",
"add_fund_to_card": "أضف أموالاً مدفوعة مسبقًا إلى البطاقات (حتى ${value})", "add_fund_to_card": "أضف أموالاً مدفوعة مسبقًا إلى البطاقات (حتى ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Тук ще се появят активни връзки", "activeConnectionsPrompt": "Тук ще се появят активни връзки",
"add": "Добави", "add": "Добави",
"add_contact": "Добави контакт", "add_contact": "Добави контакт",
"add_contact_to_address_book": "Искате ли да добавите този контакт към вашата адресна книга?",
"add_custom_node": "Добавяне на нов персонализиран Node", "add_custom_node": "Добавяне на нов персонализиран Node",
"add_custom_redemption": "Добавете персонализиран Redemption", "add_custom_redemption": "Добавете персонализиран Redemption",
"add_fund_to_card": "Добавете предплатени средства в картите (до ${value})", "add_fund_to_card": "Добавете предплатени средства в картите (до ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Zde se zobrazí aktivní připojení", "activeConnectionsPrompt": "Zde se zobrazí aktivní připojení",
"add": "Přidat", "add": "Přidat",
"add_contact": "Přidat kontakt", "add_contact": "Přidat kontakt",
"add_contact_to_address_book": "Chcete přidat tento kontakt do svého adresáře?",
"add_custom_node": "Přidat vlastní uzel", "add_custom_node": "Přidat vlastní uzel",
"add_custom_redemption": "Přidat vlastní uplatnění", "add_custom_redemption": "Přidat vlastní uplatnění",
"add_fund_to_card": "Všechny předplacené prostředky na kartě (až ${value})", "add_fund_to_card": "Všechny předplacené prostředky na kartě (až ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Hier werden aktive Verbindungen angezeigt", "activeConnectionsPrompt": "Hier werden aktive Verbindungen angezeigt",
"add": "Hinzufügen", "add": "Hinzufügen",
"add_contact": "Kontakt hinzufügen", "add_contact": "Kontakt hinzufügen",
"add_contact_to_address_book": "Möchten Sie diesen Kontakt zu Ihrem Adressbuch hinzufügen?",
"add_custom_node": "Neuen benutzerdefinierten Knoten hinzufügen", "add_custom_node": "Neuen benutzerdefinierten Knoten hinzufügen",
"add_custom_redemption": "Benutzerdefinierte Einlösung hinzufügen", "add_custom_redemption": "Benutzerdefinierte Einlösung hinzufügen",
"add_fund_to_card": "Prepaid-Guthaben zu den Karten hinzufügen (bis zu ${value})", "add_fund_to_card": "Prepaid-Guthaben zu den Karten hinzufügen (bis zu ${value})",
@ -417,8 +418,8 @@
"placeholder_transactions": "Ihre Transaktionen werden hier angezeigt", "placeholder_transactions": "Ihre Transaktionen werden hier angezeigt",
"please_fill_totp": "Bitte geben Sie den 8-stelligen Code ein, der auf Ihrem anderen Gerät vorhanden ist", "please_fill_totp": "Bitte geben Sie den 8-stelligen Code ein, der auf Ihrem anderen Gerät vorhanden ist",
"please_make_selection": "Bitte treffen Sie unten eine Auswahl zum Erstellen oder Wiederherstellen Ihrer Wallet.", "please_make_selection": "Bitte treffen Sie unten eine Auswahl zum Erstellen oder Wiederherstellen Ihrer Wallet.",
"please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.",
"Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.", "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.",
"please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.",
"please_select": "Bitte auswählen:", "please_select": "Bitte auswählen:",
"please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.", "please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.",
"please_try_to_connect_to_another_node": "Bitte versuchen Sie, sich mit einem anderen Knoten zu verbinden", "please_try_to_connect_to_another_node": "Bitte versuchen Sie, sich mit einem anderen Knoten zu verbinden",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Active connections will appear here", "activeConnectionsPrompt": "Active connections will appear here",
"add": "Add", "add": "Add",
"add_contact": "Add contact", "add_contact": "Add contact",
"add_contact_to_address_book": "Would you like to add this contact to your address book?",
"add_custom_node": "Add New Custom Node", "add_custom_node": "Add New Custom Node",
"add_custom_redemption": "Add Custom Redemption", "add_custom_redemption": "Add Custom Redemption",
"add_fund_to_card": "Add prepaid funds to the cards (up to ${value})", "add_fund_to_card": "Add prepaid funds to the cards (up to ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Las conexiones activas aparecerán aquí", "activeConnectionsPrompt": "Las conexiones activas aparecerán aquí",
"add": "Añadir", "add": "Añadir",
"add_contact": "Agregar contacto", "add_contact": "Agregar contacto",
"add_contact_to_address_book": "¿Le gustaría agregar este contacto a su libreta de direcciones?",
"add_custom_node": "Agregar nuevo nodo personalizado", "add_custom_node": "Agregar nuevo nodo personalizado",
"add_custom_redemption": "Agregar redención personalizada", "add_custom_redemption": "Agregar redención personalizada",
"add_fund_to_card": "Agregar fondos prepagos a las tarjetas (hasta ${value})", "add_fund_to_card": "Agregar fondos prepagos a las tarjetas (hasta ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Les connexions actives apparaîtront ici", "activeConnectionsPrompt": "Les connexions actives apparaîtront ici",
"add": "Ajouter", "add": "Ajouter",
"add_contact": "Ajouter le contact", "add_contact": "Ajouter le contact",
"add_contact_to_address_book": "Souhaitez-vous ajouter ce contact à votre carnet d'adresses?",
"add_custom_node": "Ajouter un nouveau nœud personnalisé", "add_custom_node": "Ajouter un nouveau nœud personnalisé",
"add_custom_redemption": "Ajouter un remboursement personnalisé", "add_custom_redemption": "Ajouter un remboursement personnalisé",
"add_fund_to_card": "Ajouter des fonds prépayés aux cartes (jusqu'à ${value})", "add_fund_to_card": "Ajouter des fonds prépayés aux cartes (jusqu'à ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Haɗin kai mai aiki zai bayyana a nan", "activeConnectionsPrompt": "Haɗin kai mai aiki zai bayyana a nan",
"add": "Ƙara", "add": "Ƙara",
"add_contact": "Ƙara lamba", "add_contact": "Ƙara lamba",
"add_contact_to_address_book": "Kuna so ku ƙara wannan lamba zuwa littafin adireshinku?",
"add_custom_node": "Ƙara Sabon Kulli na Custom", "add_custom_node": "Ƙara Sabon Kulli na Custom",
"add_custom_redemption": "Ƙara Ceto na Musamman", "add_custom_redemption": "Ƙara Ceto na Musamman",
"add_fund_to_card": "Ƙara kuɗin da aka riga aka biya a katunan (har zuwa ${value})", "add_fund_to_card": "Ƙara kuɗin da aka riga aka biya a katunan (har zuwa ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "सक्रिय कनेक्शन यहां दिखाई देंगे", "activeConnectionsPrompt": "सक्रिय कनेक्शन यहां दिखाई देंगे",
"add": "जोड़ना", "add": "जोड़ना",
"add_contact": "संपर्क जोड़ें", "add_contact": "संपर्क जोड़ें",
"add_contact_to_address_book": "क्या आप इस संपर्क को अपनी एड्रेस बुक में जोड़ना चाहेंगे?",
"add_custom_node": "नया कस्टम नोड जोड़ें", "add_custom_node": "नया कस्टम नोड जोड़ें",
"add_custom_redemption": "कस्टम रिडेम्पशन जोड़ें", "add_custom_redemption": "कस्टम रिडेम्पशन जोड़ें",
"add_fund_to_card": "कार्ड में प्रीपेड धनराशि जोड़ें (${value} तक)", "add_fund_to_card": "कार्ड में प्रीपेड धनराशि जोड़ें (${value} तक)",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Ovdje će se pojaviti aktivne veze", "activeConnectionsPrompt": "Ovdje će se pojaviti aktivne veze",
"add": "Dodaj", "add": "Dodaj",
"add_contact": "Dodaj kontakt", "add_contact": "Dodaj kontakt",
"add_contact_to_address_book": "Želite li dodati ovaj kontakt u svoj adresar?",
"add_custom_node": "Dodaj novi prilagođeni čvor", "add_custom_node": "Dodaj novi prilagođeni čvor",
"add_custom_redemption": "Dodaj prilagođeni otkup", "add_custom_redemption": "Dodaj prilagođeni otkup",
"add_fund_to_card": "Dodajte unaprijed uplaćena sredstva na kartice (do ${value})", "add_fund_to_card": "Dodajte unaprijed uplaćena sredstva na kartice (do ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Koneksi aktif akan muncul di sini", "activeConnectionsPrompt": "Koneksi aktif akan muncul di sini",
"add": "Menambahkan", "add": "Menambahkan",
"add_contact": "Tambah kontak", "add_contact": "Tambah kontak",
"add_contact_to_address_book": "Apakah Anda ingin menambahkan kontak ini ke buku alamat Anda?",
"add_custom_node": "Tambahkan Node Kustom Baru", "add_custom_node": "Tambahkan Node Kustom Baru",
"add_custom_redemption": "Tambahkan Tukar Kustom", "add_custom_redemption": "Tambahkan Tukar Kustom",
"add_fund_to_card": "Tambahkan dana pra-bayar ke kartu (hingga ${value})", "add_fund_to_card": "Tambahkan dana pra-bayar ke kartu (hingga ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Le connessioni attive verranno visualizzate qui", "activeConnectionsPrompt": "Le connessioni attive verranno visualizzate qui",
"add": "Aggiungi", "add": "Aggiungi",
"add_contact": "Aggiungi contatto", "add_contact": "Aggiungi contatto",
"add_contact_to_address_book": "Vorresti aggiungere questo contatto alla tua rubrica?",
"add_custom_node": "Aggiungi nuovo nodo personalizzato", "add_custom_node": "Aggiungi nuovo nodo personalizzato",
"add_custom_redemption": "Aggiungi riscatto personalizzato", "add_custom_redemption": "Aggiungi riscatto personalizzato",
"add_fund_to_card": "Aggiungi fondi prepagati alle carte (fino a ${value})", "add_fund_to_card": "Aggiungi fondi prepagati alle carte (fino a ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "アクティブな接続がここに表示されます", "activeConnectionsPrompt": "アクティブな接続がここに表示されます",
"add": "加える", "add": "加える",
"add_contact": "連絡先を追加", "add_contact": "連絡先を追加",
"add_contact_to_address_book": "この連絡先をアドレス帳に追加しますか?",
"add_custom_node": "新しいカスタム ノードを追加", "add_custom_node": "新しいカスタム ノードを追加",
"add_custom_redemption": "カスタム引き換えを追加", "add_custom_redemption": "カスタム引き換えを追加",
"add_fund_to_card": "プリペイド資金をカードに追加します(最大 ${value}", "add_fund_to_card": "プリペイド資金をカードに追加します(最大 ${value}",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "활성 연결이 여기에 표시됩니다", "activeConnectionsPrompt": "활성 연결이 여기에 표시됩니다",
"add": "더하다", "add": "더하다",
"add_contact": "주소록에 추가", "add_contact": "주소록에 추가",
"add_contact_to_address_book": "이 연락처를 주소록에 추가 하시겠습니까?",
"add_custom_node": "새 사용자 정의 노드 추가", "add_custom_node": "새 사용자 정의 노드 추가",
"add_custom_redemption": "사용자 지정 상환 추가", "add_custom_redemption": "사용자 지정 상환 추가",
"add_fund_to_card": "카드에 선불 금액 추가(최대 ${value})", "add_fund_to_card": "카드에 선불 금액 추가(최대 ${value})",
@ -417,8 +418,8 @@
"placeholder_transactions": "거래가 여기에 표시됩니다", "placeholder_transactions": "거래가 여기에 표시됩니다",
"please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.", "please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.",
"please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.", "please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.",
"please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", "Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"please_select": "선택 해주세요:", "please_select": "선택 해주세요:",
"please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.", "please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.",
"please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오", "please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "လက်ရှိချိတ်ဆက်မှုများ ဤနေရာတွင် ပေါ်လာပါမည်။", "activeConnectionsPrompt": "လက်ရှိချိတ်ဆက်မှုများ ဤနေရာတွင် ပေါ်လာပါမည်။",
"add": "ထည့်ပါ။", "add": "ထည့်ပါ။",
"add_contact": "အဆက်အသွယ်ထည့်ပါ။", "add_contact": "အဆက်အသွယ်ထည့်ပါ။",
"add_contact_to_address_book": "ဒီအဆက်အသွယ်ကိုမင်းရဲ့လိပ်စာစာအုပ်နဲ့ထပ်ထည့်ချင်ပါသလား။",
"add_custom_node": "စိတ်ကြိုက် Node အသစ်ကို ထည့်ပါ။", "add_custom_node": "စိတ်ကြိုက် Node အသစ်ကို ထည့်ပါ။",
"add_custom_redemption": "စိတ်ကြိုက်ရွေးယူမှုကို ထည့်ပါ။", "add_custom_redemption": "စိတ်ကြိုက်ရွေးယူမှုကို ထည့်ပါ။",
"add_fund_to_card": "ကတ်များသို့ ကြိုတင်ငွေပေးငွေများ ထည့်ပါ (${value} အထိ)", "add_fund_to_card": "ကတ်များသို့ ကြိုတင်ငွေပေးငွေများ ထည့်ပါ (${value} အထိ)",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Actieve verbindingen worden hier weergegeven", "activeConnectionsPrompt": "Actieve verbindingen worden hier weergegeven",
"add": "Toevoegen", "add": "Toevoegen",
"add_contact": "Contactpersoon toevoegen", "add_contact": "Contactpersoon toevoegen",
"add_contact_to_address_book": "Wilt u dit contact toevoegen aan uw adresboek?",
"add_custom_node": "Voeg een nieuw aangepast knooppunt toe", "add_custom_node": "Voeg een nieuw aangepast knooppunt toe",
"add_custom_redemption": "Voeg aangepaste inwisseling toe", "add_custom_redemption": "Voeg aangepaste inwisseling toe",
"add_fund_to_card": "Voeg prepaid tegoed toe aan de kaarten (tot ${value})", "add_fund_to_card": "Voeg prepaid tegoed toe aan de kaarten (tot ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Tutaj pojawią się aktywne połączenia", "activeConnectionsPrompt": "Tutaj pojawią się aktywne połączenia",
"add": "Dodaj", "add": "Dodaj",
"add_contact": "Dodaj kontakt", "add_contact": "Dodaj kontakt",
"add_contact_to_address_book": "Czy chciałbyś dodać ten kontakt do swojej książki adresowej?",
"add_custom_node": "Dodaj nowy węzeł niestandardowy", "add_custom_node": "Dodaj nowy węzeł niestandardowy",
"add_custom_redemption": "Dodaj niestandardowe wykorzystanie", "add_custom_redemption": "Dodaj niestandardowe wykorzystanie",
"add_fund_to_card": "Dodaj przedpłacone środki do kart (do ${value})", "add_fund_to_card": "Dodaj przedpłacone środki do kart (do ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Conexões ativas aparecerão aqui", "activeConnectionsPrompt": "Conexões ativas aparecerão aqui",
"add": "Adicionar", "add": "Adicionar",
"add_contact": "Adicionar contato", "add_contact": "Adicionar contato",
"add_contact_to_address_book": "Você gostaria de adicionar esse contato ao seu catálogo de endereços?",
"add_custom_node": "Adicionar novo nó personalizado", "add_custom_node": "Adicionar novo nó personalizado",
"add_custom_redemption": "Adicionar resgate personalizado", "add_custom_redemption": "Adicionar resgate personalizado",
"add_fund_to_card": "Adicionar fundos pré-pagos aos cartões (até ${value})", "add_fund_to_card": "Adicionar fundos pré-pagos aos cartões (até ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Здесь появятся активные подключения", "activeConnectionsPrompt": "Здесь появятся активные подключения",
"add": "Добавить", "add": "Добавить",
"add_contact": "Добавить контакт", "add_contact": "Добавить контакт",
"add_contact_to_address_book": "Хотели бы вы добавить этот контакт в свою адресную книгу?",
"add_custom_node": "Добавить новый пользовательский узел", "add_custom_node": "Добавить новый пользовательский узел",
"add_custom_redemption": "Добавить пользовательское погашение", "add_custom_redemption": "Добавить пользовательское погашение",
"add_fund_to_card": "Добавить предоплаченные средства на карты (до ${value})", "add_fund_to_card": "Добавить предоплаченные средства на карты (до ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "การเชื่อมต่อที่ใช้งานอยู่จะปรากฏที่นี่", "activeConnectionsPrompt": "การเชื่อมต่อที่ใช้งานอยู่จะปรากฏที่นี่",
"add": "เพิ่ม", "add": "เพิ่ม",
"add_contact": "เพิ่มผู้ติดต่อ", "add_contact": "เพิ่มผู้ติดต่อ",
"add_contact_to_address_book": "คุณต้องการเพิ่มผู้ติดต่อนี้ในสมุดที่อยู่ของคุณหรือไม่?",
"add_custom_node": "เพิ่มจุดโหนดแบบกำหนดเอง", "add_custom_node": "เพิ่มจุดโหนดแบบกำหนดเอง",
"add_custom_redemption": "เพิ่มการรับคืนที่กำหนดเอง", "add_custom_redemption": "เพิ่มการรับคืนที่กำหนดเอง",
"add_fund_to_card": "เพิ่มเงินสำรองไว้บนบัตร (ถึง ${value})", "add_fund_to_card": "เพิ่มเงินสำรองไว้บนบัตร (ถึง ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Lalabas dito ang mga aktibong koneksyon", "activeConnectionsPrompt": "Lalabas dito ang mga aktibong koneksyon",
"add": "Idagdag", "add": "Idagdag",
"add_contact": "Magdagdag ng contact", "add_contact": "Magdagdag ng contact",
"add_contact_to_address_book": "Nais mo bang idagdag ang contact na ito sa iyong address book?",
"add_custom_node": "Magdagdag ng bagong pasadyang node", "add_custom_node": "Magdagdag ng bagong pasadyang node",
"add_custom_redemption": "Magdagdag ng pasadyang pagtubos", "add_custom_redemption": "Magdagdag ng pasadyang pagtubos",
"add_fund_to_card": "Magdagdag ng prepaid na pondo sa mga kard (hanggang sa ${value})", "add_fund_to_card": "Magdagdag ng prepaid na pondo sa mga kard (hanggang sa ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Aktif bağlantılar burada görünecek", "activeConnectionsPrompt": "Aktif bağlantılar burada görünecek",
"add": "Ekle", "add": "Ekle",
"add_contact": "Kişi ekle", "add_contact": "Kişi ekle",
"add_contact_to_address_book": "Bu kişiyi adres defterinize eklemek ister misiniz?",
"add_custom_node": "Yeni Özel Düğüm Ekleme", "add_custom_node": "Yeni Özel Düğüm Ekleme",
"add_custom_redemption": "Özel Bozdurma Ekle", "add_custom_redemption": "Özel Bozdurma Ekle",
"add_fund_to_card": "Ön ödemeli kartlara para ekle (En fazla yüklenebilir tutar: ${value})", "add_fund_to_card": "Ön ödemeli kartlara para ekle (En fazla yüklenebilir tutar: ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Тут з’являться активні підключення", "activeConnectionsPrompt": "Тут з’являться активні підключення",
"add": "Добавити", "add": "Добавити",
"add_contact": "Додати контакт", "add_contact": "Додати контакт",
"add_contact_to_address_book": "Хотіли б ви додати цей контакт до своєї адресної книги?",
"add_custom_node": "Додати новий спеціальний вузол", "add_custom_node": "Додати новий спеціальний вузол",
"add_custom_redemption": "Додати спеціальне погашення", "add_custom_redemption": "Додати спеціальне погашення",
"add_fund_to_card": "Додайте передплачені кошти на картки (до ${value})", "add_fund_to_card": "Додайте передплачені кошти на картки (до ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "۔ﮯﮔ ﮞﻮﮨ ﺮﮨﺎﻇ ﮞﺎﮩﯾ ﺰﻨﺸﮑﻨﮐ ﻝﺎﻌﻓ", "activeConnectionsPrompt": "۔ﮯﮔ ﮞﻮﮨ ﺮﮨﺎﻇ ﮞﺎﮩﯾ ﺰﻨﺸﮑﻨﮐ ﻝﺎﻌﻓ",
"add": "شامل کریں۔", "add": "شامل کریں۔",
"add_contact": "۔ﮟﯾﺮﮐ ﻞﻣﺎﺷ ﮧﻄﺑﺍﺭ", "add_contact": "۔ﮟﯾﺮﮐ ﻞﻣﺎﺷ ﮧﻄﺑﺍﺭ",
"add_contact_to_address_book": "کیا آپ اس رابطہ کو اپنی ایڈریس بک میں شامل کرنا چاہیں گے؟",
"add_custom_node": "نیا کسٹم نوڈ شامل کریں۔", "add_custom_node": "نیا کسٹم نوڈ شامل کریں۔",
"add_custom_redemption": "حسب ضرورت چھٹکارا شامل کریں۔", "add_custom_redemption": "حسب ضرورت چھٹکارا شامل کریں۔",
"add_fund_to_card": "کارڈز میں پری پیڈ فنڈز شامل کریں (${value} تک)", "add_fund_to_card": "کارڈز میں پری پیڈ فنڈز شامل کریں (${value} تک)",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "Awọn asopọ ti nṣiṣe lọwọ yoo han nibi", "activeConnectionsPrompt": "Awọn asopọ ti nṣiṣe lọwọ yoo han nibi",
"add": "Fikún", "add": "Fikún",
"add_contact": "Fi olubasọrọ kun", "add_contact": "Fi olubasọrọ kun",
"add_contact_to_address_book": "Ṣe o fẹ lati ṣafikun olubasọrọ yii si iwe adirẹsi rẹ?",
"add_custom_node": "Fikún apẹka títun t'ẹ́ pààrọ̀", "add_custom_node": "Fikún apẹka títun t'ẹ́ pààrọ̀",
"add_custom_redemption": "Tẹ̀ iye owó t'ẹ́ fẹ́ ná", "add_custom_redemption": "Tẹ̀ iye owó t'ẹ́ fẹ́ ná",
"add_fund_to_card": "Ẹ fikún owó sí àwọn káàdì (kò tóbi ju ${value})", "add_fund_to_card": "Ẹ fikún owó sí àwọn káàdì (kò tóbi ju ${value})",

View file

@ -9,6 +9,7 @@
"activeConnectionsPrompt": "活动连接将出现在这里", "activeConnectionsPrompt": "活动连接将出现在这里",
"add": "添加", "add": "添加",
"add_contact": "增加联系人", "add_contact": "增加联系人",
"add_contact_to_address_book": "您想将此联系人添加到您的通讯录中吗?",
"add_custom_node": "添加新的自定义节点", "add_custom_node": "添加新的自定义节点",
"add_custom_redemption": "添加自定义兑换", "add_custom_redemption": "添加自定义兑换",
"add_fund_to_card": "向卡中添加预付资金(最多 ${value}", "add_fund_to_card": "向卡中添加预付资金(最多 ${value}",

View file

@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_ANDROID_TYPE=$1 APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com" MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.11.1" MONERO_COM_VERSION="1.12.0"
MONERO_COM_BUILD_NUMBER=78 MONERO_COM_BUILD_NUMBER=79
MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_PACKAGE="com.monero.app"
MONERO_COM_SCHEME="monero.com" MONERO_COM_SCHEME="monero.com"
CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.14.1" CAKEWALLET_VERSION="4.15.2"
CAKEWALLET_BUILD_NUMBER=197 CAKEWALLET_BUILD_NUMBER=200
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
CAKEWALLET_SCHEME="cakewallet" CAKEWALLET_SCHEME="cakewallet"

View file

@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_IOS_TYPE=$1 APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com" MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.11.1" MONERO_COM_VERSION="1.12.0"
MONERO_COM_BUILD_NUMBER=76 MONERO_COM_BUILD_NUMBER=77
MONERO_COM_BUNDLE_ID="com.cakewallet.monero" MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.14.1" CAKEWALLET_VERSION="4.15.2"
CAKEWALLET_BUILD_NUMBER=216 CAKEWALLET_BUILD_NUMBER=219
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
HAVEN_NAME="Haven" HAVEN_NAME="Haven"

View file

@ -16,13 +16,13 @@ if [ -n "$1" ]; then
fi fi
MONERO_COM_NAME="Monero.com" MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.1.1" MONERO_COM_VERSION="1.2.0"
MONERO_COM_BUILD_NUMBER=9 MONERO_COM_BUILD_NUMBER=10
MONERO_COM_BUNDLE_ID="com.cakewallet.monero" MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="1.7.1" CAKEWALLET_VERSION="1.8.2"
CAKEWALLET_BUILD_NUMBER=56 CAKEWALLET_BUILD_NUMBER=59
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then

View file

@ -75,7 +75,6 @@ import 'package:cw_core/output_info.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
import 'package:cake_wallet/view_model/send/output.dart'; import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:bitcoin_base/bitcoin_base.dart';"""; import 'package:bitcoin_base/bitcoin_base.dart';""";
const bitcoinCWHeaders = """ const bitcoinCWHeaders = """
@ -132,6 +131,7 @@ abstract class Bitcoin {
List<String> getAddresses(Object wallet); List<String> getAddresses(Object wallet);
String getAddress(Object wallet); String getAddress(Object wallet);
Future<int> estimateFakeSendAllTxAmount(Object wallet, TransactionPriority priority);
List<ElectrumSubAddress> getSubAddresses(Object wallet); List<ElectrumSubAddress> getSubAddresses(Object wallet);
String formatterBitcoinAmountToString({required int amount}); String formatterBitcoinAmountToString({required int amount});