Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-659-Automated-Integrated-Tests

This commit is contained in:
Blazebrain 2024-07-31 14:18:56 +01:00
commit 698dc40060
90 changed files with 1210 additions and 640 deletions

View file

@ -44,7 +44,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: "11.x"
java-version: "17.x"
- name: Configure placeholder git details
run: |
git config --global user.email "CI@cakewallet.com"

View file

@ -46,7 +46,7 @@ android {
defaultConfig {
applicationId appProperties['id']
minSdkVersion 24
targetSdkVersion 33
targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -1,2 +1,3 @@
Monero enhancements
Synchronization improvements
Bug fixes

View file

@ -1,3 +1,5 @@
Monero enhancements
Improvements for Tron and Nano wallets
Monero and Ethereum enhancements
Synchronization improvements
Exchange flow enhancements
Ledger improvements
Bug fixes

View file

@ -1,38 +0,0 @@
# Building CakeWallet for Windows
## Requirements and Setup
The following are the system requirements to build CakeWallet for your Windows PC.
```
Windows 10 or later (64-bit), x86-64 based
Flutter 3 or above
```
## Building CakeWallet on Windows
These steps will help you configure and execute a build of CakeWallet from its source code.
### 1. Installing Package Dependencies
For build CakeWallet windows application from sources you will be needed to have:
> [Install Flutter]Follow installation guide (https://docs.flutter.dev/get-started/install/windows) and install do not miss to dev tools (install https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) which are required for windows desktop development (need to install Git for Windows and Visual Studio 2022). Then install `Desktop development with C++` packages via GUI Visual Studio 2022, or Visual Studio Build Tools 2022 including: `C++ Build Tools core features`, `C++ 2022 Redistributable Update`, `C++ core desktop features`, `MVC v143 - VS 2022 C++ x64/x86 build tools`, `C++ CMake tools for Windwos`, `Testing tools core features - Build Tools`, `C++ AddressSanitizer`.
> [Install WSL] for building monero dependencies need to install Windows WSL (https://learn.microsoft.com/en-us/windows/wsl/install) and required packages for WSL (Ubuntu):
`$ sudo apt update `
`$ sudo apt build-essential cmake gcc-mingw-w64 g++-mingw-w64 autoconf libtool pkg-config`
### 2. Pull CakeWallet source code
You can downlaod CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git by following next command:
`$ git clone https://github.com/cake-tech/cake_wallet.git --branch MrCyjaneK-cyjan-monerodart`
OR you can download it as [Zip archive](https://github.com/cake-tech/cake_wallet/archive/refs/heads/MrCyjaneK-cyjan-monerodart.zip)
### 3. Build Monero, Monero_c and their dependencies
For use monero in the application need to build Monero wrapper - Monero_C which will be used by monero.dart package. For that need to run shell (bash - typically same named utility should be available after WSL is enabled in your system) with previously installed WSL, then change current directory to the application project directory with your used shell and then change current directory to `scripts/windows`: `$ cd scripts/windows`. Run build script: `$ ./build_all.sh`.
### 4. Configure and build CakeWallet application
To configure the application open directory where you have downloaded or unarchived CakeWallet sources and run `cakewallet.bat`.
Or if you used WSL and have active shell session you can run `$ ./cakewallet.sh` script in `scripts/windows` which will run `cakewallet.bat` in WSL.
After execution of `cakewallet.bat` you should to get `Cake Wallet.zip` in project root directory which will contains `CakeWallet.exe` file and another needed files for run the application. Now you can extract files from `Cake Wallet.zip` archive and run the application.

View file

@ -519,17 +519,20 @@ abstract class ElectrumWalletBase
);
spendsSilentPayment = true;
isSilentPayment = true;
} else {
} else if (!isHardwareWallet) {
privkey =
generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
}
vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout));
inputPrivKeyInfos.add(ECPrivateInfo(
privkey,
address.type == SegwitAddresType.p2tr,
tweak: !isSilentPayment,
));
if (privkey != null) {
inputPrivKeyInfos.add(ECPrivateInfo(
privkey,
address.type == SegwitAddresType.p2tr,
tweak: !isSilentPayment,
));
}
utxos.add(
UtxoWithAddress(
@ -541,7 +544,7 @@ abstract class ElectrumWalletBase
isSilentPayment: isSilentPayment,
),
ownerDetails: UtxoAddressDetails(
publicKey: privkey.getPublic().toHex(),
publicKey: pubKeyHex,
address: address,
),
),

View file

@ -224,6 +224,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
updateAddressesByMatch();
updateReceiveAddresses();
updateChangeAddresses();
_validateAddresses();
await updateAddressesInBox();
if (currentReceiveAddressIndex >= receiveAddresses.length) {
@ -458,10 +459,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
Future<void> discoverAddresses(List<BitcoinAddressRecord> addressList, bool isHidden,
Future<String?> Function(BitcoinAddressRecord) getAddressHistory,
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
if (!isHidden) {
_validateSideHdAddresses(addressList.toList());
}
final newAddresses = await _createNewAddresses(gap,
startIndex: addressList.length, isHidden: isHidden, type: type);
addAddresses(newAddresses);
@ -541,11 +538,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
updateAddressesByMatch();
}
void _validateSideHdAddresses(List<BitcoinAddressRecord> addrWithTransactions) {
addrWithTransactions.forEach((element) {
if (element.address !=
getAddress(index: element.index, hd: mainHd, addressType: element.type))
void _validateAddresses() {
_addresses.forEach((element) {
if (!element.isHidden && element.address !=
getAddress(index: element.index, hd: mainHd, addressType: element.type)) {
element.isHidden = true;
} else if (element.isHidden && element.address !=
getAddress(index: element.index, hd: sideHd, addressType: element.type)) {
element.isHidden = false;
}
});
}

View file

@ -281,7 +281,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
final s = 'Unexpected token: $name for CryptoCurrency fromFullName';
throw ArgumentError.value(name, 'Fullname', s);
}
return CryptoCurrency._fullNameCurrencyMap[name.toLowerCase()]!;
return CryptoCurrency._fullNameCurrencyMap[name.split("(").first.trim().toLowerCase()]!;
}
@override

View file

@ -3,6 +3,7 @@ import 'package:cw_core/keyable.dart';
abstract class TransactionInfo extends Object with Keyable {
late String id;
late String txHash = id;
late int amount;
int? fee;
late TransactionDirection direction;

View file

@ -2,7 +2,7 @@ import 'dart:typed_data';
import 'package:web3dart/web3dart.dart' as web3;
final _contractAbi = web3.ContractAbi.fromJson(
final ethereumContractAbi = web3.ContractAbi.fromJson(
'[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]',
'Erc20');
@ -13,7 +13,7 @@ class ERC20 extends web3.GeneratedContract {
required web3.EthereumAddress address,
required web3.Web3Client client,
int? chainId,
}) : super(web3.DeployedContract(_contractAbi, address), client, chainId);
}) : super(web3.DeployedContract(ethereumContractAbi, address), client, chainId);
/// Returns the remaining number of tokens that [spender] will be allowed to spend on behalf of [owner] through [transferFrom]. This is zero by default. This value changes when [approve] or [transferFrom] are called.
///

View file

@ -10,7 +10,7 @@ import 'package:cw_evm/evm_chain_transaction_priority.dart';
import 'package:cw_evm/evm_erc20_balance.dart';
import 'package:cw_evm/pending_evm_chain_transaction.dart';
import 'package:cw_evm/.secrets.g.dart' as secrets;
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
import 'package:hex/hex.dart' as hex;
import 'package:http/http.dart';
import 'package:web3dart/web3dart.dart';
@ -65,16 +65,65 @@ abstract class EVMChainClient {
Future<int> getGasUnitPrice() async {
try {
final gasPrice = await _client!.getGasPrice();
return gasPrice.getInWei.toInt();
} catch (_) {
return 0;
}
}
Future<int> getEstimatedGas() async {
Future<int?> getGasBaseFee() async {
try {
final estimatedGas = await _client!.estimateGas();
return estimatedGas.toInt();
final blockInfo = await _client!.getBlockInformation(isContainFullObj: false);
final baseFee = blockInfo.baseFeePerGas;
return baseFee?.getInWei.toInt();
} catch (_) {
return 0;
}
}
Future<int> getEstimatedGas({
String? contractAddress,
required EthereumAddress toAddress,
required EthereumAddress senderAddress,
required EtherAmount value,
EtherAmount? gasPrice,
// EtherAmount? maxFeePerGas,
// EtherAmount? maxPriorityFeePerGas,
}) async {
try {
if (contractAddress == null) {
final estimatedGas = await _client!.estimateGas(
sender: senderAddress,
gasPrice: gasPrice,
to: toAddress,
value: value,
// maxPriorityFeePerGas: maxPriorityFeePerGas,
// maxFeePerGas: maxFeePerGas,
);
return estimatedGas.toInt();
} else {
final contract = DeployedContract(
ethereumContractAbi,
EthereumAddress.fromHex(contractAddress),
);
final transfer = contract.function('transfer');
// Estimate gas units
final gasEstimate = await _client!.estimateGas(
sender: senderAddress,
to: EthereumAddress.fromHex(contractAddress),
data: transfer.encodeCall([
toAddress,
value.getInWei,
]),
);
return gasEstimate.toInt();
}
} catch (_) {
return 0;
}
@ -84,7 +133,7 @@ abstract class EVMChainClient {
required Credentials privateKey,
required String toAddress,
required BigInt amount,
required int gas,
required BigInt gas,
required EVMChainTransactionPriority priority,
required CryptoCurrency currency,
required int exponent,
@ -97,8 +146,6 @@ abstract class EVMChainClient {
bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly;
final price = _client!.getGasPrice();
final Transaction transaction = createTransaction(
from: privateKey.address,
to: EthereumAddress.fromHex(toAddress),
@ -130,11 +177,10 @@ abstract class EVMChainClient {
_sendTransaction = () async => await sendTransaction(signedTransaction);
return PendingEVMChainTransaction(
signedTransaction: signedTransaction,
amount: amount.toString(),
fee: BigInt.from(gas) * (await price).getInWei,
fee: gas,
sendTransaction: _sendTransaction,
exponent: exponent,
);
@ -233,7 +279,6 @@ abstract class EVMChainClient {
final decodedResponse = jsonDecode(response.body)[0] as Map<String, dynamic>;
final symbol = (decodedResponse['symbol'] ?? '') as String;
String filteredSymbol = symbol.replaceFirst(RegExp('^\\\$'), '');

View file

@ -27,6 +27,7 @@ import 'package:cw_evm/evm_chain_transaction_priority.dart';
import 'package:cw_evm/evm_chain_wallet_addresses.dart';
import 'package:cw_evm/evm_ledger_credentials.dart';
import 'package:cw_evm/file.dart';
import 'package:flutter/foundation.dart';
import 'package:hex/hex.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
@ -102,10 +103,12 @@ abstract class EVMChainWalletBase
Credentials get evmChainPrivateKey => _evmChainPrivateKey;
late EVMChainClient _client;
late final EVMChainClient _client;
int gasPrice = 0;
int? gasBaseFee = 0;
int estimatedGasUnits = 0;
int? _gasPrice;
int? _estimatedGas;
bool _isTransactionUpdating;
// TODO: remove after integrating our own node and having eth_newPendingTransactionFilter
@ -173,12 +176,70 @@ abstract class EVMChainWalletBase
@override
int calculateEstimatedFee(TransactionPriority priority, int? amount) {
{
try {
if (priority is EVMChainTransactionPriority) {
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
int maxFeePerGas;
if (gasBaseFee != null) {
// MaxFeePerGas with EIP1559;
maxFeePerGas = gasBaseFee! + priorityFee;
} else {
// MaxFeePerGas with gasPrice;
maxFeePerGas = gasPrice;
debugPrint('MaxFeePerGas with gasPrice: $maxFeePerGas');
}
final totalGasFee = estimatedGasUnits * maxFeePerGas;
return totalGasFee;
}
return 0;
} catch (e) {
return 0;
}
}
}
/// Allows more customization to the fetch estimatedFees flow.
///
/// We are able to pass in:
/// - The exact amount the user wants to send,
/// - The addressHex for the receiving wallet,
/// - A contract address which would be essential in determining if to calcualate the estimate for ERC20 or native ETH
Future<int> calculateActualEstimatedFeeForCreateTransaction({
required amount,
required String? contractAddress,
required String receivingAddressHex,
required TransactionPriority priority,
}) async {
try {
if (priority is EVMChainTransactionPriority) {
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
return (_gasPrice! + priorityFee) * (_estimatedGas ?? 0);
}
int maxFeePerGas;
if (gasBaseFee != null) {
// MaxFeePerGas with EIP1559;
maxFeePerGas = gasBaseFee! + priorityFee;
} else {
// MaxFeePerGas with gasPrice
maxFeePerGas = gasPrice;
}
final estimatedGas = await _client.getEstimatedGas(
contractAddress: contractAddress,
senderAddress: _evmChainPrivateKey.address,
value: EtherAmount.fromBigInt(EtherUnit.wei, amount!),
gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
toAddress: EthereumAddress.fromHex(receivingAddressHex),
// maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
// maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
);
final totalGasFee = estimatedGas * maxFeePerGas;
return totalGasFee;
}
return 0;
} catch (e) {
return 0;
@ -225,13 +286,12 @@ abstract class EVMChainWalletBase
syncStatus = AttemptingSyncStatus();
await _updateBalance();
await _updateTransactions();
_gasPrice = await _client.getGasUnitPrice();
_estimatedGas = await _client.getEstimatedGas();
Timer.periodic(
const Duration(minutes: 1), (timer) async => _gasPrice = await _client.getGasUnitPrice());
Timer.periodic(const Duration(seconds: 10),
(timer) async => _estimatedGas = await _client.getEstimatedGas());
await _updateEstimatedGasFeeParams();
Timer.periodic(const Duration(seconds: 10), (timer) async {
await _updateEstimatedGasFeeParams();
});
syncStatus = SyncedSyncStatus();
} catch (e) {
@ -239,6 +299,19 @@ abstract class EVMChainWalletBase
}
}
Future<void> _updateEstimatedGasFeeParams() async {
gasBaseFee = await _client.getGasBaseFee();
gasPrice = await _client.getGasUnitPrice();
estimatedGasUnits = await _client.getEstimatedGas(
senderAddress: _evmChainPrivateKey.address,
toAddress: _evmChainPrivateKey.address,
gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
value: EtherAmount.fromBigInt(EtherUnit.wei, BigInt.one),
);
}
@override
Future<PendingTransaction> createTransaction(Object credentials) async {
final _credentials = credentials as EVMChainTransactionCredentials;
@ -258,8 +331,17 @@ abstract class EVMChainWalletBase
final erc20Balance = balance[transactionCurrency]!;
BigInt totalAmount = BigInt.zero;
BigInt estimatedFeesForTransaction = BigInt.zero;
int exponent = transactionCurrency is Erc20Token ? transactionCurrency.decimal : 18;
num amountToEVMChainMultiplier = pow(10, exponent);
String? contractAddress;
String toAddress = _credentials.outputs.first.isParsedAddress
? _credentials.outputs.first.extractedAddress!
: _credentials.outputs.first.address;
if (transactionCurrency is Erc20Token) {
contractAddress = transactionCurrency.contractAddress;
}
// so far this can not be made with Ethereum as Ethereum does not support multiple recipients
if (hasMultiDestination) {
@ -271,35 +353,48 @@ abstract class EVMChainWalletBase
outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)));
totalAmount = BigInt.from(totalOriginalAmount * amountToEVMChainMultiplier);
final estimateFees = await calculateActualEstimatedFeeForCreateTransaction(
amount: totalAmount,
receivingAddressHex: toAddress,
priority: _credentials.priority!,
contractAddress: contractAddress,
);
estimatedFeesForTransaction = BigInt.from(estimateFees);
if (erc20Balance.balance < totalAmount) {
throw EVMChainTransactionCreationException(transactionCurrency);
}
} else {
final output = outputs.first;
// since the fees are taken from Ethereum
// then no need to subtract the fees from the amount if send all
final BigInt allAmount;
if (transactionCurrency is Erc20Token) {
allAmount = erc20Balance.balance;
} else {
final estimatedFee = BigInt.from(calculateEstimatedFee(_credentials.priority!, null));
if (estimatedFee > erc20Balance.balance) {
throw EVMChainTransactionFeesException();
}
allAmount = erc20Balance.balance - estimatedFee;
}
if (output.sendAll) {
totalAmount = allAmount;
} else {
if (!output.sendAll) {
final totalOriginalAmount =
EVMChainFormatter.parseEVMChainAmountToDouble(output.formattedCryptoAmount ?? 0);
totalAmount = BigInt.from(totalOriginalAmount * amountToEVMChainMultiplier);
}
if (output.sendAll && transactionCurrency is Erc20Token) {
totalAmount = erc20Balance.balance;
}
final estimateFees = await calculateActualEstimatedFeeForCreateTransaction(
amount: totalAmount,
receivingAddressHex: toAddress,
priority: _credentials.priority!,
contractAddress: contractAddress,
);
estimatedFeesForTransaction = BigInt.from(estimateFees);
if (output.sendAll && transactionCurrency is! Erc20Token) {
totalAmount = (erc20Balance.balance - estimatedFeesForTransaction);
if (estimatedFeesForTransaction > erc20Balance.balance) {
throw EVMChainTransactionFeesException();
}
}
if (erc20Balance.balance < totalAmount) {
throw EVMChainTransactionCreationException(transactionCurrency);
}
@ -312,11 +407,9 @@ abstract class EVMChainWalletBase
final pendingEVMChainTransaction = await _client.signTransaction(
privateKey: _evmChainPrivateKey,
toAddress: _credentials.outputs.first.isParsedAddress
? _credentials.outputs.first.extractedAddress!
: _credentials.outputs.first.address,
toAddress: toAddress,
amount: totalAmount,
gas: _estimatedGas!,
gas: estimatedFeesForTransaction,
priority: _credentials.priority!,
currency: transactionCurrency,
exponent: exponent,
@ -483,6 +576,7 @@ abstract class EVMChainWalletBase
return EthPrivateKey.fromHex(HEX.encode(addressAtIndex.privateKey as List<int>));
}
@override
Future<void>? updateBalance() async => await _updateBalance();
List<Erc20Token> get erc20Currencies => evmChainErc20TokensBox.values.toList();

View file

@ -121,9 +121,23 @@ void setRecoveringFromSeed({required bool isRecovery}) =>
monero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery);
final storeMutex = Mutex();
int lastStorePointer = 0;
int lastStoreHeight = 0;
void storeSync() async {
await storeMutex.acquire();
final addr = wptr!.address;
final synchronized = await Isolate.run(() {
return monero.Wallet_synchronized(Pointer.fromAddress(addr));
});
if (lastStorePointer == wptr!.address &&
lastStoreHeight + 5000 < monero.Wallet_blockChainHeight(wptr!) &&
!synchronized) {
return;
}
lastStorePointer = wptr!.address;
lastStoreHeight = monero.Wallet_blockChainHeight(wptr!);
await storeMutex.acquire();
await Isolate.run(() {
monero.Wallet_store(Pointer.fromAddress(addr));
});

View file

@ -21,6 +21,7 @@ final monero.WalletManager wmPtr = Pointer.fromAddress((() {
print("ptr: $_wmPtr");
} catch (e) {
print(e);
rethrow;
}
return _wmPtr!.address;
})());
@ -31,13 +32,14 @@ void createWalletSync(
required String language,
int nettype = 0}) {
txhistory = null;
wptr = monero.WalletManager_createWallet(wmPtr,
final newWptr = monero.WalletManager_createWallet(wmPtr,
path: path, password: password, language: language, networkType: 0);
final status = monero.Wallet_status(wptr!);
final status = monero.Wallet_status(newWptr);
if (status != 0) {
throw WalletCreationException(message: monero.Wallet_errorString(wptr!));
throw WalletCreationException(message: monero.Wallet_errorString(newWptr));
}
wptr = newWptr;
monero.Wallet_store(wptr!, path: path);
openedWalletsByPath[path] = wptr!;
@ -56,7 +58,7 @@ void restoreWalletFromSeedSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
wptr = monero.WalletManager_recoveryWallet(
final newWptr = monero.WalletManager_recoveryWallet(
wmPtr,
path: path,
password: password,
@ -66,12 +68,13 @@ void restoreWalletFromSeedSync(
networkType: 0,
);
final status = monero.Wallet_status(wptr!);
final status = monero.Wallet_status(newWptr);
if (status != 0) {
final error = monero.Wallet_errorString(wptr!);
final error = monero.Wallet_errorString(newWptr);
throw WalletRestoreFromSeedException(message: error);
}
wptr = newWptr;
openedWalletsByPath[path] = wptr!;
}
@ -86,7 +89,7 @@ void restoreWalletFromKeysSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
wptr = monero.WalletManager_createWalletFromKeys(
final newWptr = monero.WalletManager_createWalletFromKeys(
wmPtr,
path: path,
password: password,
@ -97,12 +100,14 @@ void restoreWalletFromKeysSync(
nettype: 0,
);
final status = monero.Wallet_status(wptr!);
final status = monero.Wallet_status(newWptr);
if (status != 0) {
throw WalletRestoreFromKeysException(
message: monero.Wallet_errorString(wptr!));
message: monero.Wallet_errorString(newWptr));
}
wptr = newWptr;
openedWalletsByPath[path] = wptr!;
}
@ -127,7 +132,7 @@ void restoreWalletFromSpendKeySync(
// );
txhistory = null;
wptr = monero.WalletManager_createDeterministicWalletFromSpendKey(
final newWptr = monero.WalletManager_createDeterministicWalletFromSpendKey(
wmPtr,
path: path,
password: password,
@ -137,14 +142,16 @@ void restoreWalletFromSpendKeySync(
restoreHeight: restoreHeight,
);
final status = monero.Wallet_status(wptr!);
final status = monero.Wallet_status(newWptr);
if (status != 0) {
final err = monero.Wallet_errorString(wptr!);
final err = monero.Wallet_errorString(newWptr);
print("err: $err");
throw WalletRestoreFromKeysException(message: err);
}
wptr = newWptr;
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
storeSync();
@ -194,28 +201,25 @@ void loadWallet(
wptr = openedWalletsByPath[path]!;
return;
}
try {
if (wptr == null || path != _lastOpenedWallet) {
if (wptr != null) {
final addr = wptr!.address;
Isolate.run(() {
monero.Wallet_store(Pointer.fromAddress(addr));
});
}
txhistory = null;
wptr = monero.WalletManager_openWallet(wmPtr,
path: path, password: password);
openedWalletsByPath[path] = wptr!;
_lastOpenedWallet = path;
if (wptr == null || path != _lastOpenedWallet) {
if (wptr != null) {
final addr = wptr!.address;
Isolate.run(() {
monero.Wallet_store(Pointer.fromAddress(addr));
});
}
} catch (e) {
print(e);
}
final status = monero.Wallet_status(wptr!);
if (status != 0) {
final err = monero.Wallet_errorString(wptr!);
print(err);
throw WalletOpeningException(message: err);
txhistory = null;
final newWptr = monero.WalletManager_openWallet(wmPtr,
path: path, password: password);
_lastOpenedWallet = path;
final status = monero.Wallet_status(newWptr);
if (status != 0) {
final err = monero.Wallet_errorString(newWptr);
print(err);
throw WalletOpeningException(message: err);
}
wptr = newWptr;
openedWalletsByPath[path] = wptr!;
}
}

View file

@ -124,8 +124,7 @@ abstract class MoneroSubaddressListBase with Store {
Future<List<Subaddress>> _getAllUnusedAddresses(
{required int accountIndex, required String label}) async {
final allAddresses = subaddress_list.getAllSubaddresses();
final lastAddress = allAddresses.last.address;
if (allAddresses.isEmpty || _usedAddresses.contains(lastAddress)) {
if (allAddresses.isEmpty || _usedAddresses.contains(allAddresses.last)) {
final isAddressUnused = await _newSubaddress(accountIndex: accountIndex, label: label);
if (!isAddressUnused) {
return await _getAllUnusedAddresses(accountIndex: accountIndex, label: label);

View file

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_monero/api/structs/transaction_info_row.dart';
@ -7,12 +9,14 @@ import 'package:cw_core/format_amount.dart';
import 'package:cw_monero/api/transaction_history.dart';
class MoneroTransactionInfo extends TransactionInfo {
MoneroTransactionInfo(this.id, this.height, this.direction, this.date,
MoneroTransactionInfo(this.txHash, this.height, this.direction, this.date,
this.isPending, this.amount, this.accountIndex, this.addressIndex, this.fee,
this.confirmations);
this.confirmations) :
id = "${txHash}_${amount}_${accountIndex}_${addressIndex}";
MoneroTransactionInfo.fromMap(Map<String, Object?> map)
: id = (map['hash'] ?? '') as String,
: id = "${map['hash']}_${map['amount']}_${map['accountIndex']}_${map['addressIndex']}",
txHash = map['hash'] as String,
height = (map['height'] ?? 0) as int,
direction = map['direction'] != null
? parseTransactionDirectionFromNumber(map['direction'] as String)
@ -34,7 +38,8 @@ class MoneroTransactionInfo extends TransactionInfo {
}
MoneroTransactionInfo.fromRow(TransactionInfoRow row)
: id = row.getHash(),
: id = "${row.getHash()}_${row.getAmount()}_${row.subaddrAccount}_${row.subaddrIndex}",
txHash = row.getHash(),
height = row.blockHeight,
direction = parseTransactionDirectionFromInt(row.direction),
date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000),
@ -53,6 +58,7 @@ class MoneroTransactionInfo extends TransactionInfo {
}
final String id;
final String txHash;
final int height;
final TransactionDirection direction;
final DateTime date;

View file

@ -126,9 +126,22 @@ void setRecoveringFromSeed({required bool isRecovery}) =>
wownero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery);
final storeMutex = Mutex();
int lastStorePointer = 0;
int lastStoreHeight = 0;
void storeSync() async {
await storeMutex.acquire();
final addr = wptr!.address;
final synchronized = await Isolate.run(() {
return wownero.Wallet_synchronized(Pointer.fromAddress(addr));
});
if (lastStorePointer == wptr!.address &&
lastStoreHeight + 5000 < wownero.Wallet_blockChainHeight(wptr!) &&
!synchronized) {
return;
}
lastStorePointer = wptr!.address;
lastStoreHeight = wownero.Wallet_blockChainHeight(wptr!);
await storeMutex.acquire();
Isolate.run(() {
wownero.Wallet_store(Pointer.fromAddress(addr));
});

View file

@ -21,6 +21,7 @@ final wownero.WalletManager wmPtr = Pointer.fromAddress((() {
print("ptr: $_wmPtr");
} catch (e) {
print(e);
rethrow;
}
return _wmPtr!.address;
})());
@ -31,13 +32,14 @@ void createWalletSync(
required String language,
int nettype = 0}) {
txhistory = null;
wptr = wownero.WalletManager_createWallet(wmPtr,
final newWptr = wownero.WalletManager_createWallet(wmPtr,
path: path, password: password, language: language, networkType: 0);
final status = wownero.Wallet_status(wptr!);
final status = wownero.Wallet_status(newWptr);
if (status != 0) {
throw WalletCreationException(message: wownero.Wallet_errorString(wptr!));
throw WalletCreationException(message: wownero.Wallet_errorString(newWptr));
}
wptr = newWptr;
wownero.Wallet_store(wptr!, path: path);
openedWalletsByPath[path] = wptr!;
@ -55,9 +57,10 @@ void restoreWalletFromSeedSync(
required String seed,
int nettype = 0,
int restoreHeight = 0}) {
var newWptr;
if (seed.split(" ").length == 14) {
txhistory = null;
wptr = wownero.WOWNERO_deprecated_restore14WordSeed(
newWptr = wownero.WOWNERO_deprecated_restore14WordSeed(
path: path,
password: password,
language: seed, // I KNOW - this is supposed to be called seed
@ -69,7 +72,7 @@ void restoreWalletFromSeedSync(
);
} else {
txhistory = null;
wptr = wownero.WalletManager_recoveryWallet(
newWptr = wownero.WalletManager_recoveryWallet(
wmPtr,
path: path,
password: password,
@ -80,13 +83,15 @@ void restoreWalletFromSeedSync(
);
}
final status = wownero.Wallet_status(wptr!);
final status = wownero.Wallet_status(newWptr);
if (status != 0) {
final error = wownero.Wallet_errorString(wptr!);
final error = wownero.Wallet_errorString(newWptr);
throw WalletRestoreFromSeedException(message: error);
}
wptr = newWptr;
openedWalletsByPath[path] = wptr!;
}
@ -100,7 +105,7 @@ void restoreWalletFromKeysSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
wptr = wownero.WalletManager_createWalletFromKeys(
final newWptr = wownero.WalletManager_createWalletFromKeys(
wmPtr,
path: path,
password: password,
@ -111,12 +116,14 @@ void restoreWalletFromKeysSync(
nettype: 0,
);
final status = wownero.Wallet_status(wptr!);
final status = wownero.Wallet_status(newWptr);
if (status != 0) {
throw WalletRestoreFromKeysException(
message: wownero.Wallet_errorString(wptr!));
message: wownero.Wallet_errorString(newWptr));
}
wptr = newWptr;
openedWalletsByPath[path] = wptr!;
}
@ -141,7 +148,7 @@ void restoreWalletFromSpendKeySync(
// );
txhistory = null;
wptr = wownero.WalletManager_createDeterministicWalletFromSpendKey(
final newWptr = wownero.WalletManager_createDeterministicWalletFromSpendKey(
wmPtr,
path: path,
password: password,
@ -151,14 +158,16 @@ void restoreWalletFromSpendKeySync(
restoreHeight: restoreHeight,
);
final status = wownero.Wallet_status(wptr!);
final status = wownero.Wallet_status(newWptr);
if (status != 0) {
final err = wownero.Wallet_errorString(wptr!);
final err = wownero.Wallet_errorString(newWptr);
print("err: $err");
throw WalletRestoreFromKeysException(message: err);
}
wptr = newWptr;
wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
storeSync();
@ -208,28 +217,25 @@ void loadWallet(
wptr = openedWalletsByPath[path]!;
return;
}
try {
if (wptr == null || path != _lastOpenedWallet) {
if (wptr != null) {
final addr = wptr!.address;
Isolate.run(() {
wownero.Wallet_store(Pointer.fromAddress(addr));
});
}
txhistory = null;
wptr = wownero.WalletManager_openWallet(wmPtr,
path: path, password: password);
openedWalletsByPath[path] = wptr!;
_lastOpenedWallet = path;
if (wptr == null || path != _lastOpenedWallet) {
if (wptr != null) {
final addr = wptr!.address;
Isolate.run(() {
wownero.Wallet_store(Pointer.fromAddress(addr));
});
}
} catch (e) {
print(e);
}
final status = wownero.Wallet_status(wptr!);
if (status != 0) {
final err = wownero.Wallet_errorString(wptr!);
print(err);
throw WalletOpeningException(message: err);
txhistory = null;
final newWptr = wownero.WalletManager_openWallet(wmPtr,
path: path, password: password);
_lastOpenedWallet = path;
final status = wownero.Wallet_status(newWptr);
if (status != 0) {
final err = wownero.Wallet_errorString(newWptr);
print(err);
throw WalletOpeningException(message: err);
}
wptr = newWptr;
openedWalletsByPath[path] = wptr!;
}
}

View file

@ -7,12 +7,14 @@ import 'package:cw_core/format_amount.dart';
import 'package:cw_wownero/api/transaction_history.dart';
class WowneroTransactionInfo extends TransactionInfo {
WowneroTransactionInfo(this.id, this.height, this.direction, this.date,
WowneroTransactionInfo(this.txHash, this.height, this.direction, this.date,
this.isPending, this.amount, this.accountIndex, this.addressIndex, this.fee,
this.confirmations);
this.confirmations) :
id = "${txHash}_${amount}_${accountIndex}_${addressIndex}";
WowneroTransactionInfo.fromMap(Map<String, Object?> map)
: id = (map['hash'] ?? '') as String,
: id = "${map['hash']}_${map['amount']}_${map['accountIndex']}_${map['addressIndex']}",
txHash = map['hash'] as String,
height = (map['height'] ?? 0) as int,
direction = map['direction'] != null
? parseTransactionDirectionFromNumber(map['direction'] as String)
@ -34,7 +36,8 @@ class WowneroTransactionInfo extends TransactionInfo {
}
WowneroTransactionInfo.fromRow(TransactionInfoRow row)
: id = row.getHash(),
: id = "${row.getHash()}_${row.getAmount()}_${row.subaddrAccount}_${row.subaddrIndex}",
txHash = row.getHash(),
height = row.blockHeight,
direction = parseTransactionDirectionFromInt(row.direction),
date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000),
@ -53,6 +56,7 @@ class WowneroTransactionInfo extends TransactionInfo {
}
final String id;
final String txHash;
final int height;
final TransactionDirection direction;
final DateTime date;

View file

@ -1,23 +1,19 @@
# Building CakeWallet for Android
# Building Cake Wallet for Android
## Requirements and Setup
The following are the system requirements to build CakeWallet for your Android device.
The following are the system requirements to build Cake Wallet for your Android device.
```
Ubuntu >= 20.04
Android SDK 29 or higher (better to have the latest one 33)
Android NDK 17c
Flutter 3.19.x or earlier
Flutter 3.19.x
```
## Building CakeWallet on Android
These steps will help you configure and execute a build of CakeWallet from its source code.
### 1. Installing Package Dependencies
CakeWallet cannot be built without the following packages installed on your build system.
CakeWallet cannot be built without the following packages installed on your system.
- curl
@ -51,13 +47,17 @@ You may easily install them on your build system with the following command:
### 2. Installing Android Studio and Android toolchain
You may download and install the latest version of Android Studio [here](https://developer.android.com/studio#downloads). After installing, start Android Studio, and go through the "Setup Wizard." This installs the latest Android SDK, Android SDK Command-line Tools, and Android SDK Build-Tools, which are required by CakeWallet. **Be sure you are installing SDK version 28 or later when stepping through the wizard**
You may download and install the latest version of Android Studio [here](https://developer.android.com/studio#downloads). After installing, start Android Studio, and go through the "Setup Wizard." This installs the latest Android SDK, Android SDK Command-line Tools, and Android SDK Build-Tools, which are required by Cake Wallet. **Be sure you are installing SDK version 28 or later when stepping through the wizard**
### 3. Installing Flutter
Need to install flutter with version `3.19.x`. For this please check section [Install Flutter manually](https://docs.flutter.dev/get-started/install/linux#install-flutter-manually).
Install Flutter with version `3.19.x`. For this please check section [Install Flutter manually](https://docs.flutter.dev/get-started/install/linux#install-flutter-manually).
### 4. Verify Installations
### 4. Installing rustup
Install rustup from the [rustup.rs](https://rustup.rs/) website.
### 5. Verify Installations
Verify that the Android toolchain, Flutter, and Android Studio have been correctly installed on your system with the following command:
@ -71,15 +71,15 @@ Doctor summary (to see all details, run flutter doctor -v):
[✓] Android Studio (version 4.0 or higher)
```
### 5. Generate a secure keystore for Android
### 6. Generate a secure keystore for Android
`$ keytool -genkey -v -keystore $HOME/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key`
You will be prompted to create two passwords. First you will be prompted for the "store password", followed by a "key password" towards the end of the creation process. **TAKE NOTE OF THESE PASSWORDS!** You will need them in later steps.
### 6. Acquiring the CakeWallet Source Code
### 7. Acquiring the Cake Wallet Source Code
Create the directory that will be use to store the CakeWallet source...
Create the directory that will be use to store the Cake Wallet source...
```
$ sudo mkdir -p /opt/android
@ -95,11 +95,11 @@ Proceed into the source code before proceeding with the next steps:
`$ cd cake_wallet/scripts/android/`
### 7. Installing Android NDK
### 8. Installing Android NDK
`$ ./install_ndk.sh`
### 8. Execute Build & Setup Commands for CakeWallet
### 9. Execute Build & Setup Commands for Cak eWallet
We need to generate project settings like app name, app icon, package name, etc. For this need to setup environment variables and configure project files.
@ -116,7 +116,7 @@ Build the Monero libraries and their dependencies:
`$ ./build_all.sh`
It is now time to change back to the base directory of the CakeWallet source code:
It is now time to change back to the base directory of the Cake Wallet source code:
`$ cd ../../`
@ -124,7 +124,7 @@ Install Flutter package dependencies with this command:
`$ flutter pub get`
Your CakeWallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command:
Your Cake Wallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command:
`$ flutter packages pub run tool/generate_new_secrets.dart`
@ -142,8 +142,8 @@ Finally build mobx models for the app:
`$ ./model_generator.sh`
### 9. Build!
### 10. Build!
`$ flutter build apk --release`
Copyright (c) 2022 Cake Technologies LLC.
Copyright (c) 2024 Cake Labs LLC

View file

@ -1,8 +1,8 @@
# Building CakeWallet for iOS
# Building Cake Wallet for iOS
## Requirements and Setup
The following are the system requirements to build CakeWallet for your iOS device.
The following are the system requirements to build Cake Wallet for your iOS device.
```
macOS >= 14.0
@ -10,13 +10,9 @@ Xcode 15.3
Flutter 3.19.x
```
## Building CakeWallet on iOS
These steps will help you configure and execute a build of CakeWallet from its source code.
### 1. Installing Package Dependencies
CakeWallet cannot be built without the following packages installed on your build system.
Cake Wallet cannot be built without the following packages installed on your build system.
For installing dependency tools you can use brew [Install brew](https://brew.sh).
@ -32,7 +28,11 @@ You may download and install the latest version of [Xcode](https://developer.app
Need to install flutter with version `3.19.x`. For this please check section [Install Flutter](https://docs.flutter.dev/get-started/install/macos/mobile-ios?tab=download).
### 4. Verify Installations
### 4. Installing rustup
Install rustup from the [rustup.rs](https://rustup.rs/) website.
### 5. Verify Installations
Verify that the Flutter and Xcode have been correctly installed on your system with the following command:
@ -45,7 +45,7 @@ Doctor summary (to see all details, run flutter doctor -v):
[✓] Xcode - develop for iOS and macOS (Xcode 15.3)
```
### 5. Acquiring the CakeWallet source code
### 6. Acquiring the CakeWallet source code
Download the source code.
@ -55,7 +55,7 @@ Proceed into the source code before proceeding with the next steps:
`$ cd cake_wallet/scripts/ios/`
### 6. Execute Build & Setup Commands for CakeWallet
### 7. Execute Build & Setup Commands for Cake Wallet
We need to generate project settings like app name, app icon, package name, etc. For this need to setup environment variables and configure project files.
@ -72,7 +72,7 @@ Build the Monero libraries and their dependencies:
`$ ./build_monero_all.sh`
It is now time to change back to the base directory of the CakeWallet source code:
It is now time to change back to the base directory of the Cake Wallet source code:
`$ cd ../../`
@ -80,7 +80,7 @@ Install Flutter package dependencies with this command:
`$ flutter pub get`
Your CakeWallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command:
Your Cake Wallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command:
`$ flutter packages pub run tool/generate_new_secrets.dart`
@ -88,7 +88,7 @@ Then we need to generate localization files and mobx models.
`$ ./configure_cake_wallet.sh ios`
### 7. Build!
### 8. Build!
`$ flutter build ios --release`
@ -98,4 +98,4 @@ Or if you want to run to connected device:
`$ flutter run --release`
Copyright (c) 2024 Cake Technologies LLC.
Copyright (c) 2024 Cake Labs LLC

View file

@ -1,8 +1,8 @@
# Building CakeWallet for macOS
# Building Cake Wallet for macOS
## Requirements and Setup
The following are the system requirements to build CakeWallet for your macOS device.
The following are the system requirements to build Cake Wallet for your macOS device.
```
macOS >= 14.0
@ -10,13 +10,9 @@ Xcode 15.3
Flutter 3.19.x
```
## Building CakeWallet on macOS
These steps will help you configure and execute a build of CakeWallet from its source code.
### 1. Installing Package Dependencies
CakeWallet cannot be built without the following packages installed on your build system.
Cake Wallet cannot be built without the following packages installed on your build system.
For installing dependency tools you can use brew [Install brew](https://brew.sh).
@ -34,7 +30,11 @@ You may download and install the latest version of [Xcode](https://developer.app
Need to install flutter with version `3.19.x`. For this please check section [Install Flutter](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
### 4. Verify Installations
### 4. Installing rustup
Install rustup from the [rustup.rs](https://rustup.rs/) website.
### 5. Verify Installations
Verify that Flutter and Xcode have been correctly installed on your system with the following command:
@ -47,7 +47,7 @@ Doctor summary (to see all details, run flutter doctor -v):
[✓] Xcode - develop for iOS and macOS (Xcode 15.3)
```
### 5. Acquiring the CakeWallet source code
### 6. Acquiring the Cake Wallet source code
Download the source code.
@ -57,7 +57,7 @@ Proceed into the source code before proceeding with the next steps:
`$ cd cake_wallet/scripts/macos/`
### 6. Execute Build & Setup Commands for CakeWallet
### 7. Execute Build & Setup Commands for Cake Wallet
We need to generate project settings like app name, app icon, package name, etc. For this need to setup environment variables and configure project files.
@ -83,7 +83,7 @@ If you be needed to build universal monero lib, then it will require additional
If you will be needed to build monero wallet lib only for x86_64 on arm64 mac, then you need use steps above, but run build script with rosetta without arguments: `$ arch -x86_64 ./build_monero_all.sh`.
It is now time to change back to the base directory of the CakeWallet source code:
It is now time to change back to the base directory of the Cake Wallet source code:
`$ cd ../../`
@ -91,7 +91,7 @@ Install Flutter package dependencies with this command:
`$ flutter pub get`
Your CakeWallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command:
Your Cake Wallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command:
`$ flutter packages pub run tool/generate_new_secrets.dart`
@ -99,7 +99,7 @@ Then we need to generate localization files and mobx models.
`$ ./configure_cake_wallet.sh macos`
### 7. Build!
### 8. Build!
`$ flutter build macos --release`
@ -109,4 +109,4 @@ Or if you want to run to connected device:
`$ flutter run --release`
Copyright (c) 2024 Cake Technologies LLC.
Copyright (c) 2024 Cake Labs LLC

57
howto-build-windows.md Normal file
View file

@ -0,0 +1,57 @@
# Building Cake Wallet for Windows
## Requirements and Setup
The following are the system requirements to build CakeWallet for your Windows PC.
```
Windows 10 or later (64-bit), x86-64 based
Flutter 3.19.x
```
### 1. Installing Flutter
Install Flutter with version `3.19.x`. Follow the Flutter [installation guide](https://docs.flutter.dev/get-started/install/windows).
### 2. Install Development Tools
Install Git for Windows and Visual Studio 2022. Follow the [Development Tools](https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) installation instructions.
Then install `Desktop development with C++` packages via Visual Studio 2022, or Visual Studio Build Tools 2022 including:
- `C++ Build Tools core features`
- `C++ 2022 Redistributable Update`
- `C++ core desktop features`
- `MVC v143 - VS 2022 C++ x64/x86 build tools`
- `C++ CMake tools for Windwos`
- `Testing tools core features - Build Tools`
- `C++ AddressSanitizer`.
### 3. Installing rustup
Install rustup from the [rustup.rs](https://rustup.rs/#) website. Download and run the 64-bit rustup-init.exe
### 4. Installing WSL (Windows Subsystem for Linux)
For building monero dependencies, it is required to install Windows WSL (https://learn.microsoft.com/en-us/windows/wsl/install) and required packages for WSL (Ubuntu):
`$ sudo apt update `
`$ sudo apt build-essential cmake gcc-mingw-w64 g++-mingw-w64 autoconf libtool pkg-config`
### 5. Pull Cake Wallet source code
You can downlaod CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git:
`$ git clone https://github.com/cake-tech/cake_wallet.git --branch MrCyjaneK-cyjan-monerodart`
OR you can download it as [Zip archive](https://github.com/cake-tech/cake_wallet/archive/refs/heads/MrCyjaneK-cyjan-monerodart.zip)
### 6. Build Monero, monero_c and their dependencies
To use Monero in Cake Wallet, you must build the Monero_C wrapper which will be used by monero.dart package.
For that you need to run the shell (bash - typically same named utility should be available after WSL is enabled in your system) with the previously installed WSL install, then change current directory to the application project directory with your shell then change current directory to `scripts/windows`: `$ cd scripts/windows`. Run build script: `$ ./build_all.sh`.
### 7. Configure and build Cake Wallet application
To configure the application, open the directory where you have downloaded or unarchived Cake Wallet sources and run `cakewallet.bat`.
Or if you used WSL and have active shell session you can run `$ ./cakewallet.sh` script in `scripts/windows` which will run `cakewallet.bat` in WSL.
After execution of `cakewallet.bat` you should to get `Cake Wallet.zip` in project root directory which will contains `CakeWallet.exe` file and another needed files for run the application. Now you can extract files from `Cake Wallet.zip` archive and run the application.
Copyright (c) 2024 Cake Labs LLC.

View file

@ -96,8 +96,6 @@ PODS:
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sp_scanner (0.0.1):
- Flutter
- SwiftProtobuf (1.26.0)
- SwiftyGif (5.4.5)
- Toast (4.1.1)
@ -137,7 +135,6 @@ DEPENDENCIES:
- sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sp_scanner (from `.symlinks/plugins/sp_scanner/ios`)
- uni_links (from `.symlinks/plugins/uni_links/ios`)
- UnstoppableDomainsResolution (~> 4.0.0)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@ -205,8 +202,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sp_scanner:
:path: ".symlinks/plugins/sp_scanner/ios"
uni_links:
:path: ".symlinks/plugins/uni_links/ios"
url_launcher_ios:
@ -248,7 +243,6 @@ SPEC CHECKSUMS:
sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12
SwiftProtobuf: 5e8349171e7c2f88f5b9e683cb3cb79d1dc780b3
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e

View file

@ -560,7 +560,7 @@ class CWBitcoin extends Bitcoin {
if (tweaksResponse != null) {
return true;
}
} on RequestFailedTimeoutException {
} on RequestFailedTimeoutException catch (_) {
return false;
} catch (_) {
rethrow;

View file

@ -149,10 +149,9 @@ class MoonPayProvider extends BuyProvider {
'colorCode': settingsStore.currentTheme.type == ThemeType.dark
? '#${Palette.blueCraiola.value.toRadixString(16).substring(2, 8)}'
: '#${Palette.moderateSlateBlue.value.toRadixString(16).substring(2, 8)}',
'defaultCurrencyCode': _normalizeCurrency(currency),
'baseCurrencyCode': _normalizeCurrency(currency),
'baseCurrencyCode': settingsStore.fiatCurrency.title,
'baseCurrencyAmount': amount ?? '0',
'currencyCode': currencyCode,
'currencyCode': _normalizeCurrency(currency),
'walletAddress': walletAddress,
'lockAmount': 'false',
'showAllCurrencies': 'false',

View file

@ -678,7 +678,7 @@ Future<void> setup({
getIt.registerFactory<MoneroAccountListViewModel>(() {
final wallet = getIt.get<AppStore>().wallet!;
if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) {
if (wallet.type == WalletType.monero || wallet.type == WalletType.wownero || wallet.type == WalletType.haven) {
return MoneroAccountListViewModel(wallet);
}
throw Exception(
@ -709,6 +709,7 @@ Future<void> setup({
getIt.registerFactoryParam<MoneroAccountEditOrCreateViewModel, AccountListItem?, void>(
(AccountListItem? account, _) => MoneroAccountEditOrCreateViewModel(
monero!.getAccountList(getIt.get<AppStore>().wallet!),
wownero?.getAccountList(getIt.get<AppStore>().wallet!),
haven?.getAccountList(getIt.get<AppStore>().wallet!),
wallet: getIt.get<AppStore>().wallet!,
accountListItem: account));

View file

@ -391,7 +391,14 @@ Future<void> ios_migrate_trades_list(Box<Trade> tradeSource) async {
}
return Trade(
id: tradeId, provider: provider!, from: from, to: to, createdAt: date, amount: '');
id: tradeId,
provider: provider!,
from: from,
to: to,
createdAt: date,
amount: '',
receiveAmount: '',
);
});
await tradeSource.addAll(trades);
await prefs.setBool('ios_migration_trade_list_completed', true);

View file

@ -67,9 +67,8 @@ class ProvidersHelper {
];
case WalletType.litecoin:
case WalletType.bitcoinCash:
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood, ProviderType.moonpay];
case WalletType.solana:
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood];
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood, ProviderType.moonpay];
case WalletType.tron:
return [
ProviderType.askEachTime,

View file

@ -194,20 +194,24 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final refundAddress = responseJSON['refundAddress'] as String;
final extraId = responseJSON['payinExtraId'] as String?;
final payoutAddress = responseJSON['payoutAddress'] as String;
final fromAmount = responseJSON['fromAmount']?.toString();
final toAmount = responseJSON['toAmount']?.toString();
return Trade(
id: id,
from: request.fromCurrency,
to: request.toCurrency,
provider: description,
inputAddress: inputAddress,
refundAddress: refundAddress,
extraId: extraId,
createdAt: DateTime.now(),
amount: responseJSON['fromAmount']?.toString() ?? request.fromAmount,
state: TradeState.created,
payoutAddress: payoutAddress,
isSendAll: isSendAll);
id: id,
from: request.fromCurrency,
to: request.toCurrency,
provider: description,
inputAddress: inputAddress,
refundAddress: refundAddress,
extraId: extraId,
createdAt: DateTime.now(),
amount: fromAmount ?? request.fromAmount,
receiveAmount: toAmount ?? request.toAmount,
state: TradeState.created,
payoutAddress: payoutAddress,
isSendAll: isSendAll,
);
}
@override

View file

@ -172,20 +172,23 @@ class ExolixExchangeProvider extends ExchangeProvider {
final extraId = responseJSON['depositExtraId'] as String?;
final payoutAddress = responseJSON['withdrawalAddress'] as String;
final amount = responseJSON['amount'].toString();
final receiveAmount = responseJSON['amountTo']?.toString();
return Trade(
id: id,
from: request.fromCurrency,
to: request.toCurrency,
provider: description,
inputAddress: inputAddress,
refundAddress: refundAddress,
extraId: extraId,
createdAt: DateTime.now(),
amount: amount,
state: TradeState.created,
payoutAddress: payoutAddress,
isSendAll: isSendAll);
id: id,
from: request.fromCurrency,
to: request.toCurrency,
provider: description,
inputAddress: inputAddress,
refundAddress: refundAddress,
extraId: extraId,
createdAt: DateTime.now(),
amount: amount,
receiveAmount:receiveAmount ?? request.toAmount,
state: TradeState.created,
payoutAddress: payoutAddress,
isSendAll: isSendAll,
);
}
@override

View file

@ -162,11 +162,13 @@ class QuantexExchangeProvider extends ExchangeProvider {
throw Exception('Unexpected http status: ${response.statusCode}');
final responseData = responseBody['data'] as Map<String, dynamic>;
final receiveAmount = responseData["amount_receive"]?.toString();
return Trade(
id: responseData["order_id"] as String,
inputAddress: responseData["server_address"] as String,
amount: request.fromAmount,
receiveAmount: receiveAmount ?? request.toAmount,
from: request.fromCurrency,
to: request.toCurrency,
provider: description,

View file

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:developer';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
@ -29,6 +30,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
CryptoCurrency.bttc,
CryptoCurrency.usdt,
CryptoCurrency.eos,
CryptoCurrency.xmr,
];
static const affiliateId = secrets.sideShiftAffiliateId;
@ -137,8 +139,20 @@ class SideShiftExchangeProvider extends ExchangeProvider {
final response = await get(uri);
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
if (response.statusCode == 500) {
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final error = responseJSON['error']['message'] as String;
throw Exception('SideShift Internal Server Error: $error');
}
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
return double.parse(responseJSON['rate'] as String);
} catch (_) {
} catch (e) {
log('Error fetching rate in SideShift Provider: ${e.toString()}');
return 0.00;
}
}
@ -199,6 +213,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
refundAddress: settleAddress,
state: TradeState.created,
amount: depositAmount ?? request.fromAmount,
receiveAmount: request.toAmount,
payoutAddress: settleAddress,
createdAt: DateTime.now(),
isSendAll: isSendAll,

View file

@ -153,6 +153,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
final payoutAddress = responseJSON['address_to'] as String;
final settleAddress = responseJSON['user_refund_address'] as String;
final extraId = responseJSON['extra_id_from'] as String?;
final receiveAmount = responseJSON['amount_to'] as String?;
return Trade(
id: id,
@ -164,6 +165,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
extraId: extraId,
state: TradeState.created,
amount: request.fromAmount,
receiveAmount: receiveAmount ?? request.toAmount,
payoutAddress: payoutAddress,
createdAt: DateTime.now(),
isSendAll: isSendAll,

View file

@ -40,7 +40,7 @@ class ThorChainExchangeProvider extends ExchangeProvider {
static const _txInfoPath = '/thorchain/tx/status/';
static const _affiliateName = 'cakewallet';
static const _affiliateBps = '175';
static const _nameLookUpPath= 'v2/thorname/lookup/';
static const _nameLookUpPath = 'v2/thorname/lookup/';
final Box<Trade> tradesStore;
@ -137,19 +137,27 @@ class ThorChainExchangeProvider extends ExchangeProvider {
final inputAddress = responseJSON['inbound_address'] as String?;
final memo = responseJSON['memo'] as String?;
final directAmountOutResponse = responseJSON['expected_amount_out'] as String?;
String? receiveAmount;
if (directAmountOutResponse != null) {
receiveAmount = _thorChainAmountToDouble(directAmountOutResponse).toString();
}
return Trade(
id: '',
from: request.fromCurrency,
to: request.toCurrency,
provider: description,
inputAddress: inputAddress,
createdAt: DateTime.now(),
amount: request.fromAmount,
state: TradeState.notFound,
payoutAddress: request.toAddress,
memo: memo,
isSendAll: isSendAll);
id: '',
from: request.fromCurrency,
to: request.toCurrency,
provider: description,
inputAddress: inputAddress,
createdAt: DateTime.now(),
amount: request.fromAmount,
receiveAmount: receiveAmount ?? request.toAmount,
state: TradeState.notFound,
payoutAddress: request.toAddress,
memo: memo,
isSendAll: isSendAll,
);
}
@override
@ -234,7 +242,6 @@ class ThorChainExchangeProvider extends ExchangeProvider {
return chainToAddressMap;
}
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
Uri uri = Uri.https(_baseNodeURL, _quotePath, params);

View file

@ -224,22 +224,26 @@ class TrocadorExchangeProvider extends ExchangeProvider {
final password = responseJSON['password'] as String;
final providerId = responseJSON['id_provider'] as String;
final providerName = responseJSON['provider'] as String;
final amount = responseJSON['amount_from']?.toString();
final receiveAmount = responseJSON['amount_to']?.toString();
return Trade(
id: id,
from: request.fromCurrency,
to: request.toCurrency,
provider: description,
inputAddress: inputAddress,
refundAddress: refundAddress,
state: TradeState.deserialize(raw: status),
password: password,
providerId: providerId,
providerName: providerName,
createdAt: DateTime.tryParse(date)?.toLocal(),
amount: responseJSON['amount_from']?.toString() ?? request.fromAmount,
payoutAddress: payoutAddress,
isSendAll: isSendAll);
id: id,
from: request.fromCurrency,
to: request.toCurrency,
provider: description,
inputAddress: inputAddress,
refundAddress: refundAddress,
state: TradeState.deserialize(raw: status),
password: password,
providerId: providerId,
providerName: providerName,
createdAt: DateTime.tryParse(date)?.toLocal(),
amount: amount ?? request.fromAmount,
receiveAmount: receiveAmount ?? request.toAmount,
payoutAddress: payoutAddress,
isSendAll: isSendAll,
);
}
@override

View file

@ -13,6 +13,7 @@ class Trade extends HiveObject {
CryptoCurrency? from,
CryptoCurrency? to,
TradeState? state,
this.receiveAmount,
this.createdAt,
this.expiredAt,
this.inputAddress,
@ -122,6 +123,9 @@ class Trade extends HiveObject {
@HiveField(22)
String? router;
@HiveField(23, defaultValue: '')
String? receiveAmount;
static Trade fromMap(Map<String, Object?> map) {
return Trade(
id: map['id'] as String,
@ -131,6 +135,7 @@ class Trade extends HiveObject {
createdAt:
map['date'] != null ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) : null,
amount: map['amount'] as String,
receiveAmount: map['receive_amount'] as String?,
walletId: map['wallet_id'] as String,
fromWalletAddress: map['from_wallet_address'] as String?,
memo: map['memo'] as String?,
@ -149,6 +154,7 @@ class Trade extends HiveObject {
'output': to.serialize(),
'date': createdAt != null ? createdAt!.millisecondsSinceEpoch : null,
'amount': amount,
'receive_amount': receiveAmount,
'wallet_id': walletId,
'from_wallet_address': fromWalletAddress,
'memo': memo,
@ -179,6 +185,7 @@ class TradeAdapter extends TypeAdapter<Trade> {
return Trade(
id: fields[0] == null ? '' : fields[0] as String,
amount: fields[7] == null ? '' : fields[7] as String,
receiveAmount: fields[23] as String?,
createdAt: fields[5] as DateTime?,
expiredAt: fields[6] as DateTime?,
inputAddress: fields[8] as String?,
@ -206,7 +213,7 @@ class TradeAdapter extends TypeAdapter<Trade> {
@override
void write(BinaryWriter writer, Trade obj) {
writer
..writeByte(23)
..writeByte(24)
..writeByte(0)
..write(obj.id)
..writeByte(1)
@ -252,7 +259,9 @@ class TradeAdapter extends TypeAdapter<Trade> {
..writeByte(21)
..write(obj.isSendAll)
..writeByte(22)
..write(obj.router);
..write(obj.router)
..writeByte(23)
..write(obj.receiveAmount);
}
@override

View file

@ -9,18 +9,18 @@ import 'package:cake_wallet/src/screens/cake_pay/widgets/link_extractor.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/text_icon_button.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.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/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/themes/extensions/picker_theme.dart';
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
@ -372,9 +372,7 @@ class CakePayBuyCardDetailPage extends BasePage {
if (state is TransactionCommitted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
cakePayPurchaseViewModel.sendViewModel.clearOutputs();
if (context.mounted) {
showStateAlert(context, S.of(context).sending, S.of(context).transaction_sent);
}
if (context.mounted) showSentAlert(context);
});
}
});
@ -394,6 +392,27 @@ class CakePayBuyCardDetailPage extends BasePage {
});
}
Future<void> showSentAlert(BuildContext context) async {
final order = cakePayPurchaseViewModel.order!.orderId;
final isCopy = await showPopUp<bool>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).transaction_sent,
alertContent:
S.of(context).cake_pay_save_order + '\n${order}',
leftButtonText: S.of(context).ignor,
rightButtonText: S.of(context).copy,
actionLeftButton: () => Navigator.of(context).pop(false),
actionRightButton: () => Navigator.of(context).pop(true));
}) ??
false;
if (isCopy) {
await Clipboard.setData(ClipboardData(text: order));
}
}
void _handleDispose(ReactionDisposer? disposer) {
cakePayPurchaseViewModel.dispose();
if (disposer != null) {

View file

@ -340,7 +340,7 @@ class CryptoBalanceWidget extends StatelessWidget {
builder: (BuildContext context) => AlertWithTwoActions(
alertTitle: S.of(context).change_current_node_title,
alertContent: S.of(context).confirm_silent_payments_switch_node,
rightButtonText: S.of(context).ok,
rightButtonText: S.of(context).confirm,
leftButtonText: S.of(context).cancel,
actionRightButton: () {
dashboardViewModel.setSilentPaymentsScanning(newValue);

View file

@ -139,6 +139,7 @@ class ReceivePage extends BasePage {
walletAddressListViewModel: addressListViewModel,
trailingButtonTap: () async {
if (addressListViewModel.type == WalletType.monero ||
addressListViewModel.type == WalletType.wownero ||
addressListViewModel.type == WalletType.haven) {
await showPopUp<void>(
context: context,

View file

@ -22,38 +22,42 @@ class RescanPage extends BasePage {
@override
Widget body(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Observer(
builder: (_) => BlockchainHeightWidget(
key: _blockchainHeightWidgetKey,
onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value,
isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan,
doSingleScan: _rescanViewModel.doSingleScan,
toggleSingleScan: () =>
_rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan,
walletType: _rescanViewModel.wallet.type,
)),
Observer(
builder: (_) => LoadingPrimaryButton(
isLoading: _rescanViewModel.state == RescanWalletState.rescaning,
text: S.of(context).rescan,
onPressed: () async {
if (_rescanViewModel.isSilentPaymentsScan) {
return _toggleSilentPaymentsScanning(context);
}
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => FocusScope.of(context).unfocus(),
child: Padding(
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Observer(
builder: (_) => BlockchainHeightWidget(
key: _blockchainHeightWidgetKey,
onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value,
isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan,
doSingleScan: _rescanViewModel.doSingleScan,
toggleSingleScan: () =>
_rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan,
walletType: _rescanViewModel.wallet.type,
)),
Observer(
builder: (_) => LoadingPrimaryButton(
isLoading: _rescanViewModel.state == RescanWalletState.rescaning,
text: S.of(context).rescan,
onPressed: () async {
if (_rescanViewModel.isSilentPaymentsScan) {
return _toggleSilentPaymentsScanning(context);
}
_rescanViewModel.rescanCurrentWallet(
restoreHeight: _blockchainHeightWidgetKey.currentState!.height);
_rescanViewModel.rescanCurrentWallet(
restoreHeight: _blockchainHeightWidgetKey.currentState!.height);
Navigator.of(context).pop();
},
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isDisabled: !_rescanViewModel.isButtonEnabled,
))
]),
Navigator.of(context).pop();
},
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isDisabled: !_rescanViewModel.isButtonEnabled,
))
]),
),
);
}
@ -71,7 +75,7 @@ class RescanPage extends BasePage {
builder: (BuildContext _dialogContext) => AlertWithTwoActions(
alertTitle: S.of(_dialogContext).change_current_node_title,
alertContent: S.of(_dialogContext).confirm_silent_payments_switch_node,
rightButtonText: S.of(_dialogContext).ok,
rightButtonText: S.of(_dialogContext).confirm,
leftButtonText: S.of(_dialogContext).cancel,
actionRightButton: () async {
Navigator.of(_dialogContext).pop();

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
@ -49,117 +50,115 @@ class WelcomePage extends BasePage {
@override
Widget body(BuildContext context) {
final welcomeImage = currentTheme.type == ThemeType.dark
? welcomeImageDark
: welcomeImageLight;
final welcomeImage = currentTheme.type == ThemeType.dark ? welcomeImageDark : welcomeImageLight;
final newWalletImage = Image.asset('assets/images/new_wallet.png',
height: 12,
width: 12,
color: Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor);
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
height: 12,
width: 12,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor);
height: 12, width: 12, color: Theme.of(context).extension<CakeTextTheme>()!.titleColor);
return WillPopScope(
onWillPop: () async => false,
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24),
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: <Widget>[
AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(
child: welcomeImage, fit: BoxFit.contain),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: Text(
S.of(context).welcome,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appTitle(context),
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appDescription(context),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor,
),
textAlign: TextAlign.center,
),
),
],
),
Column(
children: <Widget>[
Text(
S.of(context).please_make_selection,
onWillPop: () async => false,
child: ScrollableWithBottomSection(
content: Container(
alignment: Alignment.center,
padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24),
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: <Widget>[
AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: welcomeImage, fit: BoxFit.contain),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: Text(
S.of(context).welcome,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor,
),
textAlign: TextAlign.center,
),
Padding(
padding: EdgeInsets.only(top: 24),
child: PrimaryImageButton(
key: ValueKey('welcome_page_create_new_wallet_button_key'),
onPressed: () => Navigator.pushNamed(
context, Routes.newWalletFromWelcome),
image: newWalletImage,
text: S.of(context).create_new,
color: Theme.of(context).extension<WalletListTheme>()!.createNewWalletButtonBackgroundColor,
textColor: Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor,
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appTitle(context),
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
textAlign: TextAlign.center,
),
Padding(
padding: EdgeInsets.only(top: 10),
child: PrimaryImageButton(
key: ValueKey('welcome_page_restore_wallet_button_key'),
onPressed: () {
Navigator.pushNamed(
context, Routes.restoreOptions,
arguments: true);
},
image: restoreWalletImage,
text: S.of(context).restore_wallet,
color: Theme.of(context).cardColor,
textColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
)
],
)
],
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appDescription(context),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor,
),
textAlign: TextAlign.center,
),
),
],
),
],
),
),
),
bottomSection: Column(
children: <Widget>[
Text(
S.of(context).please_make_selection,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor,
),
)));
textAlign: TextAlign.center,
),
Padding(
padding: EdgeInsets.only(top: 24),
child: PrimaryImageButton(
key: ValueKey('welcome_page_create_new_wallet_button_key'),
onPressed: () => Navigator.pushNamed(context, Routes.newWalletFromWelcome),
image: newWalletImage,
text: S.of(context).create_new,
color: Theme.of(context)
.extension<WalletListTheme>()!
.createNewWalletButtonBackgroundColor,
textColor:
Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor,
),
),
Padding(
padding: EdgeInsets.only(top: 10),
child: PrimaryImageButton(
key: ValueKey('welcome_page_restore_wallet_button_key'),
onPressed: () {
Navigator.pushNamed(context, Routes.restoreOptions, arguments: true);
},
image: restoreWalletImage,
text: S.of(context).restore_wallet,
color: Theme.of(context).cardColor,
textColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
)
],
),
),
);
}
}

View file

@ -65,89 +65,93 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
child: BaseTextFormField(
focusNode: widget.focusNode,
controller: restoreHeightController,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false),
hintText: widget.isSilentPaymentsScan
? S.of(context).silent_payments_scan_from_height
: S.of(context).widgets_restore_from_blockheight,
)))
],
),
if (widget.hasDatePicker) ...[
Padding(
padding: EdgeInsets.only(top: 15, bottom: 15),
child: Text(
S.of(context).widgets_or,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
),
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => FocusScope.of(context).unfocus(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: Container(
child: InkWell(
onTap: () => _selectDate(context),
child: IgnorePointer(
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
child: BaseTextFormField(
controller: dateController,
hintText: widget.isSilentPaymentsScan
? S.of(context).silent_payments_scan_from_date
: S.of(context).widgets_restore_from_date,
)),
),
))
focusNode: widget.focusNode,
controller: restoreHeightController,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false),
hintText: widget.isSilentPaymentsScan
? S.of(context).silent_payments_scan_from_height
: S.of(context).widgets_restore_from_blockheight,
)))
],
),
if (widget.isSilentPaymentsScan)
if (widget.hasDatePicker) ...[
Padding(
padding: EdgeInsets.only(top: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).scan_one_block,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
Padding(
padding: const EdgeInsets.only(right: 8),
child: StandardSwitch(
value: widget.doSingleScan,
onTaped: () => widget.toggleSingleScan?.call(),
),
)
],
padding: EdgeInsets.only(top: 15, bottom: 15),
child: Text(
S.of(context).widgets_or,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
),
Padding(
padding: EdgeInsets.only(left: 40, right: 40, top: 24),
child: Text(
widget.isSilentPaymentsScan
? S.of(context).silent_payments_scan_from_date_or_blockheight
: S.of(context).restore_from_date_or_blockheight,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12, fontWeight: FontWeight.normal, color: Theme.of(context).hintColor),
Row(
children: <Widget>[
Flexible(
child: Container(
child: InkWell(
onTap: () => _selectDate(context),
child: IgnorePointer(
child: BaseTextFormField(
controller: dateController,
hintText: widget.isSilentPaymentsScan
? S.of(context).silent_payments_scan_from_date
: S.of(context).widgets_restore_from_date,
)),
),
))
],
),
)
]
],
if (widget.isSilentPaymentsScan)
Padding(
padding: EdgeInsets.only(top: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).scan_one_block,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
Padding(
padding: const EdgeInsets.only(right: 8),
child: StandardSwitch(
value: widget.doSingleScan,
onTaped: () => widget.toggleSingleScan?.call(),
),
)
],
),
),
Padding(
padding: EdgeInsets.only(left: 40, right: 40, top: 24),
child: Text(
widget.isSilentPaymentsScan
? S.of(context).silent_payments_scan_from_date_or_blockheight
: S.of(context).restore_from_date_or_blockheight,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12, fontWeight: FontWeight.normal, color: Theme.of(context).hintColor),
),
)
]
],
),
);
}

View file

@ -1184,9 +1184,11 @@ abstract class SettingsStoreBase with Store {
raw: sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ??
priority[WalletType.monero]!;
priority[WalletType.wownero] = wownero?.deserializeWowneroTransactionPriority(
raw: sharedPreferences.getInt(PreferencesKey.wowneroTransactionPriority)!) ??
priority[WalletType.wownero]!;
if (wownero != null &&
sharedPreferences.getInt(PreferencesKey.wowneroTransactionPriority) != null) {
priority[WalletType.wownero] = wownero!.deserializeWowneroTransactionPriority(
raw: sharedPreferences.getInt(PreferencesKey.wowneroTransactionPriority)!);
}
if (bitcoin != null &&
sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority) != null) {

View file

@ -61,7 +61,7 @@ abstract class BalanceViewModelBase with Store {
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
@computed
bool get hasSilentPayments => wallet.type == WalletType.bitcoin;
bool get hasSilentPayments => wallet.type == WalletType.bitcoin && !wallet.isHardwareWallet;
@computed
double get price {
@ -88,7 +88,7 @@ abstract class BalanceViewModelBase with Store {
wallet.type == WalletType.tron;
@computed
bool get hasAccounts => wallet.type == WalletType.monero;
bool get hasAccounts => wallet.type == WalletType.monero || wallet.type == WalletType.wownero;
@computed
SortBalanceBy get sortBalanceBy => settingsStore.sortBalanceBy;

View file

@ -31,6 +31,7 @@ import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cake_wallet/wownero/wownero.dart' as wow;
import 'package:cryptography/cryptography.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/cake_hive.dart';
@ -163,6 +164,29 @@ abstract class DashboardViewModelBase with Store {
final sortedTransactions = [..._accountTransactions];
sortedTransactions.sort((a, b) => a.date.compareTo(b.date));
transactions = ObservableList.of(sortedTransactions.map((transaction) => TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
} else if (_wallet.type == WalletType.wownero) {
subname = wow.wownero!.getCurrentAccount(_wallet).label;
_onMoneroAccountChangeReaction = reaction(
(_) => wow.wownero!.getWowneroWalletDetails(wallet).account,
(wow.Account account) => _onMoneroAccountChange(_wallet));
_onMoneroBalanceChangeReaction = reaction(
(_) => wow.wownero!.getWowneroWalletDetails(wallet).balance,
(wow.WowneroBalance balance) => _onMoneroTransactionsUpdate(_wallet));
final _accountTransactions = _wallet.transactionHistory.transactions.values
.where((tx) =>
wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id)
.toList();
final sortedTransactions = [..._accountTransactions];
sortedTransactions.sort((a, b) => a.date.compareTo(b.date));
transactions = ObservableList.of(sortedTransactions.map((transaction) => TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
@ -200,6 +224,10 @@ abstract class DashboardViewModelBase with Store {
return monero!.getTransactionInfoAccountId(transaction) ==
monero!.getCurrentAccount(wallet).id;
}
if (wallet.type == WalletType.wownero) {
return wow.wownero!.getTransactionInfoAccountId(transaction) ==
wow.wownero!.getCurrentAccount(wallet).id;
}
return true;
});
@ -308,7 +336,7 @@ abstract class DashboardViewModelBase with Store {
wallet.type == WalletType.haven;
@computed
bool get hasSilentPayments => wallet.type == WalletType.bitcoin;
bool get hasSilentPayments => wallet.type == WalletType.bitcoin && !wallet.isHardwareWallet;
@computed
bool get showSilentPaymentsCard => hasSilentPayments && settingsStore.silentPaymentsCardDisplay;
@ -459,6 +487,21 @@ abstract class DashboardViewModelBase with Store {
(_) => monero!.getMoneroWalletDetails(wallet).balance,
(MoneroBalance balance) => _onMoneroTransactionsUpdate(wallet));
_onMoneroTransactionsUpdate(wallet);
} else if (wallet.type == WalletType.wownero) {
subname = wow.wownero!.getCurrentAccount(wallet).label;
_onMoneroAccountChangeReaction?.reaction.dispose();
_onMoneroBalanceChangeReaction?.reaction.dispose();
_onMoneroAccountChangeReaction = reaction(
(_) => wow.wownero!.getWowneroWalletDetails(wallet).account,
(wow.Account account) => _onMoneroAccountChange(wallet));
_onMoneroBalanceChangeReaction = reaction(
(_) => wow.wownero!.getWowneroWalletDetails(wallet).balance,
(wow.WowneroBalance balance) => _onMoneroTransactionsUpdate(wallet));
_onMoneroTransactionsUpdate(wallet);
} else {
// FIX-ME: Check for side effects
@ -489,32 +532,54 @@ abstract class DashboardViewModelBase with Store {
return monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id;
}
if (wallet.type == WalletType.wownero) {
return wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id;
}
return true;
});
}
@action
void _onMoneroAccountChange(WalletBase wallet) {
subname = monero!.getCurrentAccount(wallet).label;
if (wallet.type == WalletType.monero) {
subname = monero!.getCurrentAccount(wallet).label;
} else if (wallet.type == WalletType.wownero) {
subname = wow.wownero!.getCurrentAccount(wallet).label;
}
_onMoneroTransactionsUpdate(wallet);
}
@action
void _onMoneroTransactionsUpdate(WalletBase wallet) {
transactions.clear();
if (wallet.type == WalletType.monero) {
final _accountTransactions = monero!
.getTransactionHistory(wallet)
.transactions
.values
.where(
(tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
.toList();
final _accountTransactions = monero!
.getTransactionHistory(wallet)
.transactions
.values
.where(
(tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
.toList();
transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
} else if (wallet.type == WalletType.wownero) {
final _accountTransactions = wow.wownero!
.getTransactionHistory(wallet)
.transactions
.values
.where(
(tx) => wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id)
.toList();
transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
}
}
void updateActions() {

View file

@ -147,8 +147,13 @@ abstract class ExchangeTradeViewModelBase with Store {
items.clear();
if (trade.provider != ExchangeProviderDescription.thorChain)
items.add(ExchangeTradeItem(
title: "${trade.provider.title} ${S.current.id}", data: '${trade.id}', isCopied: true));
items.add(
ExchangeTradeItem(
title: "${trade.provider.title} ${S.current.id}",
data: '${trade.id}',
isCopied: true,
),
);
if (trade.extraId != null) {
final title = trade.from == CryptoCurrency.xrp
@ -161,15 +166,26 @@ abstract class ExchangeTradeViewModelBase with Store {
}
items.addAll([
ExchangeTradeItem(title: S.current.amount, data: '${trade.amount}', isCopied: true),
ExchangeTradeItem(
title: S.current.send_to_this_address('${tradesStore.trade!.from}', tagFrom) + ':',
data: trade.inputAddress ?? '',
isCopied: true),
title: S.current.amount,
data: '${trade.amount} ${trade.from}',
isCopied: true,
),
ExchangeTradeItem(
title: S.current.arrive_in_this_address('${tradesStore.trade!.to}', tagTo) + ':',
data: trade.payoutAddress ?? '',
isCopied: true),
title: S.current.estimated_receive_amount +':',
data: '${tradesStore.trade?.receiveAmount} ${trade.to}',
isCopied: true,
),
ExchangeTradeItem(
title: S.current.send_to_this_address('${tradesStore.trade!.from}', tagFrom) + ':',
data: trade.inputAddress ?? '',
isCopied: true,
),
ExchangeTradeItem(
title: S.current.arrive_in_this_address('${tradesStore.trade!.to}', tagTo) + ':',
data: trade.payoutAddress ?? '',
isCopied: true,
),
]);
}

View file

@ -405,13 +405,22 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
.where((element) => !isFixedRateMode || element.supportsFixedRate)
.toList();
final result = await Future.wait<double>(_providers.map((element) => element.fetchRate(
from: depositCurrency,
to: receiveCurrency,
amount: amount,
isFixedRateMode: isFixedRateMode,
isReceiveAmount: isFixedRateMode)));
final result = await Future.wait<double>(
_providers.map(
(element) => element
.fetchRate(
from: depositCurrency,
to: receiveCurrency,
amount: amount,
isFixedRateMode: isFixedRateMode,
isReceiveAmount: isFixedRateMode,
)
.timeout(
Duration(seconds: 7),
onTimeout: () => 0.0,
),
),
);
_sortedAvailableProviders.clear();
for (int i = 0; i < result.length; i++) {
@ -441,22 +450,31 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
double? highestMax = 0.0;
try {
for (var provider in selectedProviders) {
/// if this provider is not valid for the current pair, skip it
if (!providersForCurrentPair().contains(provider)) continue;
final result = await Future.wait(
selectedProviders.where((provider) => providersForCurrentPair().contains(provider)).map(
(provider) => provider
.fetchLimits(
from: from,
to: to,
isFixedRateMode: isFixedRateMode,
)
.onError((error, stackTrace) => Limits(max: 0.0, min: double.maxFinite))
.timeout(
Duration(seconds: 7),
onTimeout: () => Limits(max: 0.0, min: double.maxFinite),
),
),
);
try {
final tempLimits =
await provider.fetchLimits(from: from, to: to, isFixedRateMode: isFixedRateMode);
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) lowestMin = tempLimits.min;
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax)
highestMax = tempLimits.max;
} catch (e) {
continue;
result.forEach((tempLimits) {
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin!) {
lowestMin = tempLimits.min;
}
}
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax!) {
highestMax = tempLimits.max;
}
});
} on ConcurrentModificationError {
/// if user changed the selected providers while fetching limits
/// then delay the fetching limits a bit and try again
@ -490,17 +508,24 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
}
try {
for (var provider in _sortedAvailableProviders.values) {
for (var i = 0; i < _sortedAvailableProviders.values.length; i++) {
final provider = _sortedAvailableProviders.values.toList()[i];
final providerRate = _sortedAvailableProviders.keys.toList()[i];
if (!(await provider.checkIsAvailable())) continue;
_bestRate = providerRate;
await changeDepositAmount(amount: depositAmount);
final request = TradeRequest(
fromCurrency: depositCurrency,
toCurrency: receiveCurrency,
fromAmount: depositAmount.replaceAll(',', '.'),
toAmount: receiveAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
toAddress: receiveAddress,
isFixedRate: isFixedRateMode);
fromCurrency: depositCurrency,
toCurrency: receiveCurrency,
fromAmount: depositAmount.replaceAll(',', '.'),
toAmount: receiveAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
toAddress: receiveAddress,
isFixedRate: isFixedRateMode,
);
var amount = isFixedRateMode ? receiveAmount : depositAmount;
amount = amount.replaceAll(',', '.');

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
@ -13,7 +14,7 @@ class MoneroAccountEditOrCreateViewModel = MoneroAccountEditOrCreateViewModelBas
with _$MoneroAccountEditOrCreateViewModel;
abstract class MoneroAccountEditOrCreateViewModelBase with Store {
MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList, this._havenAccountList,
MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList, this._wowneroAccountList, this._havenAccountList,
{required WalletBase wallet, AccountListItem? accountListItem})
: state = InitialExecutionState(),
isEdit = accountListItem != null,
@ -30,6 +31,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
String label;
final MoneroAccountList _moneroAccountList;
final WowneroAccountList? _wowneroAccountList;
final HavenAccountList? _havenAccountList;
final AccountListItem? _accountListItem;
final WalletBase _wallet;
@ -42,6 +44,10 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
if (_wallet.type == WalletType.haven) {
await saveHaven();
}
if (_wallet.type == WalletType.wownero) {
await saveWownero();
}
}
Future<void> saveMonero() async {
@ -91,4 +97,27 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
state = FailureState(e.toString());
}
}
Future<void> saveWownero() async {
try {
state = IsExecutingState();
if (_accountListItem != null) {
await _wowneroAccountList?.setLabelAccount(
_wallet,
accountIndex: _accountListItem!.id,
label: label);
} else {
await _wowneroAccountList?.addAccount(
_wallet,
label: label);
}
await _wallet.save();
state = ExecutedSuccessfullyState();
} catch (e) {
state = FailureState(e.toString());
}
}
}

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
@ -47,6 +48,17 @@ abstract class MoneroAccountListViewModelBase with Store {
.toList();
}
if (_wallet.type == WalletType.wownero) {
return wownero
!.getAccountList(_wallet)
.accounts.map((acc) => AccountListItem(
label: acc.label,
id: acc.id,
balance: acc.balance,
isSelected: acc.id == wownero!.getCurrentAccount(_wallet).id))
.toList();
}
throw Exception('Unexpected wallet type: ${_wallet.type}');
}
@ -62,6 +74,15 @@ abstract class MoneroAccountListViewModelBase with Store {
);
}
if (_wallet.type == WalletType.wownero) {
wownero!.setCurrentAccount(
_wallet,
item.id,
item.label,
item.balance,
);
}
if (_wallet.type == WalletType.haven) {
haven!.setCurrentAccount(
_wallet,

View file

@ -65,7 +65,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
bool get isReady => address.isNotEmpty && port.isNotEmpty;
bool get hasAuthCredentials =>
_walletType == WalletType.monero || _walletType == WalletType.haven;
_walletType == WalletType.monero || _walletType == WalletType.wownero || _walletType == WalletType.haven;
bool get hasTestnetSupport => _walletType == WalletType.bitcoin;

View file

@ -86,7 +86,7 @@ abstract class TransactionDetailsViewModelBase with Store {
if (showRecipientAddress && !isRecipientAddressShown) {
try {
final recipientAddress = transactionDescriptionBox.values
.firstWhere((val) => val.id == transactionInfo.id)
.firstWhere((val) => val.id == transactionInfo.txHash)
.recipientAddress;
if (recipientAddress?.isNotEmpty ?? false) {
@ -105,14 +105,14 @@ abstract class TransactionDetailsViewModelBase with Store {
value: _explorerDescription(type),
onTap: () async {
try {
final uri = Uri.parse(_explorerUrl(type, tx.id));
final uri = Uri.parse(_explorerUrl(type, tx.txHash));
if (await canLaunchUrl(uri)) await launchUrl(uri, mode: LaunchMode.externalApplication);
} catch (e) {}
}));
final description = transactionDescriptionBox.values.firstWhere(
(val) => val.id == transactionInfo.id,
orElse: () => TransactionDescription(id: transactionInfo.id));
(val) => val.id == transactionInfo.txHash,
orElse: () => TransactionDescription(id: transactionInfo.txHash));
items.add(TextFieldListItem(
title: S.current.note_tap_to_change,
@ -214,7 +214,7 @@ abstract class TransactionDetailsViewModelBase with Store {
final addressIndex = tx.additionalInfo['addressIndex'] as int;
final feeFormatted = tx.feeFormatted();
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'),
@ -250,7 +250,7 @@ abstract class TransactionDetailsViewModelBase with Store {
void _addElectrumListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.confirmations, value: tx.confirmations.toString()),
@ -265,7 +265,7 @@ abstract class TransactionDetailsViewModelBase with Store {
void _addHavenListItems(TransactionInfo tx, DateFormat dateFormat) {
items.addAll([
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'),
@ -277,7 +277,7 @@ abstract class TransactionDetailsViewModelBase with Store {
void _addEthereumListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.confirmations, value: tx.confirmations.toString()),
@ -296,7 +296,7 @@ abstract class TransactionDetailsViewModelBase with Store {
void _addNanoListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash),
if (showRecipientAddress && tx.to != null)
StandartListItem(title: S.current.transaction_details_recipient_address, value: tx.to!),
if (showRecipientAddress && tx.from != null)
@ -313,7 +313,7 @@ abstract class TransactionDetailsViewModelBase with Store {
void _addPolygonListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.confirmations, value: tx.confirmations.toString()),
@ -332,7 +332,7 @@ abstract class TransactionDetailsViewModelBase with Store {
void _addSolanaListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()),
@ -396,7 +396,7 @@ abstract class TransactionDetailsViewModelBase with Store {
void _addTronListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()),
@ -455,7 +455,7 @@ abstract class TransactionDetailsViewModelBase with Store {
final addressIndex = tx.additionalInfo['addressIndex'] as int;
final feeFormatted = tx.feeFormatted();
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.txHash),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'),

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
@ -80,6 +81,16 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
await wallet.save();
}
if (wallet.type == WalletType.wownero) {
await wownero
!.getSubaddressList(wallet)
.addSubaddress(
wallet,
accountIndex: wownero!.getCurrentAccount(wallet).id,
label: label);
await wallet.save();
}
if (wallet.type == WalletType.haven) {
await haven
!.getSubaddressList(wallet)
@ -103,6 +114,11 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
accountIndex: monero!.getCurrentAccount(wallet).id, addressIndex: index, label: label);
await wallet.save();
}
if (wallet.type == WalletType.wownero) {
await wownero!.getSubaddressList(wallet).setLabelSubaddress(wallet,
accountIndex: wownero!.getCurrentAccount(wallet).id, addressIndex: index, label: label);
await wallet.save();
}
if (wallet.type == WalletType.haven) {
await haven!.getSubaddressList(wallet).setLabelSubaddress(wallet,
accountIndex: haven!.getCurrentAccount(wallet).id,

View file

@ -217,7 +217,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type),
_cryptoNumberFormat = NumberFormat(_cryptoNumberPattern),
hasAccounts =
appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven,
appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.wownero || appStore.wallet!.type == WalletType.haven,
amount = '',
_settingsStore = appStore.settingsStore,
super(appStore: appStore) {
@ -229,7 +229,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
_init();
selectedCurrency = walletTypeToCryptoCurrency(wallet.type);
hasAccounts = wallet.type == WalletType.monero || wallet.type == WalletType.haven;
hasAccounts = wallet.type == WalletType.monero || wallet.type == WalletType.wownero || wallet.type == WalletType.haven;
}
static const String _cryptoNumberPattern = '0.00000000';
@ -340,6 +340,20 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
addressList.addAll(addressItems);
}
if (wallet.type == WalletType.wownero) {
final primaryAddress = wownero!.getSubaddressList(wallet).subaddresses.first;
final addressItems = wownero!.getSubaddressList(wallet).subaddresses.map((subaddress) {
final isPrimary = subaddress == primaryAddress;
return WalletAddressListItem(
id: subaddress.id,
isPrimary: isPrimary,
name: subaddress.label,
address: subaddress.address);
});
addressList.addAll(addressItems);
}
if (wallet.type == WalletType.haven) {
final primaryAddress = haven!.getSubaddressList(wallet).subaddresses.first;
final addressItems = haven!.getSubaddressList(wallet).subaddresses.map((subaddress) {
@ -430,20 +444,6 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
}
if (wallet.type == WalletType.wownero) {
final primaryAddress = wownero!.getSubaddressList(wallet).subaddresses.first;
final addressItems = wownero!.getSubaddressList(wallet).subaddresses.map((subaddress) {
final isPrimary = subaddress == primaryAddress;
return WalletAddressListItem(
id: subaddress.id,
isPrimary: isPrimary,
name: subaddress.label,
address: subaddress.address);
});
addressList.addAll(addressItems);
}
if (searchText.isNotEmpty) {
return ObservableList.of(addressList.where((item) {
if (item is WalletAddressListItem) {
@ -465,6 +465,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
return monero!.getCurrentAccount(wallet).label;
}
if (wallet.type == WalletType.wownero) {
return wownero!.getCurrentAccount(wallet).label;
}
if (wallet.type == WalletType.haven) {
return haven!.getCurrentAccount(wallet).label;
}
@ -475,6 +479,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
@computed
bool get hasAddressList =>
wallet.type == WalletType.monero ||
wallet.type == WalletType.wownero ||
wallet.type == WalletType.haven ||
wallet.type == WalletType.bitcoinCash ||
wallet.type == WalletType.bitcoin ||
@ -513,7 +518,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
void _init() {
_baseItems = [];
if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) {
if (wallet.type == WalletType.monero || wallet.type == WalletType.wownero || wallet.type == WalletType.haven) {
_baseItems.add(WalletAccountListHeader());
}

View file

@ -83,8 +83,8 @@ abstract class WalletKeysViewModelBase with Store {
StandartListItem(
title: S.current.view_key_private,
value: keys['privateViewKey']!),
StandartListItem(
title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
if (_appStore.wallet!.seed!.isNotEmpty)
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
]);
if (_appStore.wallet?.seed != null &&
@ -123,8 +123,8 @@ abstract class WalletKeysViewModelBase with Store {
StandartListItem(
title: S.current.view_key_private,
value: keys['privateViewKey']!),
StandartListItem(
title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
if (_appStore.wallet!.seed!.isNotEmpty)
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
]);
}
@ -147,8 +147,8 @@ abstract class WalletKeysViewModelBase with Store {
StandartListItem(
title: S.current.view_key_private,
value: keys['privateViewKey']!),
StandartListItem(
title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
if (_appStore.wallet!.seed!.isNotEmpty)
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
]);
if (_appStore.wallet?.seed != null &&

View file

@ -66,7 +66,7 @@ dependencies:
url: https://github.com/cake-tech/device_display_brightness.git
ref: master
workmanager: ^0.5.1
wakelock_plus: ^1.1.3
wakelock_plus: ^1.2.5
flutter_mailer: ^2.0.2
device_info_plus: ^9.1.0
base32: 2.1.3
@ -127,6 +127,7 @@ dev_dependencies:
git:
url: https://github.com/cake-tech/google-translator.git
version: 1.0.0
archive: ^3.6.1
dependency_overrides:
bech32:

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "موضوع الكعكة الظلام",
"cake_pay_account_note": "قم بالتسجيل باستخدام عنوان بريد إلكتروني فقط لمشاهدة البطاقات وشرائها. حتى أن بعضها متوفر بسعر مخفض!",
"cake_pay_learn_more": "شراء واسترداد بطاقات الهدايا على الفور في التطبيق!\nاسحب من اليسار إلى اليمين لمعرفة المزيد.",
"cake_pay_save_order": "يجب إرسال البطاقة إلى بريدك الإلكتروني خلال يوم عمل واحد \n حفظ معرف الطلب الخاص بك:",
"cake_pay_subtitle": "شراء بطاقات مسبقة الدفع وبطاقات الهدايا في جميع أنحاء العالم",
"cake_pay_web_cards_subtitle": "اشتري بطاقات مدفوعة مسبقا وبطاقات هدايا في جميع أنحاء العالم",
"cake_pay_web_cards_title": "بطاقات Cake Pay Web",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "تأكيد خصم الرسوم",
"confirm_fee_deduction_content": "هل توافق على خصم الرسوم من الإخراج؟",
"confirm_sending": "تأكيد الإرسال",
"confirm_silent_payments_switch_node": "حاليا مطلوب لتبديل العقد لمسح المدفوعات الصامتة",
"confirm_silent_payments_switch_node": "العقدة الحالية لا تدعم المدفوعات الصامتة \\ ncake wallet سوف تتحول إلى عقدة متوافقة ، فقط للمسح الضوئي",
"confirmations": "التأكيدات",
"confirmed": "رصيد مؤكد",
"confirmed_tx": "مؤكد",
@ -266,6 +267,7 @@
"errorSigningTransaction": "ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ ءﺎﻨﺛﺃ ﺄﻄﺧ ﺙﺪﺣ",
"estimated": "مُقدَّر",
"estimated_new_fee": "رسوم جديدة مقدرة",
"estimated_receive_amount": "مقدرة المبلغ الاستقبال",
"etherscan_history": "Etherscan تاريخ",
"event": "ﺙﺪﺣ",
"events": "ﺙﺍﺪﺣﻷﺍ",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Торта тъмна тема",
"cake_pay_account_note": "Регистрайте се само с един имейл, за да виждате и купувате карти. За някои има дори и отстъпка!",
"cake_pay_learn_more": "Купете и използвайте гифткарти директно в приложението!\nПлъзнете отляво надясно, за да научите още.",
"cake_pay_save_order": "Картата трябва да бъде изпратена до вашия имейл в рамките на 1 работен ден \n Запазете вашия идентификационен номер на поръчката:",
"cake_pay_subtitle": "Купете предплатени карти и карти за подаръци в световен мащаб",
"cake_pay_web_cards_subtitle": "Купете световно признати предплатени и гифт карти",
"cake_pay_web_cards_title": "Cake Pay Онлайн Карти",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Потвърдете приспадането на таксите",
"confirm_fee_deduction_content": "Съгласни ли сте да приспадате таксата от продукцията?",
"confirm_sending": "Потвърждаване на изпращането",
"confirm_silent_payments_switch_node": "Понастоящем се изисква да превключвате възлите за сканиране на мълчаливи плащания",
"confirm_silent_payments_switch_node": "Текущият ви възел не поддържа Silent Payments \\ Ncake Wallet ще премине към съвместим възел, само за сканиране",
"confirmations": "потвърждения",
"confirmed": "Потвърден баланс",
"confirmed_tx": "Потвърдено",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Възникна грешка при подписване на транзакция",
"estimated": "Изчислено",
"estimated_new_fee": "Прогнозна нова такса",
"estimated_receive_amount": "Прогнозна сума за получаване",
"etherscan_history": "История на Etherscan",
"event": "Събитие",
"events": "събития",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Dort tmavé téma",
"cake_pay_account_note": "Přihlaste se svou e-mailovou adresou pro zobrazení a nákup karet. Některé jsou dostupné ve slevě!",
"cake_pay_learn_more": "Okamžitý nákup a uplatnění dárkových karet v aplikaci!\nPřejeďte prstem zleva doprava pro další informace.",
"cake_pay_save_order": "Karta by měla být odeslána do vašeho e-mailu do 1 pracovního dne \n Uložit ID objednávky:",
"cake_pay_subtitle": "Kupte si celosvětové předplacené karty a dárkové karty",
"cake_pay_web_cards_subtitle": "Kupte si celosvětové předplacené a dárkové karty",
"cake_pay_web_cards_title": "Cake Pay webové karty",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Potvrďte odpočet poplatků",
"confirm_fee_deduction_content": "Souhlasíte s odečtením poplatku z výstupu?",
"confirm_sending": "Potvrdit odeslání",
"confirm_silent_payments_switch_node": "V současné době je nutné přepínat uzly pro skenování tichých plateb",
"confirm_silent_payments_switch_node": "Váš aktuální uzel nepodporuje tiché platby \\ Ncake peněženka se přepne na kompatibilní uzel, pouze pro skenování",
"confirmations": "Potvrzení",
"confirmed": "Potvrzený zůstatek",
"confirmed_tx": "Potvrzeno",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Při podepisování transakce došlo k chybě",
"estimated": "Odhadováno",
"estimated_new_fee": "Odhadovaný nový poplatek",
"estimated_receive_amount": "Odhadovaná částka přijímání",
"etherscan_history": "Historie Etherscanu",
"event": "událost",
"events": "Události",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Cake Dark Thema",
"cake_pay_account_note": "Melden Sie sich nur mit einer E-Mail-Adresse an, um Karten anzuzeigen und zu kaufen. Einige sind sogar mit Rabatt erhältlich!",
"cake_pay_learn_more": "Kaufen und lösen Sie Geschenkkarten sofort in der App ein!\nWischen Sie von links nach rechts, um mehr zu erfahren.",
"cake_pay_save_order": "Die Karte sollte innerhalb von 1 Werktag an Ihre E-Mail gesendet werden, \n Ihre Bestell-ID zu speichern:",
"cake_pay_subtitle": "Kaufen Sie weltweite Prepaid -Karten und Geschenkkarten",
"cake_pay_web_cards_subtitle": "Kaufen Sie weltweit Prepaid-Karten und Geschenkkarten",
"cake_pay_web_cards_title": "Cake Pay-Webkarten",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Gebührenabzug bestätigen",
"confirm_fee_deduction_content": "Stimmen Sie zu, die Gebühr von der Ausgabe abzuziehen?",
"confirm_sending": "Senden bestätigen",
"confirm_silent_payments_switch_node": "Derzeit ist es erforderlich, Knoten zu wechseln, um stille Zahlungen zu scannen",
"confirm_silent_payments_switch_node": "Ihr aktueller Knoten unterstützt keine stillen Zahlungen \\ NCAKE Wallet wechselt zu einem kompatiblen Knoten, nur zum Scannen",
"confirmations": "Bestätigungen",
"confirmed": "Bestätigter Saldo",
"confirmed_tx": "Bestätigt",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Beim Signieren der Transaktion ist ein Fehler aufgetreten",
"estimated": "Geschätzt",
"estimated_new_fee": "Geschätzte neue Gebühr",
"estimated_receive_amount": "Geschätzter Empfangsbetrag",
"etherscan_history": "Etherscan-Geschichte",
"event": "Ereignis",
"events": "Veranstaltungen",
@ -876,4 +878,4 @@
"you_will_get": "Konvertieren zu",
"you_will_send": "Konvertieren von",
"yy": "YY"
}
}

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Cake Dark Theme",
"cake_pay_account_note": "Sign up with just an email address to see and purchase cards. Some are even available at a discount!",
"cake_pay_learn_more": "Instantly purchase and redeem gift cards in the app!\nSwipe left to right to learn more.",
"cake_pay_save_order": "The card should be sent to your e-mail within 1 business day \n Save your Order ID:",
"cake_pay_subtitle": "Buy worldwide prepaid cards and gift cards",
"cake_pay_web_cards_subtitle": "Buy worldwide prepaid cards and gift cards",
"cake_pay_web_cards_title": "Cake Pay Web Cards",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Confirm Fee Deduction",
"confirm_fee_deduction_content": "Do you agree to deduct the fee from the output?",
"confirm_sending": "Confirm sending",
"confirm_silent_payments_switch_node": "Currently it is required to switch nodes to scan silent payments",
"confirm_silent_payments_switch_node": "Your current node does not support silent payments\\nCake Wallet will switch to a compatible node, just for scanning",
"confirmations": "Confirmations",
"confirmed": "Confirmed Balance",
"confirmed_tx": "Confirmed",
@ -266,6 +267,7 @@
"errorSigningTransaction": "An error has occured while signing transaction",
"estimated": "Estimated",
"estimated_new_fee": "Estimated new fee",
"estimated_receive_amount": "Estimated receive amount",
"etherscan_history": "Etherscan history",
"event": "Event",
"events": "Events",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Tema oscuro del pastel",
"cake_pay_account_note": "Regístrese con solo una dirección de correo electrónico para ver y comprar tarjetas. ¡Algunas incluso están disponibles con descuento!",
"cake_pay_learn_more": "¡Compre y canjee tarjetas de regalo al instante en la aplicación!\nDeslice el dedo de izquierda a derecha para obtener más información.",
"cake_pay_save_order": "La tarjeta debe enviarse a su correo electrónico dentro de 1 día hábil \n Guardar su ID de pedido:",
"cake_pay_subtitle": "Compre tarjetas prepagas y tarjetas de regalo en todo el mundo",
"cake_pay_web_cards_subtitle": "Compre tarjetas de prepago y tarjetas de regalo en todo el mundo",
"cake_pay_web_cards_title": "Tarjetas Web Cake Pay",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Confirmar la deducción de la tarifa",
"confirm_fee_deduction_content": "¿Acepta deducir la tarifa de la producción?",
"confirm_sending": "Confirmar envío",
"confirm_silent_payments_switch_node": "Actualmente se requiere cambiar los nodos para escanear pagos silenciosos",
"confirm_silent_payments_switch_node": "Su nodo actual no admite pagos silenciosos \\ ncake billet cambiará a un nodo compatible, solo para escanear",
"confirmations": "Confirmaciones",
"confirmed": "Saldo confirmado",
"confirmed_tx": "Confirmado",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Se ha producido un error al firmar la transacción.",
"estimated": "Estimado",
"estimated_new_fee": "Nueva tarifa estimada",
"estimated_receive_amount": "Cantidad de recepción estimada",
"etherscan_history": "historia de etherscan",
"event": "Evento",
"events": "Eventos",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Thème sombre du gâteau",
"cake_pay_account_note": "Inscrivez-vous avec juste une adresse e-mail pour voir et acheter des cartes. Certaines sont même disponibles à prix réduit !",
"cake_pay_learn_more": "Achetez et utilisez instantanément des cartes-cadeaux dans l'application !\nBalayer de gauche à droite pour en savoir plus.",
"cake_pay_save_order": "La carte doit être envoyée à votre e-mail dans un jour ouvrable \n Enregistrez votre identifiant de commande:",
"cake_pay_subtitle": "Achetez des cartes et des cartes-cadeaux prépayées mondiales",
"cake_pay_web_cards_subtitle": "Achetez des cartes prépayées et des cartes-cadeaux dans le monde entier",
"cake_pay_web_cards_title": "Cartes Web Cake Pay",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Confirmer la déduction des frais",
"confirm_fee_deduction_content": "Acceptez-vous de déduire les frais de la production?",
"confirm_sending": "Confirmer l'envoi",
"confirm_silent_payments_switch_node": "Actuellement, il est nécessaire de changer de nœuds pour scanner les paiements silencieux",
"confirm_silent_payments_switch_node": "Votre nœud actuel ne prend pas en charge les paiements silencieux \\ ncake qui passera à un nœud compatible, juste pour la numérisation",
"confirmations": "Confirmations",
"confirmed": "Solde confirmé",
"confirmed_tx": "Confirmé",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Une erreur s'est produite lors de la signature de la transaction",
"estimated": "Estimé",
"estimated_new_fee": "De nouveaux frais estimés",
"estimated_receive_amount": "Recevoir estimé le montant",
"etherscan_history": "Historique Etherscan",
"event": "Événement",
"events": "Événements",
@ -873,4 +875,4 @@
"you_will_get": "Convertir vers",
"you_will_send": "Convertir depuis",
"yy": "AA"
}
}

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Cake Dark Jigo",
"cake_pay_account_note": "Yi rajista tare da adireshin imel kawai don gani da siyan katunan. Wasu ma suna samuwa a rangwame!",
"cake_pay_learn_more": "Nan take siya ku kwaso katunan kyaututtuka a cikin app!\nTake hagu zuwa dama don ƙarin koyo.",
"cake_pay_save_order": "Ya kamata a aika katin zuwa e-mail ɗinku a cikin rana 1 na kasuwanci \n Ajiye id ku:",
"cake_pay_subtitle": "Sayi katunan shirye-shiryen duniya da katunan kyauta",
"cake_pay_web_cards_subtitle": "Sayi katunan da aka riga aka biya na duniya da katunan kyauta",
"cake_pay_web_cards_title": "Cake Pay Web Cards",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Tabbatar da cire kudade",
"confirm_fee_deduction_content": "Shin kun yarda ku cire kuɗin daga fitarwa?",
"confirm_sending": "Tabbatar da aikawa",
"confirm_silent_payments_switch_node": "A halin yanzu ana buƙatar sauya nodes don bincika biyan siliki",
"confirm_silent_payments_switch_node": "Kumburinku na yanzu ba ya goyan bayan biyan shiru da shiru \\ NCADA Wallet zai canza zuwa kumburi mai dacewa, don bincika",
"confirmations": "Tabbatar",
"confirmed": "An tabbatar",
"confirmed_tx": "Tabbatar",
@ -266,6 +267,7 @@
"errorSigningTransaction": "An sami kuskure yayin sanya hannu kan ciniki",
"estimated": "Kiyasta",
"estimated_new_fee": "An kiyasta sabon kudin",
"estimated_receive_amount": "Kiyasta samun adadin",
"etherscan_history": "Etherscan tarihin kowane zamani",
"event": "Lamarin",
"events": "Abubuwan da suka faru",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "केक डार्क थीम",
"cake_pay_account_note": "कार्ड देखने और खरीदने के लिए केवल एक ईमेल पते के साथ साइन अप करें। कुछ छूट पर भी उपलब्ध हैं!",
"cake_pay_learn_more": "ऐप में उपहार कार्ड तुरंत खरीदें और रिडीम करें!\nअधिक जानने के लिए बाएं से दाएं स्वाइप करें।",
"cake_pay_save_order": "कार्ड को आपके ई-मेल को 1 व्यावसायिक दिन के भीतर भेजा जाना चाहिए \n आपकी ऑर्डर आईडी सहेजें:",
"cake_pay_subtitle": "दुनिया भर में प्रीपेड कार्ड और उपहार कार्ड खरीदें",
"cake_pay_web_cards_subtitle": "दुनिया भर में प्रीपेड कार्ड और गिफ्ट कार्ड खरीदें",
"cake_pay_web_cards_title": "केक भुगतान वेब कार्ड",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "शुल्क कटौती की पुष्टि करें",
"confirm_fee_deduction_content": "क्या आप आउटपुट से शुल्क में कटौती करने के लिए सहमत हैं?",
"confirm_sending": "भेजने की पुष्टि करें",
"confirm_silent_payments_switch_node": "वर्तमान में मूक भुगतान को स्कैन करने के लिए नोड्स को स्विच करना आवश्यक है",
"confirm_silent_payments_switch_node": "आपका वर्तमान नोड मूक भुगतान का समर्थन नहीं करता है \\ ncake वॉलेट एक संगत नोड पर स्विच करेगा, बस स्कैनिंग के लिए",
"confirmations": "पुष्टिकरण",
"confirmed": "पुष्टि की गई शेष राशिी",
"confirmed_tx": "की पुष्टि",
@ -266,6 +267,7 @@
"errorSigningTransaction": "लेन-देन पर हस्ताक्षर करते समय एक त्रुटि उत्पन्न हुई है",
"estimated": "अनुमानित",
"estimated_new_fee": "अनुमानित नया शुल्क",
"estimated_receive_amount": "अनुमानित राशि",
"etherscan_history": "इथरस्कैन इतिहास",
"event": "आयोजन",
"events": "आयोजन",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "TOKA DARKA TEMA",
"cake_pay_account_note": "Prijavite se samo s adresom e-pošte da biste vidjeli i kupili kartice. Neke su čak dostupne uz popust!",
"cake_pay_learn_more": "Azonnal vásárolhat és válthat be ajándékutalványokat az alkalmazásban!\nTovábbi információért csúsztassa balról jobbra az ujját.",
"cake_pay_save_order": "Karticu treba poslati na vašu e-poštu u roku od 1 radnog dana \n Spremi ID narudžbe:",
"cake_pay_subtitle": "Kupite svjetske unaprijed plaćene kartice i poklon kartice",
"cake_pay_web_cards_subtitle": "Kupujte prepaid kartice i poklon kartice diljem svijeta",
"cake_pay_web_cards_title": "Cake Pay Web kartice",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Potvrdite odbitak naknade",
"confirm_fee_deduction_content": "Slažete li se da ćete odbiti naknadu od izlaza?",
"confirm_sending": "Potvrdi slanje",
"confirm_silent_payments_switch_node": "Trenutno je potrebno prebaciti čvorove na skeniranje tihih plaćanja",
"confirm_silent_payments_switch_node": "Vaš trenutni čvor ne podržava tiha plaćanja \\ ncake novčanik prebacit će se na kompatibilni čvor, samo za skeniranje",
"confirmations": "Potvrde",
"confirmed": "Potvrđeno stanje",
"confirmed_tx": "Potvrđen",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Došlo je do pogreške prilikom potpisivanja transakcije",
"estimated": "procijenjen",
"estimated_new_fee": "Procijenjena nova naknada",
"estimated_receive_amount": "Procijenjeni iznos primanja",
"etherscan_history": "Etherscan povijest",
"event": "Događaj",
"events": "Događaji",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Tema Kue Gelap",
"cake_pay_account_note": "Daftar hanya dengan alamat email untuk melihat dan membeli kartu. Beberapa di antaranya bahkan tersedia dengan diskon!",
"cake_pay_learn_more": "Beli dan tukar kartu hadiah secara instan di aplikasi!\nGeser ke kanan untuk informasi lebih lanjut.",
"cake_pay_save_order": "Kartu harus dikirim ke email Anda dalam 1 hari kerja \n Simpan ID pesanan Anda:",
"cake_pay_subtitle": "Beli kartu prabayar di seluruh dunia dan kartu hadiah",
"cake_pay_web_cards_subtitle": "Beli kartu prabayar dan kartu hadiah secara global",
"cake_pay_web_cards_title": "Kartu Web Cake Pay",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Konfirmasi pengurangan biaya",
"confirm_fee_deduction_content": "Apakah Anda setuju untuk mengurangi biaya dari output?",
"confirm_sending": "Konfirmasi pengiriman",
"confirm_silent_payments_switch_node": "Saat ini diminta untuk mengganti node untuk memindai pembayaran diam",
"confirm_silent_payments_switch_node": "Node Anda saat ini tidak mendukung pembayaran diam \\ ncake Wallet akan beralih ke simpul yang kompatibel, hanya untuk pemindaian",
"confirmations": "Konfirmasi",
"confirmed": "Saldo Terkonfirmasi",
"confirmed_tx": "Dikonfirmasi",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Terjadi kesalahan saat menandatangani transaksi",
"estimated": "Diperkirakan",
"estimated_new_fee": "Perkiraan biaya baru",
"estimated_receive_amount": "Diperkirakan jumlah menerima",
"etherscan_history": "Sejarah Etherscan",
"event": "Peristiwa",
"events": "Acara",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Tema oscuro della torta",
"cake_pay_account_note": "Iscriviti con solo un indirizzo email per vedere e acquistare le carte. Alcune sono anche disponibili con uno sconto!",
"cake_pay_learn_more": "Acquista e riscatta istantaneamente carte regalo nell'app!\nScorri da sinistra a destra per saperne di più.",
"cake_pay_save_order": "La carta deve essere inviata alla tua e-mail entro 1 giorno lavorativo \n Salva il tuo ID ordine:",
"cake_pay_subtitle": "Acquista carte prepagate in tutto il mondo e carte regalo",
"cake_pay_web_cards_subtitle": "Acquista carte prepagate e carte regalo in tutto il mondo",
"cake_pay_web_cards_title": "Carte Web Cake Pay",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Conferma la detrazione delle commissioni",
"confirm_fee_deduction_content": "Accetti di detrarre la commissione dall'output?",
"confirm_sending": "Conferma l'invio",
"confirm_silent_payments_switch_node": "Attualmente è necessario cambiare nodi per scansionare i pagamenti silenziosi",
"confirm_silent_payments_switch_node": "Il tuo nodo corrente non supporta i pagamenti silenziosi \\ ncake Wallet passerà a un nodo compatibile, solo per la scansione",
"confirmations": "Conferme",
"confirmed": "Saldo confermato",
"confirmed_tx": "Confermato",
@ -267,6 +268,7 @@
"errorSigningTransaction": "Si è verificato un errore durante la firma della transazione",
"estimated": "Stimato",
"estimated_new_fee": "Nuova commissione stimata",
"estimated_receive_amount": "Importo di ricezione stimato",
"etherscan_history": "Storia Etherscan",
"event": "Evento",
"events": "Eventi",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "ケーキ暗いテーマ",
"cake_pay_account_note": "メールアドレスだけでサインアップして、カードを表示して購入できます。割引価格で利用できるカードもあります!",
"cake_pay_learn_more": "アプリですぐにギフトカードを購入して引き換えましょう!\n左から右にスワイプして詳細をご覧ください。",
"cake_pay_save_order": "カードは1営業日以内に電子メールに送信する必要があります\n注文IDを保存します。",
"cake_pay_subtitle": "世界中のプリペイドカードとギフトカードを購入します",
"cake_pay_web_cards_subtitle": "世界中のプリペイド カードとギフト カードを購入する",
"cake_pay_web_cards_title": "Cake Pay ウェブカード",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "料金控除を確認します",
"confirm_fee_deduction_content": "出力から料金を差し引くことに同意しますか?",
"confirm_sending": "送信を確認",
"confirm_silent_payments_switch_node": "現在、ノードを切り替えてサイレント決済をスキャンする必要があります",
"confirm_silent_payments_switch_node": "現在のノードはサイレントペイメントをサポートしていません\\ ncakeウォレットは、スキャン用に互換性のあるードに切り替えます",
"confirmations": "確認",
"confirmed": "確認済み残高",
"confirmed_tx": "確認済み",
@ -266,6 +267,7 @@
"errorSigningTransaction": "トランザクションの署名中にエラーが発生しました",
"estimated": "推定",
"estimated_new_fee": "推定新しい料金",
"estimated_receive_amount": "推定受信金額",
"etherscan_history": "イーサスキャンの歴史",
"event": "イベント",
"events": "イベント",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "케이크 다크 테마",
"cake_pay_account_note": "이메일 주소로 가입하면 카드를 보고 구매할 수 있습니다. 일부는 할인된 가격으로 사용 가능합니다!",
"cake_pay_learn_more": "앱에서 즉시 기프트 카드를 구매하고 사용하세요!\n자세히 알아보려면 왼쪽에서 오른쪽으로 스와이프하세요.",
"cake_pay_save_order": "카드는 영업일 1 일 이내에 이메일로 전자 메일로 보내야합니다. \n 주문 ID 저장 :",
"cake_pay_subtitle": "전세계 선불 카드와 기프트 카드를 구입하십시오",
"cake_pay_web_cards_subtitle": "전 세계 선불 카드 및 기프트 카드 구매",
"cake_pay_web_cards_title": "케이크페이 웹카드",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "수수료 공제를 확인하십시오",
"confirm_fee_deduction_content": "출력에서 수수료를 공제하는 데 동의하십니까?",
"confirm_sending": "전송 확인",
"confirm_silent_payments_switch_node": "현재 사일런트 결제를 스캔하려면 노드를 전환해야합니다.",
"confirm_silent_payments_switch_node": "현재 노드는 무음 지불을 지원하지 않습니다 \\ ncake 지갑은 스캔을 위해 호환 가능한 노드로 전환됩니다.",
"confirmations": "확인",
"confirmed": "확인된 잔액",
"confirmed_tx": "확인",
@ -266,6 +267,7 @@
"errorSigningTransaction": "거래에 서명하는 동안 오류가 발생했습니다.",
"estimated": "예상",
"estimated_new_fee": "예상 새로운 수수료",
"estimated_receive_amount": "예상 수신 금액",
"etherscan_history": "이더스캔 역사",
"event": "이벤트",
"events": "이벤트",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "ကိတ်မုန့် Dark Theme",
"cake_pay_account_note": "ကတ်များကြည့်ရှုဝယ်ယူရန် အီးမေးလ်လိပ်စာတစ်ခုဖြင့် စာရင်းသွင်းပါ။ အချို့ကို လျှော့ဈေးဖြင့်ပင် ရနိုင်သည်။",
"cake_pay_learn_more": "အက်ပ်ရှိ လက်ဆောင်ကတ်များကို ချက်ချင်းဝယ်ယူပြီး ကူပွန်ဖြင့် လဲလှယ်ပါ။\nပိုမိုလေ့လာရန် ဘယ်မှညာသို့ ပွတ်ဆွဲပါ။",
"cake_pay_save_order": "ကဒ်ကိုသင်၏အီးမေးလ်သို့ပေးပို့သင့်သည်။ သင်၏အမှာစာ ID ကိုသိမ်းပါ။",
"cake_pay_subtitle": "Worldwide ကြိုတင်ငွေဖြည့်ကဒ်များနှင့်လက်ဆောင်ကဒ်များကို 0 ယ်ပါ",
"cake_pay_web_cards_subtitle": "ကမ္ဘာတစ်ဝှမ်း ကြိုတင်ငွေပေးကတ်များနှင့် လက်ဆောင်ကတ်များကို ဝယ်ယူပါ။",
"cake_pay_web_cards_title": "Cake Pay ဝဘ်ကတ်များ",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "အခကြေးငွေကိုနှုတ်ယူခြင်း",
"confirm_fee_deduction_content": "output မှအခကြေးငွေကိုယူရန်သဘောတူပါသလား။",
"confirm_sending": "ပေးပို့အတည်ပြုပါ။",
"confirm_silent_payments_switch_node": "လောလောဆယ်အသံတိတ်ငွေပေးချေမှုကိုစကင်ဖတ်စစ်ဆေးရန် node များကိုပြောင်းရန်လိုအပ်သည်",
"confirm_silent_payments_switch_node": "သင်၏လက်ရှိ node သည်အသံတိတ်ငွေပေးချေမှုကိုမပံ့ပိုးပါဟု \\ t",
"confirmations": "အတည်ပြုချက်များ",
"confirmed": "အတည်ပြုထားသော လက်ကျန်ငွေ",
"confirmed_tx": "အတည်ပြုသည်",
@ -266,6 +267,7 @@
"errorSigningTransaction": "ငွေပေးငွေယူ လက်မှတ်ထိုးစဉ် အမှားအယွင်းတစ်ခု ဖြစ်ပေါ်ခဲ့သည်။",
"estimated": "ခန့်မှန်း",
"estimated_new_fee": "ခန့်မှန်းသစ်ခန့်မှန်း",
"estimated_receive_amount": "ခန့်မှန်းရရှိသောပမာဏ",
"etherscan_history": "Etherscan သမိုင်း",
"event": "ပွဲ",
"events": "အဲ့ဒါနဲ့",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Cake Dark Theme",
"cake_pay_account_note": "Meld u aan met alleen een e-mailadres om kaarten te bekijken en te kopen. Sommige zijn zelfs met korting verkrijgbaar!",
"cake_pay_learn_more": "Koop en wissel cadeaubonnen direct in de app in!\nSwipe van links naar rechts voor meer informatie.",
"cake_pay_save_order": "De kaart moet binnen 1 werkdag naar uw e-mail worden verzonden.",
"cake_pay_subtitle": "Koop wereldwijde prepaid -kaarten en cadeaubonnen",
"cake_pay_web_cards_subtitle": "Koop wereldwijd prepaidkaarten en cadeaubonnen",
"cake_pay_web_cards_title": "Cake Pay-webkaarten",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Bevestig de aftrek van de kosten",
"confirm_fee_deduction_content": "Stemt u ermee in om de vergoeding af te trekken van de output?",
"confirm_sending": "Bevestig verzending",
"confirm_silent_payments_switch_node": "Momenteel is het vereist om knooppunten te schakelen om stille betalingen te scannen",
"confirm_silent_payments_switch_node": "Uw huidige knooppunt ondersteunt geen stille betalingen \\ ncake -portemonnee schakelt over naar een compatibele knoop",
"confirmations": "Bevestigingen",
"confirmed": "Bevestigd saldo",
"confirmed_tx": "Bevestigd",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Er is een fout opgetreden tijdens het ondertekenen van de transactie",
"estimated": "Geschatte",
"estimated_new_fee": "Geschatte nieuwe vergoeding",
"estimated_receive_amount": "Geschat ontvangen bedrag",
"etherscan_history": "Etherscan-geschiedenis",
"event": "Evenement",
"events": "Evenementen",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Cake Dark Temat",
"cake_pay_account_note": "Zarejestruj się, używając tylko adresu e-mail, aby przeglądać i kupować karty. Niektóre są nawet dostępne ze zniżką!",
"cake_pay_learn_more": "Kupuj i wykorzystuj karty podarunkowe od razu w aplikacji!\nPrzesuń od lewej do prawej, aby dowiedzieć się więcej.",
"cake_pay_save_order": "Karta powinna zostać wysłana na adres e-mail w ciągu 1 dnia roboczego \n Zapisz identyfikator zamówienia:",
"cake_pay_subtitle": "Kup na całym świecie karty przedpłacone i karty podarunkowe",
"cake_pay_web_cards_subtitle": "Kupuj na całym świecie karty przedpłacone i karty podarunkowe",
"cake_pay_web_cards_title": "Cake Pay Web Cards",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Potwierdź odliczenie opłaty",
"confirm_fee_deduction_content": "Czy zgadzasz się odliczyć opłatę od wyników?",
"confirm_sending": "Potwierdź wysłanie",
"confirm_silent_payments_switch_node": "Obecnie wymagane jest zmiana węzłów w celu skanowania cichych płatności",
"confirm_silent_payments_switch_node": "Twój obecny węzeł nie obsługuje cichych płatności \\ NCAKE Portfel przełączy się na kompatybilny węzeł, tylko do skanowania",
"confirmations": "Potwierdzenia",
"confirmed": "Potwierdzone saldo",
"confirmed_tx": "Potwierdzony",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Wystąpił błąd podczas podpisywania transakcji",
"estimated": "Oszacowano",
"estimated_new_fee": "Szacowana nowa opłata",
"estimated_receive_amount": "Szacowana kwota otrzymania",
"etherscan_history": "Historia Etherscanu",
"event": "Wydarzenie",
"events": "Wydarzenia",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Bolo tema escuro",
"cake_pay_account_note": "Inscreva-se com apenas um endereço de e-mail para ver e comprar cartões. Alguns estão até com desconto!",
"cake_pay_learn_more": "Compre e resgate vales-presente instantaneamente no app!\nDeslize da esquerda para a direita para saber mais.",
"cake_pay_save_order": "O cartão deve ser enviado ao seu e-mail dentro de 1 dia útil \n Salvar seu ID do pedido:",
"cake_pay_subtitle": "Compre cartões pré -pagos em todo o mundo e cartões -presente",
"cake_pay_web_cards_subtitle": "Compre cartões pré-pagos e cartões-presente em todo o mundo",
"cake_pay_web_cards_title": "Cartões Cake Pay Web",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Confirme dedução da taxa",
"confirm_fee_deduction_content": "Você concorda em deduzir a taxa da saída?",
"confirm_sending": "Confirmar o envio",
"confirm_silent_payments_switch_node": "Atualmente, é necessário trocar de nós para digitalizar pagamentos silenciosos",
"confirm_silent_payments_switch_node": "Seu nó atual não suporta pagamentos silenciosos \\ Ncake Wallet mudará para um nó compatível, apenas para digitalização",
"confirmations": "Confirmações",
"confirmed": "Saldo Confirmado",
"confirmed_tx": "Confirmado",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Ocorreu um erro ao assinar a transação",
"estimated": "Estimado",
"estimated_new_fee": "Nova taxa estimada",
"estimated_receive_amount": "Valor estimado de recebimento",
"etherscan_history": "história Etherscan",
"event": "Evento",
"events": "Eventos",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Тейт темная тема",
"cake_pay_account_note": "Зарегистрируйтесь, указав только адрес электронной почты, чтобы просматривать и покупать карты. Некоторые даже доступны со скидкой!",
"cake_pay_learn_more": "Мгновенно покупайте и используйте подарочные карты в приложении!\nПроведите по экрану слева направо, чтобы узнать больше.",
"cake_pay_save_order": "Карта должна быть отправлена ​​на ваше электронное письмо в течение 1 рабочего дня \n Сохраните свой идентификатор заказа:",
"cake_pay_subtitle": "Купить карты с предоплатой и подарочными картами по всему миру",
"cake_pay_web_cards_subtitle": "Покупайте карты предоплаты и подарочные карты по всему миру",
"cake_pay_web_cards_title": "Веб-карты Cake Pay",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Подтвердите вычет платы",
"confirm_fee_deduction_content": "Согласны ли вы вычесть плату из вывода?",
"confirm_sending": "Подтвердить отправку",
"confirm_silent_payments_switch_node": "В настоящее время требуется переключение узлов для сканирования молчаливых платежей",
"confirm_silent_payments_switch_node": "Ваш текущий узел не поддерживает Silent Payments \\ ncake Wallet переключится на совместимый узел, только для сканирования",
"confirmations": "Подтверждения",
"confirmed": "Подтвержденный баланс",
"confirmed_tx": "Подтвержденный",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Произошла ошибка при подписании транзакции",
"estimated": "Примерно",
"estimated_new_fee": "Расчетная новая плата",
"estimated_receive_amount": "Расчетная сумма получения",
"etherscan_history": "История Эфириума",
"event": "Событие",
"events": "События",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "ธีมเค้กมืด",
"cake_pay_account_note": "ลงทะเบียนด้วยอีเมลเพียงอย่างเดียวเพื่อดูและซื้อบัตร บางบัตรอาจมีส่วนลด!",
"cake_pay_learn_more": "ซื้อและเบิกบัตรของขวัญในแอพพลิเคชันทันที!\nกระแทกขวาไปซ้ายเพื่อเรียนรู้เพิ่มเติม",
"cake_pay_save_order": "บัตรควรส่งไปยังอีเมลของคุณภายใน 1 วันทำการ \n บันทึกรหัสคำสั่งซื้อของคุณ:",
"cake_pay_subtitle": "ซื้อบัตรเติมเงินและบัตรของขวัญทั่วโลก",
"cake_pay_web_cards_subtitle": "ซื้อบัตรพร้อมเงินระดับโลกและบัตรของขวัญ",
"cake_pay_web_cards_title": "Cake Pay Web Cards",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "ยืนยันการหักค่าธรรมเนียม",
"confirm_fee_deduction_content": "คุณตกลงที่จะหักค่าธรรมเนียมจากผลลัพธ์หรือไม่?",
"confirm_sending": "ยืนยันการส่ง",
"confirm_silent_payments_switch_node": "ขณะนี้จำเป็นต้องเปลี่ยนโหนดเพื่อสแกนการชำระเงินแบบเงียบ",
"confirm_silent_payments_switch_node": "โหนดปัจจุบันของคุณไม่รองรับการชำระเงินแบบเงียบ \\ ncake กระเป๋าเงินจะเปลี่ยนเป็นโหนดที่เข้ากันได้เพียงเพื่อการสแกน",
"confirmations": "การยืนยัน",
"confirmed": "ยอดคงเหลือที่ยืนยันแล้ว",
"confirmed_tx": "ซึ่งยืนยันแล้ว",
@ -266,6 +267,7 @@
"errorSigningTransaction": "เกิดข้อผิดพลาดขณะลงนามธุรกรรม",
"estimated": "ประมาณการ",
"estimated_new_fee": "ค่าธรรมเนียมใหม่โดยประมาณ",
"estimated_receive_amount": "โดยประมาณว่าจำนวนเงินที่ได้รับ",
"etherscan_history": "ประวัติอีเธอร์สแกน",
"event": "เหตุการณ์",
"events": "กิจกรรม",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Cake madilim na tema",
"cake_pay_account_note": "Mag -sign up na may isang email address lamang upang makita at bumili ng mga kard. Ang ilan ay magagamit kahit sa isang diskwento!",
"cake_pay_learn_more": "Agad na bumili at tubusin ang mga kard ng regalo sa app!\nMag -swipe pakaliwa sa kanan upang matuto nang higit pa.",
"cake_pay_save_order": "Ang card ay dapat ipadala sa iyong e-mail sa loob ng 1 araw ng negosyo \n i-save ang iyong order ID:",
"cake_pay_subtitle": "Bumili ng mga pandaigdigang prepaid card at gift card",
"cake_pay_web_cards_subtitle": "Bumili ng mga pandaigdigang prepaid card at gift card",
"cake_pay_web_cards_title": "Cake pay web card",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Kumpirmahin ang pagbabawas ng bayad",
"confirm_fee_deduction_content": "Sumasang -ayon ka bang bawasan ang bayad mula sa output?",
"confirm_sending": "Kumpirmahin ang pagpapadala",
"confirm_silent_payments_switch_node": "Sa kasalukuyan kinakailangan itong lumipat ng mga node upang i -scan ang mga tahimik na pagbabayad",
"confirm_silent_payments_switch_node": "Ang iyong kasalukuyang node ay hindi sumusuporta sa tahimik na pagbabayad \\ ncake wallet ay lilipat sa isang katugmang node, para lamang sa pag -scan",
"confirmations": "Mga kumpirmasyon",
"confirmed": "Nakumpirma na balanse",
"confirmed_tx": "Nakumpirma",
@ -266,6 +267,7 @@
"errorSigningTransaction": "May naganap na error habang pinipirmahan ang transaksyon",
"estimated": "Tinatayang",
"estimated_new_fee": "Tinatayang bagong bayad",
"estimated_receive_amount": "Tinatayang natanggap na halaga",
"etherscan_history": "Kasaysayan ng Etherscan",
"event": "Kaganapan",
"events": "Mga kaganapan",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Kek Koyu Tema",
"cake_pay_account_note": "Kartları görmek ve satın almak için sadece bir e-posta adresiyle kaydolun. Hatta bazıları indirimli olarak bile mevcut!",
"cake_pay_learn_more": "Uygulamada anında hediye kartları satın alın ve harcayın!\nDaha fazla öğrenmek için soldan sağa kaydır.",
"cake_pay_save_order": "Kart, 1 İş Günü içinde e-postanıza gönderilmelidir \n Sipariş Kimliğinizi Kaydet:",
"cake_pay_subtitle": "Dünya çapında ön ödemeli kartlar ve hediye kartları satın alın",
"cake_pay_web_cards_subtitle": "Dünya çapında ön ödemeli kartlar ve hediye kartları satın alın",
"cake_pay_web_cards_title": "Cake Pay Web Kartları",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Ücret kesintisini onaylayın",
"confirm_fee_deduction_content": "Ücreti çıktıdan düşürmeyi kabul ediyor musunuz?",
"confirm_sending": "Göndermeyi onayla",
"confirm_silent_payments_switch_node": "Şu anda sessiz ödemeleri taramak için düğümleri değiştirmek gerekiyor",
"confirm_silent_payments_switch_node": "Mevcut düğümünüz sessiz ödemeleri desteklemiyor \\ nCake cüzdanı, sadece tarama için uyumlu bir düğüme geçecektir",
"confirmations": "Onay",
"confirmed": "Onaylanmış Bakiye",
"confirmed_tx": "Onaylanmış",
@ -266,6 +267,7 @@
"errorSigningTransaction": "İşlem imzalanırken bir hata oluştu",
"estimated": "Tahmini",
"estimated_new_fee": "Tahmini yeni ücret",
"estimated_receive_amount": "Tahmini alma miktarı",
"etherscan_history": "Etherscan geçmişi",
"event": "Etkinlik",
"events": "Olaylar",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Темна тема торта",
"cake_pay_account_note": "Зареєструйтеся, використовуючи лише адресу електронної пошти, щоб переглядати та купувати картки. Деякі навіть доступні зі знижкою!",
"cake_pay_learn_more": "Миттєво купуйте та активуйте подарункові картки в додатку!\nПроведіть пальцем зліва направо, щоб дізнатися більше.",
"cake_pay_save_order": "Картка повинна бути надіслана на вашу електронну пошту протягом 1 робочого дня \n Зберегти ідентифікатор замовлення:",
"cake_pay_subtitle": "Купіть у всьому світі передплачені картки та подарункові картки",
"cake_pay_web_cards_subtitle": "Купуйте передоплачені та подарункові картки по всьому світу",
"cake_pay_web_cards_title": "Веб-картки Cake Pay",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Підтвердьте відрахування комісії",
"confirm_fee_deduction_content": "Чи погоджуєтесь ви вирахувати комісію з сумми одержувача?",
"confirm_sending": "Підтвердити відправлення",
"confirm_silent_payments_switch_node": "В даний час потрібно перемикати вузли на сканування мовчазних платежів",
"confirm_silent_payments_switch_node": "Ваш поточний вузол не підтримує мовчазні платежі \\ ncake Wallet перейде на сумісний вузол, лише для сканування",
"confirmations": "Підтвердження",
"confirmed": "Підтверджений баланс",
"confirmed_tx": "Підтверджений",
@ -266,6 +267,7 @@
"errorSigningTransaction": "Під час підписання транзакції сталася помилка",
"estimated": "Приблизно ",
"estimated_new_fee": "Орієнтовна нова комісія",
"estimated_receive_amount": "Орієнтовна сума отримує",
"etherscan_history": "Історія Etherscan",
"event": "Подія",
"events": "Події",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "کیک ڈارک تھیم",
"cake_pay_account_note": "کارڈز دیکھنے اور خریدنے کے لیے صرف ایک ای میل ایڈریس کے ساتھ سائن اپ کریں۔ کچھ رعایت پر بھی دستیاب ہیں!",
"cake_pay_learn_more": "ایپ میں فوری طور پر گفٹ کارڈز خریدیں اور بھنائیں!\\nمزید جاننے کے لیے بائیں سے دائیں سوائپ کریں۔",
"cake_pay_save_order": "کارڈ 1 کاروباری دن کے اندر آپ کے ای میل پر بھیجا جانا چاہئے \n اپنے آرڈر کی شناخت کو بچائیں:",
"cake_pay_subtitle": "دنیا بھر میں پری پیڈ کارڈز اور گفٹ کارڈ خریدیں",
"cake_pay_web_cards_subtitle": "دنیا بھر میں پری پیڈ کارڈز اور گفٹ کارڈز خریدیں۔",
"cake_pay_web_cards_title": "Cake پے ویب کارڈز",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "فیس میں کٹوتی کی تصدیق کریں",
"confirm_fee_deduction_content": "کیا آپ آؤٹ پٹ سے فیس کم کرنے پر راضی ہیں؟",
"confirm_sending": "بھیجنے کی تصدیق کریں۔",
"confirm_silent_payments_switch_node": "فی الحال خاموش ادائیگیوں کو اسکین کرنے کے لئے نوڈس کو تبدیل کرنے کی ضرورت ہے",
"confirm_silent_payments_switch_node": "آپ کا موجودہ نوڈ خاموش ادائیگیوں کی حمایت نہیں کرتا ہے۔",
"confirmations": "تصدیقات",
"confirmed": "تصدیق شدہ بیلنس",
"confirmed_tx": "تصدیق",
@ -266,6 +267,7 @@
"errorSigningTransaction": "۔ﮯﮨ ﯽﺌﮔﺁ ﺶﯿﭘ ﯽﺑﺍﺮﺧ ﮏﯾﺍ ﺖﻗﻭ ﮯﺗﺮﮐ ﻂﺨﺘﺳﺩ ﺮﭘ ﻦﯾﺩ ﻦﯿﻟ",
"estimated": "تخمینہ لگایا",
"estimated_new_fee": "تخمینہ شدہ نئی فیس",
"estimated_receive_amount": "تخمینہ وصول کی رقم",
"etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ",
"event": "ﺐﯾﺮﻘﺗ",
"events": "ﺕﺎﺒﯾﺮﻘﺗ",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "Akara oyinbo dudu koko",
"cake_pay_account_note": "Ẹ fi àdírẹ́sì ímeèlì nìkan forúkọ sílẹ̀ k'ẹ́ rí àti ra àwọn káàdì. Ẹ lè fi owó tó kéré jù ra àwọn káàdì kan!",
"cake_pay_learn_more": "Láìpẹ́ ra àti lo àwọn káàdí ìrajà t'á lò nínú irú kan ìtajà nínú áàpù!\nẸ tẹ̀ òsì de ọ̀tún láti kọ́ jù.",
"cake_pay_save_order": "Kaadi yẹ ki o firanṣẹ si imeeli rẹ laarin ọjọ iṣowo 1 \n Fipamọ aṣẹ rẹ:",
"cake_pay_subtitle": "Ra awọn kaadi ti a san ni agbaye ati awọn kaadi ẹbun",
"cake_pay_web_cards_subtitle": "Ra àwọn káàdì ìrajà t'á lò nínú ìtajà kan àti àwọn káàdì náà t'á lè lò níbikíbi",
"cake_pay_web_cards_title": "Àwọn káàdì wẹ́ẹ̀bù ti Cake Pay",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "Jẹrisi iyọkuro owo",
"confirm_fee_deduction_content": "Ṣe o gba lati yọkuro idiyele naa kuro ni iṣejade?",
"confirm_sending": "Jẹ́rìí sí ránṣẹ́",
"confirm_silent_payments_switch_node": "Lọwọlọwọ o nilo lati yi awọn apa pada si awọn sisanwo ipalọlọ",
"confirm_silent_payments_switch_node": "Ilode rẹ ti lọwọlọwọ ko ṣe atilẹyin awọn sisanwo ti o dakẹ \\ owet apamọwọ yoo yipada si oju-ọrọ ibaramu, o kan fun Scning",
"confirmations": "Àwọn ẹ̀rí",
"confirmed": "A ti jẹ́rìí ẹ̀",
"confirmed_tx": "Jẹrisi",
@ -267,6 +268,7 @@
"errorSigningTransaction": "Aṣiṣe kan ti waye lakoko ti o fowo si iṣowo",
"estimated": "Ó tó a fojú díwọ̀n",
"estimated_new_fee": "Ifoju tuntun owo tuntun",
"estimated_receive_amount": "Ifoju gba iye",
"etherscan_history": "Etherscan itan",
"event": "Iṣẹlẹ",
"events": "Awọn iṣẹlẹ",

View file

@ -95,6 +95,7 @@
"cake_dark_theme": "蛋糕黑暗主题",
"cake_pay_account_note": "只需使用電子郵件地址註冊即可查看和購買卡片。有些甚至可以打折!",
"cake_pay_learn_more": "立即在应用中购买和兑换礼品卡!\n从左向右滑动以了解详情。",
"cake_pay_save_order": "该卡应在1个工作日内发送到您的电子邮件\n保存您的订单ID",
"cake_pay_subtitle": "购买全球预付费卡和礼品卡",
"cake_pay_web_cards_subtitle": "购买全球预付卡和礼品卡",
"cake_pay_web_cards_title": "蛋糕支付网络卡",
@ -142,7 +143,7 @@
"confirm_fee_deduction": "确认费用扣除",
"confirm_fee_deduction_content": "您是否同意从产出中扣除费用?",
"confirm_sending": "确认发送",
"confirm_silent_payments_switch_node": "目前需要切换节点来扫描无声付款",
"confirm_silent_payments_switch_node": "您当前的节点不支持无声付款\\ ncake钱包将切换到兼容节点仅用于扫描",
"confirmations": "确认",
"confirmed": "确认余额",
"confirmed_tx": "确认的",
@ -266,6 +267,7 @@
"errorSigningTransaction": "签署交易时发生错误",
"estimated": "估计值",
"estimated_new_fee": "估计新费用",
"estimated_receive_amount": "估计接收金额",
"etherscan_history": "以太扫描历史",
"event": "事件",
"events": "活动",

View file

@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.16.0"
MONERO_COM_BUILD_NUMBER=94
MONERO_COM_VERSION="1.16.1"
MONERO_COM_BUILD_NUMBER=95
MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app"
MONERO_COM_SCHEME="monero.com"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.19.0"
CAKEWALLET_BUILD_NUMBER=220
CAKEWALLET_VERSION="4.19.1"
CAKEWALLET_BUILD_NUMBER=221
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
CAKEWALLET_SCHEME="cakewallet"

View file

@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.16.0"
MONERO_COM_BUILD_NUMBER=92
MONERO_COM_VERSION="1.16.1"
MONERO_COM_BUILD_NUMBER=93
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.19.0"
CAKEWALLET_BUILD_NUMBER=254
CAKEWALLET_VERSION="4.19.1"
CAKEWALLET_BUILD_NUMBER=256
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
HAVEN_NAME="Haven"

View file

@ -19,6 +19,7 @@ NPROC="-j$(sysctl -n hw.logicalcpu)"
for COIN in monero wownero;
do
pushd ../monero_c
rm -rf external/ios/build
./build_single.sh ${COIN} host-apple-ios $NPROC
popd
done

View file

@ -16,13 +16,13 @@ if [ -n "$1" ]; then
fi
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.6.0"
MONERO_COM_BUILD_NUMBER=24
MONERO_COM_VERSION="1.6.1"
MONERO_COM_BUILD_NUMBER=26
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="1.12.0"
CAKEWALLET_BUILD_NUMBER=81
CAKEWALLET_VERSION="1.12.1"
CAKEWALLET_BUILD_NUMBER=82
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then

View file

@ -8,7 +8,7 @@ if [[ ! -d "monero_c" ]];
then
git clone https://github.com/mrcyjanek/monero_c --branch rewrite-wip
cd monero_c
git checkout eaa7bdb8be3479418445ddb18bf33d453f64afcf
git checkout c094ed5da69d2274747bf6edd7ca24124487bd34
git reset --hard
git submodule update --init --force --recursive
./apply_patches.sh monero

View file

@ -1,5 +1,5 @@
#define MyAppName "Cake Wallet"
#define MyAppVersion "0.0.2"
#define MyAppVersion "0.0.3"
#define MyAppPublisher "Cake Labs LLC"
#define MyAppURL "https://cakewallet.com/"
#define MyAppExeName "CakeWallet.exe"

View file

@ -2,6 +2,8 @@ import 'utils/translation/arb_file_utils.dart';
import 'utils/translation/translation_constants.dart';
import 'utils/translation/translation_utils.dart';
/// flutter packages pub run tool/append_translation.dart "hello_world" "Hello World!"
void main(List<String> args) async {
if (args.length != 2) {
throw Exception(

View file

@ -0,0 +1,50 @@
import 'package:dio/dio.dart';
import 'package:archive/archive_io.dart';
final _dio = Dio();
final List<String> triplets = [
"x86_64-linux-gnu", // linux desktop - majority of users onlinux
// "i686-linux-gnu", // not supported by cake
// "i686-meego-linux-gnu", // sailfishos (emulator)- not supported by cake
// "aarch64-linux-gnu", // not (yet) supported by cake - (mostly) mobile linux
// "aarch64-meego-linux-gnu", // sailfishos - not supported by cake
"x86_64-linux-android",
// "i686-linux-android", // not supported by monero_c - mostly old android emulators
"aarch64-linux-android",
"armv7a-linux-androideabi",
// "i686-w64-mingw32", // 32bit windows - not supported by monero_c
"x86_64-w64-mingw32",
// "x86_64-apple-darwin11", // Intel macbooks (contrib) - not used by cake
// "aarch64-apple-darwin11", // apple silicon macbooks (contrib) - not used by cake
// "host-apple-darwin", // not available on CI (yet)
// "x86_64-host-apple-darwin", // not available on CI (yet)
"aarch64-host-apple-darwin", // apple silicon macbooks (local builds)
"host-apple-ios",
];
Future<void> main() async {
final resp = await _dio.get("https://api.github.com/repos/mrcyjanek/monero_c/releases");
final data = resp.data[0];
final tagName = data['tag_name'];
print("Downloading artifacts for: ${tagName}");
final assets = data['assets'] as List<dynamic>;
for (var i = 0; i < assets.length; i++) {
for (var triplet in triplets) {
final asset = assets[i];
final filename = asset["name"] as String;
if (!filename.contains(triplet)) continue;
final coin = filename.split("_")[0];
String localFilename = filename.replaceAll("${coin}_${triplet}_", "");
localFilename = "scripts/monero_c/release/${coin}/${triplet}_${localFilename}";
final url = asset["browser_download_url"] as String;
print("- downloading $localFilename");
await _dio.download(url, localFilename);
print(" extracting $localFilename");
final inputStream = InputFileStream(localFilename);
final archive = XZDecoder().decodeBuffer(inputStream);
final outputStream = OutputFileStream(localFilename.replaceAll(".xz", ""));
outputStream.writeBytes(archive);
}
}
}