mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 12:29:51 +00:00
Merge branch 'mweb' of https://github.com/cake-tech/cake_wallet into breez-mweb [skip ci]
This commit is contained in:
commit
f8473fbedc
103 changed files with 3406 additions and 301 deletions
28
.github/workflows/pr_test_build.yml
vendored
28
.github/workflows/pr_test_build.yml
vendored
|
@ -90,6 +90,34 @@ jobs:
|
|||
cd /opt/android/cake_wallet
|
||||
flutter pub get
|
||||
|
||||
|
||||
- name: Install go and gomobile
|
||||
run: |
|
||||
# install go > 1.21:
|
||||
wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz
|
||||
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz
|
||||
export PATH=$PATH:/usr/local/go/bin
|
||||
export PATH=$PATH:~/go/bin
|
||||
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||
gomobile init
|
||||
|
||||
- name: Build mwebd
|
||||
run: |
|
||||
# paths are reset after each step, so we need to set them again:
|
||||
export PATH=$PATH:/usr/local/go/bin
|
||||
export PATH=$PATH:~/go/bin
|
||||
# build mwebd:
|
||||
cd /opt/android/cake_wallet
|
||||
git clone https://github.com/ltcmweb/mwebd
|
||||
cd /opt/android/cake_wallet/mwebd
|
||||
gomobile bind -target=android -androidapi 21 .
|
||||
mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/
|
||||
mv ./mwebd.aar $_
|
||||
cd ..
|
||||
rm -rf mwebd
|
||||
|
||||
|
||||
|
||||
- name: Generate KeyStore
|
||||
run: |
|
||||
cd /opt/android/cake_wallet/android/app
|
||||
|
|
BIN
assets/images/mweb_logo.png
Normal file
BIN
assets/images/mweb_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
|
@ -7,6 +7,7 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
|||
static const p2tr = BitcoinReceivePageOption._('Taproot (P2TR)');
|
||||
static const p2wsh = BitcoinReceivePageOption._('Segwit (P2WSH)');
|
||||
static const p2pkh = BitcoinReceivePageOption._('Legacy (P2PKH)');
|
||||
static const mweb = BitcoinReceivePageOption._('MWEB');
|
||||
|
||||
static const silent_payments = BitcoinReceivePageOption._('Silent Payments');
|
||||
|
||||
|
@ -27,6 +28,11 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
|||
BitcoinReceivePageOption.p2pkh
|
||||
];
|
||||
|
||||
static const allLitecoin = [
|
||||
BitcoinReceivePageOption.p2wpkh,
|
||||
BitcoinReceivePageOption.mweb
|
||||
];
|
||||
|
||||
BitcoinAddressType toType() {
|
||||
switch (this) {
|
||||
case BitcoinReceivePageOption.p2tr:
|
||||
|
@ -39,6 +45,8 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
|||
return P2shAddressType.p2wpkhInP2sh;
|
||||
case BitcoinReceivePageOption.silent_payments:
|
||||
return SilentPaymentsAddresType.p2sp;
|
||||
case BitcoinReceivePageOption.mweb:
|
||||
return SegwitAddresType.mweb;
|
||||
case BitcoinReceivePageOption.p2wpkh:
|
||||
default:
|
||||
return SegwitAddresType.p2wpkh;
|
||||
|
@ -51,6 +59,8 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
|||
return BitcoinReceivePageOption.p2tr;
|
||||
case SegwitAddresType.p2wsh:
|
||||
return BitcoinReceivePageOption.p2wsh;
|
||||
case SegwitAddresType.mweb:
|
||||
return BitcoinReceivePageOption.mweb;
|
||||
case P2pkhAddressType.p2pkh:
|
||||
return BitcoinReceivePageOption.p2pkh;
|
||||
case P2shAddressType.p2wpkhInP2sh:
|
||||
|
|
|
@ -87,7 +87,7 @@ class LitecoinTransactionPriority extends BitcoinTransactionPriority {
|
|||
}
|
||||
|
||||
@override
|
||||
String get units => 'Latoshi';
|
||||
String get units => 'Litoshi';
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
|
|
|
@ -353,14 +353,18 @@ class ElectrumClient {
|
|||
// "height": 520481,
|
||||
// "hex": "00000020890208a0ae3a3892aa047c5468725846577cfcd9b512b50000000000000000005dc2b02f2d297a9064ee103036c14d678f9afc7e3d9409cf53fd58b82e938e8ecbeca05a2d2103188ce804c4"
|
||||
// }
|
||||
Future<int?> getCurrentBlockChainTip() =>
|
||||
callWithTimeout(method: 'blockchain.headers.subscribe').then((result) {
|
||||
if (result is Map<String, dynamic>) {
|
||||
return result["height"] as int;
|
||||
BehaviorSubject<Map<String, dynamic>>? tipListener;
|
||||
int? currentTip;
|
||||
Future<int?> getCurrentBlockChainTip() async {
|
||||
final method = 'blockchain.headers.subscribe';
|
||||
final cb = (result) => currentTip = result['height'] as int;
|
||||
if (tipListener == null) {
|
||||
tipListener = subscribe(id: method, method: method);
|
||||
tipListener?.listen(cb);
|
||||
callWithTimeout(method: method).then(cb);
|
||||
}
|
||||
return currentTip;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
BehaviorSubject<Object>? chainTipSubscribe() {
|
||||
_id += 1;
|
||||
|
@ -454,6 +458,12 @@ class ElectrumClient {
|
|||
|
||||
void _methodHandler({required String method, required Map<String, dynamic> request}) {
|
||||
switch (method) {
|
||||
case 'blockchain.headers.subscribe':
|
||||
final params = request['params'] as List<dynamic>;
|
||||
final id = 'blockchain.headers.subscribe';
|
||||
|
||||
_tasks[id]?.subject?.add(params.last);
|
||||
break;
|
||||
case 'blockchain.scripthash.subscribe':
|
||||
final params = request['params'] as List<dynamic>;
|
||||
final scripthash = params.first as String?;
|
||||
|
|
|
@ -23,6 +23,7 @@ import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
|||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:cw_bitcoin/exceptions.dart';
|
||||
import 'package:cw_bitcoin/litecoin_network.dart';
|
||||
import 'package:cw_bitcoin/litecoin_wallet.dart';
|
||||
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
|
||||
import 'package:cw_bitcoin/script_hash.dart';
|
||||
import 'package:cw_bitcoin/utils.dart';
|
||||
|
@ -156,11 +157,13 @@ abstract class ElectrumWalletBase
|
|||
Set<String> get addressesSet => walletAddresses.allAddresses.map((addr) => addr.address).toSet();
|
||||
|
||||
List<String> get scriptHashes => walletAddresses.addressesByReceiveType
|
||||
.where((addr) => addressTypeFromStr(addr.address, network) is! MwebAddress)
|
||||
.map((addr) => scriptHash(addr.address, network: network))
|
||||
.toList();
|
||||
|
||||
List<String> get publicScriptHashes => walletAddresses.allAddresses
|
||||
.where((addr) => !addr.isHidden)
|
||||
.where((addr) => addressTypeFromStr(addr.address, network) is! MwebAddress)
|
||||
.map((addr) => scriptHash(addr.address, network: network))
|
||||
.toList();
|
||||
|
||||
|
@ -405,18 +408,21 @@ abstract class ElectrumWalletBase
|
|||
@override
|
||||
Future<void> startSync() async {
|
||||
try {
|
||||
if (this is! LitecoinWallet) {
|
||||
syncStatus = SyncronizingSyncStatus();
|
||||
}
|
||||
|
||||
if (hasSilentPaymentsScanning) {
|
||||
await _setInitialHeight();
|
||||
}
|
||||
|
||||
await _subscribeForUpdates();
|
||||
|
||||
await subscribeForUpdates();
|
||||
await updateTransactions();
|
||||
|
||||
await updateAllUnspents();
|
||||
await updateBalance();
|
||||
|
||||
await updateFeeRates();
|
||||
Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates());
|
||||
|
||||
if (alwaysScan == true) {
|
||||
|
@ -589,26 +595,15 @@ abstract class ElectrumWalletBase
|
|||
paysToSilentPayment: hasSilentPayment,
|
||||
);
|
||||
|
||||
int estimatedSize;
|
||||
if (network is BitcoinCashNetwork) {
|
||||
estimatedSize = ForkedTransactionBuilder.estimateTransactionSize(
|
||||
utxos: utxoDetails.utxos,
|
||||
outputs: outputs,
|
||||
network: network as BitcoinCashNetwork,
|
||||
memo: memo,
|
||||
);
|
||||
} else {
|
||||
estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize(
|
||||
int fee = await calcFee(
|
||||
utxos: utxoDetails.utxos,
|
||||
outputs: outputs,
|
||||
network: network,
|
||||
memo: memo,
|
||||
feeRate: feeRate,
|
||||
inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
|
||||
vinOutpoints: utxoDetails.vinOutpoints,
|
||||
);
|
||||
}
|
||||
|
||||
int fee = feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize);
|
||||
|
||||
if (fee == 0) {
|
||||
throw BitcoinTransactionNoFeeException();
|
||||
|
@ -702,26 +697,13 @@ abstract class ElectrumWalletBase
|
|||
value: BigInt.from(amountLeftForChangeAndFee),
|
||||
));
|
||||
|
||||
int estimatedSize;
|
||||
if (network is BitcoinCashNetwork) {
|
||||
estimatedSize = ForkedTransactionBuilder.estimateTransactionSize(
|
||||
utxos: utxoDetails.utxos,
|
||||
outputs: outputs,
|
||||
network: network as BitcoinCashNetwork,
|
||||
memo: memo,
|
||||
);
|
||||
} else {
|
||||
estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize(
|
||||
int fee = await calcFee(
|
||||
utxos: utxoDetails.utxos,
|
||||
outputs: outputs,
|
||||
network: network,
|
||||
memo: memo,
|
||||
inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
|
||||
vinOutpoints: utxoDetails.vinOutpoints,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
}
|
||||
|
||||
int fee = feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize);
|
||||
|
||||
if (fee == 0) {
|
||||
throw BitcoinTransactionNoFeeException();
|
||||
|
@ -731,6 +713,8 @@ abstract class ElectrumWalletBase
|
|||
final lastOutput = outputs.last;
|
||||
final amountLeftForChange = amountLeftForChangeAndFee - fee;
|
||||
|
||||
print(amountLeftForChangeAndFee);
|
||||
|
||||
if (!_isBelowDust(amountLeftForChange)) {
|
||||
// Here, lastOutput already is change, return the amount left without the fee to the user's address.
|
||||
outputs[outputs.length - 1] =
|
||||
|
@ -811,6 +795,37 @@ abstract class ElectrumWalletBase
|
|||
);
|
||||
}
|
||||
|
||||
Future<int> calcFee({
|
||||
required List<UtxoWithAddress> utxos,
|
||||
required List<BitcoinBaseOutput> outputs,
|
||||
required BasedUtxoNetwork network,
|
||||
String? memo,
|
||||
required int feeRate,
|
||||
List<ECPrivateInfo>? inputPrivKeyInfos,
|
||||
List<Outpoint>? vinOutpoints,
|
||||
}) async {
|
||||
int estimatedSize;
|
||||
if (network is BitcoinCashNetwork) {
|
||||
estimatedSize = ForkedTransactionBuilder.estimateTransactionSize(
|
||||
utxos: utxos,
|
||||
outputs: outputs,
|
||||
network: network,
|
||||
memo: memo,
|
||||
);
|
||||
} else {
|
||||
estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize(
|
||||
utxos: utxos,
|
||||
outputs: outputs,
|
||||
network: network,
|
||||
memo: memo,
|
||||
inputPrivKeyInfos: inputPrivKeyInfos,
|
||||
vinOutpoints: vinOutpoints,
|
||||
);
|
||||
}
|
||||
|
||||
return feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
try {
|
||||
|
@ -1151,9 +1166,13 @@ abstract class ElectrumWalletBase
|
|||
}));
|
||||
|
||||
unspentCoins = updatedUnspentCoins;
|
||||
}
|
||||
|
||||
if (unspentCoinsInfo.isEmpty) {
|
||||
unspentCoins.forEach((coin) => _addCoinInfo(coin));
|
||||
Future<void> updateUnspent() async {
|
||||
await updateAllUnspents();
|
||||
|
||||
if (unspentCoinsInfo.length != unspentCoins.length) {
|
||||
unspentCoins.forEach((coin) => addCoinInfo(coin));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1173,7 +1192,7 @@ abstract class ElectrumWalletBase
|
|||
if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
|
||||
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||
} else {
|
||||
_addCoinInfo(coin);
|
||||
addCoinInfo(coin);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1205,7 +1224,7 @@ abstract class ElectrumWalletBase
|
|||
if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
|
||||
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||
} else {
|
||||
_addCoinInfo(coin);
|
||||
addCoinInfo(coin);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1213,10 +1232,17 @@ abstract class ElectrumWalletBase
|
|||
|
||||
@action
|
||||
Future<List<BitcoinUnspent>> fetchUnspent(BitcoinAddressRecord address) async {
|
||||
final unspents = await electrumClient.getListUnspent(address.getScriptHash(network));
|
||||
|
||||
List<Map<String, dynamic>> unspents = [];
|
||||
List<BitcoinUnspent> updatedUnspentCoins = [];
|
||||
|
||||
try {
|
||||
unspents = await electrumClient.getListUnspent(address.getScriptHash(network));
|
||||
} catch (e, s) {
|
||||
print(e);
|
||||
print(s);
|
||||
return [];
|
||||
}
|
||||
|
||||
await Future.wait(unspents.map((unspent) async {
|
||||
try {
|
||||
final coin = BitcoinUnspent.fromJSON(address, unspent);
|
||||
|
@ -1232,7 +1258,7 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
@action
|
||||
Future<void> _addCoinInfo(BitcoinUnspent coin) async {
|
||||
Future<void> addCoinInfo(BitcoinUnspent coin) async {
|
||||
final newInfo = UnspentCoinsInfo(
|
||||
walletId: id,
|
||||
hash: coin.hash,
|
||||
|
@ -1568,7 +1594,7 @@ abstract class ElectrumWalletBase
|
|||
matchedAddresses.toList(),
|
||||
addressRecord.isHidden,
|
||||
(address) async {
|
||||
await _subscribeForUpdates();
|
||||
await subscribeForUpdates();
|
||||
return _fetchAddressHistory(address, await getCurrentChainTip())
|
||||
.then((history) => history.isNotEmpty ? address.address : null);
|
||||
},
|
||||
|
@ -1657,7 +1683,7 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _subscribeForUpdates() async {
|
||||
Future<void> subscribeForUpdates() async {
|
||||
final unsubscribedScriptHashes = walletAddresses.allAddresses.where(
|
||||
(address) => !_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)),
|
||||
);
|
||||
|
@ -1685,8 +1711,10 @@ abstract class ElectrumWalletBase
|
|||
}));
|
||||
}
|
||||
|
||||
Future<ElectrumBalance> _fetchBalances() async {
|
||||
final addresses = walletAddresses.allAddresses.toList();
|
||||
Future<ElectrumBalance> fetchBalances() async {
|
||||
final addresses = walletAddresses.allAddresses
|
||||
.where((address) => addressTypeFromStr(address.address, network) is! MwebAddress)
|
||||
.toList();
|
||||
final balanceFutures = <Future<Map<String, dynamic>>>[];
|
||||
for (var i = 0; i < addresses.length; i++) {
|
||||
final addressRecord = addresses[i];
|
||||
|
@ -1724,6 +1752,7 @@ abstract class ElectrumWalletBase
|
|||
totalConfirmed += confirmed;
|
||||
totalUnconfirmed += unconfirmed;
|
||||
|
||||
addressRecord.balance = confirmed + unconfirmed;
|
||||
if (confirmed > 0 || unconfirmed > 0) {
|
||||
addressRecord.setAsUsed();
|
||||
}
|
||||
|
@ -1734,22 +1763,10 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
Future<void> updateBalance() async {
|
||||
balance[currency] = await _fetchBalances();
|
||||
balance[currency] = await fetchBalances();
|
||||
await save();
|
||||
}
|
||||
|
||||
String getChangeAddress() {
|
||||
const minCountOfHiddenAddresses = 5;
|
||||
final random = Random();
|
||||
var addresses = walletAddresses.allAddresses.where((addr) => addr.isHidden).toList();
|
||||
|
||||
if (addresses.length < minCountOfHiddenAddresses) {
|
||||
addresses = walletAddresses.allAddresses.toList();
|
||||
}
|
||||
|
||||
return addresses[random.nextInt(addresses.length)].address;
|
||||
}
|
||||
|
||||
@override
|
||||
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError;
|
||||
|
||||
|
@ -2126,6 +2143,8 @@ BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network)
|
|||
return P2wshAddress.fromAddress(address: address, network: network);
|
||||
} else if (P2trAddress.regex.hasMatch(address)) {
|
||||
return P2trAddress.fromAddress(address: address, network: network);
|
||||
} else if (MwebAddress.regex.hasMatch(address)) {
|
||||
return MwebAddress.fromAddress(address: address, network: network);
|
||||
} else if (SilentPaymentAddress.regex.hasMatch(address)) {
|
||||
return SilentPaymentAddress.fromAddress(address);
|
||||
} else {
|
||||
|
@ -2142,6 +2161,8 @@ BitcoinAddressType _getScriptType(BitcoinBaseAddress type) {
|
|||
return SegwitAddresType.p2wsh;
|
||||
} else if (type is P2trAddress) {
|
||||
return SegwitAddresType.p2tr;
|
||||
} else if (type is MwebAddress) {
|
||||
return SegwitAddresType.mweb;
|
||||
} else if (type is SilentPaymentsAddresType) {
|
||||
return SilentPaymentsAddresType.p2sp;
|
||||
} else {
|
||||
|
|
|
@ -16,6 +16,7 @@ const List<BitcoinAddressType> ADDRESS_TYPES = [
|
|||
P2pkhAddressType.p2pkh,
|
||||
SegwitAddresType.p2tr,
|
||||
SegwitAddresType.p2wsh,
|
||||
SegwitAddresType.mweb,
|
||||
P2shAddressType.p2wpkhInP2sh,
|
||||
];
|
||||
|
||||
|
@ -214,6 +215,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
} else if (walletInfo.type == WalletType.litecoin) {
|
||||
await _generateInitialAddresses();
|
||||
await _generateInitialAddresses(type: SegwitAddresType.mweb);
|
||||
} else if (walletInfo.type == WalletType.bitcoin) {
|
||||
await _generateInitialAddresses();
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
|
@ -321,17 +323,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
{required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) =>
|
||||
'';
|
||||
|
||||
@override
|
||||
Future<void> updateAddressesInBox() async {
|
||||
try {
|
||||
addressesMap.clear();
|
||||
addressesMap[address] = 'Active';
|
||||
|
||||
allAddressesMap.clear();
|
||||
_addresses.forEach((addressRecord) {
|
||||
allAddressesMap[addressRecord.address] = addressRecord.name;
|
||||
});
|
||||
Future<String> getAddressAsync(
|
||||
{required int index,
|
||||
required bitcoin.HDWallet hd,
|
||||
BitcoinAddressType? addressType}) async =>
|
||||
getAddress(index: index, hd: hd, addressType: addressType);
|
||||
|
||||
void addBitcoinAddressTypes() {
|
||||
final lastP2wpkh = _addresses
|
||||
.where((addressRecord) =>
|
||||
_isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
|
||||
|
@ -388,6 +386,43 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
addressesMap[address] = 'Active - Silent Payments';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAddressesInBox() async {
|
||||
try {
|
||||
addressesMap.clear();
|
||||
addressesMap[address] = 'Active';
|
||||
|
||||
allAddressesMap.clear();
|
||||
_addresses.forEach((addressRecord) {
|
||||
allAddressesMap[addressRecord.address] = addressRecord.name;
|
||||
});
|
||||
|
||||
if (walletInfo.type == WalletType.bitcoin) {
|
||||
addBitcoinAddressTypes();
|
||||
}
|
||||
|
||||
if (walletInfo.type == WalletType.litecoin) {
|
||||
final lastP2wpkh = _addresses
|
||||
.where((addressRecord) =>
|
||||
_isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
|
||||
.toList()
|
||||
.last;
|
||||
if (lastP2wpkh.address != address) {
|
||||
addressesMap[lastP2wpkh.address] = 'P2WPKH';
|
||||
} else {
|
||||
addressesMap[address] = 'Active - P2WPKH';
|
||||
}
|
||||
|
||||
final lastMweb = _addresses.firstWhere(
|
||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb));
|
||||
if (lastMweb.address != address) {
|
||||
addressesMap[lastMweb.address] = 'MWEB';
|
||||
} else {
|
||||
addressesMap[address] = 'Active - MWEB';
|
||||
}
|
||||
}
|
||||
|
||||
await saveAddressesInBox();
|
||||
} catch (e) {
|
||||
|
@ -508,7 +543,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
for (var i = startIndex; i < count + startIndex; i++) {
|
||||
final address = BitcoinAddressRecord(
|
||||
getAddress(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType),
|
||||
await getAddressAsync(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType),
|
||||
index: i,
|
||||
isHidden: isHidden,
|
||||
type: type ?? addressPageType,
|
||||
|
@ -543,7 +578,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
if (!element.isHidden && element.address !=
|
||||
getAddress(index: element.index, hd: mainHd, addressType: element.type)) {
|
||||
element.isHidden = true;
|
||||
} else if (element.isHidden && element.address !=
|
||||
} else if (element.isHidden &&
|
||||
element.address !=
|
||||
getAddress(index: element.index, hd: sideHd, addressType: element.type)) {
|
||||
element.isHidden = false;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,23 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:convert/convert.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/mweb_utxo.dart';
|
||||
import 'package:cw_mweb/mwebd.pbgrpc.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
|
||||
import 'package:cw_bitcoin/utils.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_bitcoin/litecoin_wallet_addresses.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
|
@ -9,11 +25,14 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||
import 'package:cw_bitcoin/litecoin_network.dart';
|
||||
import 'package:cw_mweb/cw_mweb.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
|
||||
part 'litecoin_wallet.g.dart';
|
||||
|
@ -32,7 +51,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
ElectrumBalance? initialBalance,
|
||||
Map<String, int>? initialRegularAddressIndex,
|
||||
Map<String, int>? initialChangeAddressIndex,
|
||||
}) : super(
|
||||
int? initialMwebHeight,
|
||||
bool? alwaysScan,
|
||||
}) : mwebHd =
|
||||
bitcoin.HDWallet.fromSeed(seedBytes, network: litecoinNetwork).derivePath("m/1000'"),
|
||||
super(
|
||||
mnemonic: mnemonic,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
|
@ -43,6 +66,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
seedBytes: seedBytes,
|
||||
currency: CryptoCurrency.ltc,
|
||||
) {
|
||||
mwebEnabled = alwaysScan ?? false;
|
||||
walletAddresses = LitecoinWalletAddresses(
|
||||
walletInfo,
|
||||
initialAddresses: initialAddresses,
|
||||
|
@ -51,12 +75,24 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
mainHd: hd,
|
||||
sideHd: accountHD.derive(1),
|
||||
network: network,
|
||||
mwebHd: mwebHd,
|
||||
);
|
||||
autorun((_) {
|
||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||
});
|
||||
CwMweb.stub().then((value) {
|
||||
_stub = value;
|
||||
});
|
||||
}
|
||||
|
||||
final bitcoin.HDWallet mwebHd;
|
||||
late final Box<MwebUtxo> mwebUtxosBox;
|
||||
Timer? _syncTimer;
|
||||
StreamSubscription<Utxo>? _utxoStream;
|
||||
int mwebUtxosHeight = 0;
|
||||
late RpcClient _stub;
|
||||
late bool mwebEnabled;
|
||||
|
||||
static Future<LitecoinWallet> create(
|
||||
{required String mnemonic,
|
||||
required String password,
|
||||
|
@ -101,6 +137,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
required String password,
|
||||
required bool alwaysScan,
|
||||
}) async {
|
||||
final snp =
|
||||
await ElectrumWalletSnapshot.load(name, walletInfo.type, password, LitecoinNetwork.mainnet);
|
||||
|
@ -115,9 +152,466 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||
addressPageType: snp.addressPageType,
|
||||
alwaysScan: alwaysScan,
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> startSync() async {
|
||||
if (!mwebEnabled) {
|
||||
syncStatus = SyncronizingSyncStatus();
|
||||
await subscribeForUpdates();
|
||||
await updateTransactions();
|
||||
syncStatus = SyncedSyncStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
await subscribeForUpdates();
|
||||
await updateTransactions();
|
||||
await updateFeeRates();
|
||||
|
||||
Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates());
|
||||
|
||||
_stub = await CwMweb.stub();
|
||||
_syncTimer?.cancel();
|
||||
_syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async {
|
||||
if (syncStatus is FailedSyncStatus) return;
|
||||
final height = await electrumClient.getCurrentBlockChainTip() ?? 0;
|
||||
final resp = await _stub.status(StatusRequest());
|
||||
if (resp.blockHeaderHeight < height) {
|
||||
int h = resp.blockHeaderHeight;
|
||||
syncStatus = SyncingSyncStatus(height - h, h / height);
|
||||
} else if (resp.mwebHeaderHeight < height) {
|
||||
int h = resp.mwebHeaderHeight;
|
||||
syncStatus = SyncingSyncStatus(height - h, h / height);
|
||||
} else if (resp.mwebUtxosHeight < height) {
|
||||
syncStatus = SyncingSyncStatus(1, 0.999);
|
||||
} else {
|
||||
// prevent unnecessary reaction triggers:
|
||||
if (syncStatus is! SyncedSyncStatus) {
|
||||
syncStatus = SyncedSyncStatus();
|
||||
}
|
||||
|
||||
if (resp.mwebUtxosHeight > mwebUtxosHeight) {
|
||||
mwebUtxosHeight = resp.mwebUtxosHeight;
|
||||
await checkMwebUtxosSpent();
|
||||
// update the confirmations for each transaction:
|
||||
for (final transaction in transactionHistory.transactions.values) {
|
||||
if (transaction.isPending) continue;
|
||||
final confirmations = mwebUtxosHeight - transaction.height + 1;
|
||||
if (transaction.confirmations == confirmations) continue;
|
||||
transaction.confirmations = confirmations;
|
||||
transactionHistory.addOne(transaction);
|
||||
}
|
||||
await transactionHistory.save();
|
||||
}
|
||||
}
|
||||
});
|
||||
updateUnspent();
|
||||
fetchBalances();
|
||||
// this runs in the background and processes new utxos as they come in:
|
||||
processMwebUtxos();
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> stopSync() async {
|
||||
_syncTimer?.cancel();
|
||||
_utxoStream?.cancel();
|
||||
await CwMweb.stop();
|
||||
}
|
||||
|
||||
Future<void> initMwebUtxosBox() async {
|
||||
final boxName = "${walletInfo.name.replaceAll(" ", "_")}_${MwebUtxo.boxName}";
|
||||
|
||||
mwebUtxosBox = await CakeHive.openBox<MwebUtxo>(boxName);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> renameWalletFiles(String newWalletName) async {
|
||||
// rename the hive box:
|
||||
final oldBoxName = "${walletInfo.name.replaceAll(" ", "_")}_${MwebUtxo.boxName}";
|
||||
final newBoxName = "${newWalletName.replaceAll(" ", "_")}_${MwebUtxo.boxName}";
|
||||
|
||||
final oldBox = await Hive.openBox<MwebUtxo>(oldBoxName);
|
||||
mwebUtxosBox = await CakeHive.openBox<MwebUtxo>(newBoxName);
|
||||
for (final key in oldBox.keys) {
|
||||
await mwebUtxosBox.put(key, oldBox.get(key)!);
|
||||
}
|
||||
|
||||
await super.renameWalletFiles(newWalletName);
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> rescan({
|
||||
required int height,
|
||||
int? chainTip,
|
||||
ScanData? scanData,
|
||||
bool? doSingleScan,
|
||||
bool? usingElectrs,
|
||||
}) async {
|
||||
await mwebUtxosBox.clear();
|
||||
transactionHistory.clear();
|
||||
mwebUtxosHeight = height;
|
||||
await walletInfo.updateRestoreHeight(height);
|
||||
|
||||
// reset coin balances and txCount to 0:
|
||||
unspentCoins.forEach((coin) {
|
||||
if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
|
||||
coin.bitcoinAddressRecord.balance = 0;
|
||||
coin.bitcoinAddressRecord.txCount = 0;
|
||||
});
|
||||
|
||||
for (var addressRecord in walletAddresses.allAddresses) {
|
||||
addressRecord.balance = 0;
|
||||
addressRecord.txCount = 0;
|
||||
}
|
||||
|
||||
print("STARTING SYNC");
|
||||
await startSync();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
await super.init();
|
||||
await initMwebUtxosBox();
|
||||
}
|
||||
|
||||
Future<void> handleIncoming(MwebUtxo utxo, RpcClient stub) async {
|
||||
final status = await stub.status(StatusRequest());
|
||||
var date = DateTime.now();
|
||||
var confirmations = 0;
|
||||
if (utxo.height > 0) {
|
||||
date = DateTime.fromMillisecondsSinceEpoch(utxo.blockTime * 1000);
|
||||
confirmations = status.blockHeaderHeight - utxo.height + 1;
|
||||
}
|
||||
var tx = transactionHistory.transactions.values
|
||||
.firstWhereOrNull((tx) => tx.outputAddresses?.contains(utxo.outputId) ?? false);
|
||||
|
||||
if (tx == null) {
|
||||
tx = ElectrumTransactionInfo(
|
||||
WalletType.litecoin,
|
||||
id: utxo.outputId,
|
||||
height: utxo.height,
|
||||
amount: utxo.value.toInt(),
|
||||
fee: 0,
|
||||
direction: TransactionDirection.incoming,
|
||||
isPending: utxo.height == 0,
|
||||
date: date,
|
||||
confirmations: confirmations,
|
||||
inputAddresses: [],
|
||||
outputAddresses: [utxo.outputId],
|
||||
);
|
||||
}
|
||||
|
||||
bool isNew = transactionHistory.transactions[tx.id] == null;
|
||||
|
||||
// don't update the confirmations if the tx is updated by electrum:
|
||||
if (tx.confirmations == 0 || utxo.height != 0) {
|
||||
tx.height = utxo.height;
|
||||
tx.isPending = utxo.height == 0;
|
||||
tx.confirmations = confirmations;
|
||||
}
|
||||
|
||||
if (!(tx.outputAddresses?.contains(utxo.address) ?? false)) {
|
||||
tx.outputAddresses?.add(utxo.address);
|
||||
isNew = true;
|
||||
}
|
||||
|
||||
if (isNew) {
|
||||
final addressRecord = walletAddresses.allAddresses
|
||||
.firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address);
|
||||
if (addressRecord == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if our address isn't in the inputs, update the txCount:
|
||||
final inputAddresses = tx.inputAddresses ?? [];
|
||||
if (!inputAddresses.contains(utxo.address)) {
|
||||
addressRecord.txCount++;
|
||||
}
|
||||
|
||||
addressRecord.balance += utxo.value.toInt();
|
||||
addressRecord.setAsUsed();
|
||||
}
|
||||
|
||||
transactionHistory.addOne(tx);
|
||||
|
||||
if (isNew) {
|
||||
// update the unconfirmed balance when a new tx is added:
|
||||
// we do this after adding the tx to the history so that sub address balances are updated correctly
|
||||
// (since that calculation is based on the tx history)
|
||||
await updateBalance();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> processMwebUtxos() async {
|
||||
final scanSecret = mwebHd.derive(0x80000000).privKey!;
|
||||
int restoreHeight = walletInfo.restoreHeight;
|
||||
print("SCANNING FROM HEIGHT: $restoreHeight");
|
||||
final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: restoreHeight);
|
||||
|
||||
// process old utxos:
|
||||
for (final utxo in mwebUtxosBox.values) {
|
||||
if (utxo.address.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if (walletInfo.restoreHeight > utxo.height) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
await handleIncoming(utxo, _stub);
|
||||
|
||||
if (utxo.height > walletInfo.restoreHeight) {
|
||||
await walletInfo.updateRestoreHeight(utxo.height);
|
||||
}
|
||||
}
|
||||
|
||||
// process new utxos as they come in:
|
||||
_utxoStream?.cancel();
|
||||
_utxoStream = _stub.utxos(req).listen((Utxo sUtxo) async {
|
||||
final utxo = MwebUtxo(
|
||||
address: sUtxo.address,
|
||||
blockTime: sUtxo.blockTime,
|
||||
height: sUtxo.height,
|
||||
outputId: sUtxo.outputId,
|
||||
value: sUtxo.value.toInt(),
|
||||
);
|
||||
|
||||
if (mwebUtxosBox.containsKey(utxo.outputId)) {
|
||||
// we've already stored this utxo, skip it:
|
||||
return;
|
||||
}
|
||||
|
||||
// if (utxo.address.isEmpty) {
|
||||
// await updateUnspent();
|
||||
// await updateBalance();
|
||||
// initDone = true;
|
||||
// }
|
||||
|
||||
await updateUnspent();
|
||||
await updateBalance();
|
||||
|
||||
final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs;
|
||||
|
||||
// don't process utxos with addresses that are not in the mwebAddrs list:
|
||||
if (utxo.address.isNotEmpty && !mwebAddrs.contains(utxo.address)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await mwebUtxosBox.put(utxo.outputId, utxo);
|
||||
|
||||
await handleIncoming(utxo, _stub);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> checkMwebUtxosSpent() async {
|
||||
while ((await Future.wait(transactionHistory.transactions.values
|
||||
.where((tx) => tx.direction == TransactionDirection.outgoing && tx.isPending)
|
||||
.map(checkPendingTransaction)))
|
||||
.any((x) => x));
|
||||
final outputIds =
|
||||
mwebUtxosBox.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId).toList();
|
||||
|
||||
final resp = await _stub.spent(SpentRequest(outputId: outputIds));
|
||||
final spent = resp.outputId;
|
||||
if (spent.isEmpty) return;
|
||||
final status = await _stub.status(StatusRequest());
|
||||
final height = await electrumClient.getCurrentBlockChainTip();
|
||||
if (height == null || status.blockHeaderHeight != height) return;
|
||||
if (status.mwebUtxosHeight != height) return;
|
||||
int amount = 0;
|
||||
Set<String> inputAddresses = {};
|
||||
var output = AccumulatorSink<Digest>();
|
||||
var input = sha256.startChunkedConversion(output);
|
||||
for (final outputId in spent) {
|
||||
final utxo = mwebUtxosBox.get(outputId);
|
||||
await mwebUtxosBox.delete(outputId);
|
||||
if (utxo == null) continue;
|
||||
final addressRecord = walletAddresses.allAddresses
|
||||
.firstWhere((addressRecord) => addressRecord.address == utxo.address);
|
||||
if (!inputAddresses.contains(utxo.address)) {
|
||||
addressRecord.txCount++;
|
||||
// print("COUNT UPDATED HERE 3!!!!! ${addressRecord.address} ${addressRecord.txCount} !!!!!!");
|
||||
}
|
||||
addressRecord.balance -= utxo.value.toInt();
|
||||
amount += utxo.value.toInt();
|
||||
inputAddresses.add(utxo.address);
|
||||
input.add(hex.decode(outputId));
|
||||
}
|
||||
if (inputAddresses.isEmpty) return;
|
||||
input.close();
|
||||
var digest = output.events.single;
|
||||
final tx = ElectrumTransactionInfo(
|
||||
WalletType.litecoin,
|
||||
id: digest.toString(),
|
||||
height: height,
|
||||
amount: amount,
|
||||
fee: 0,
|
||||
direction: TransactionDirection.outgoing,
|
||||
isPending: false,
|
||||
date: DateTime.fromMillisecondsSinceEpoch(status.blockTime * 1000),
|
||||
confirmations: 1,
|
||||
inputAddresses: inputAddresses.toList(),
|
||||
outputAddresses: [],
|
||||
);
|
||||
print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@2");
|
||||
|
||||
transactionHistory.addOne(tx);
|
||||
await transactionHistory.save();
|
||||
}
|
||||
|
||||
Future<bool> checkPendingTransaction(ElectrumTransactionInfo tx) async {
|
||||
if (!tx.isPending) return false;
|
||||
final outputId = <String>[], target = <String>{};
|
||||
final isHash = RegExp(r'^[a-f0-9]{64}$').hasMatch;
|
||||
final spendingOutputIds = tx.inputAddresses?.where(isHash) ?? [];
|
||||
final payingToOutputIds = tx.outputAddresses?.where(isHash) ?? [];
|
||||
outputId.addAll(spendingOutputIds);
|
||||
outputId.addAll(payingToOutputIds);
|
||||
target.addAll(spendingOutputIds);
|
||||
for (final outputId in payingToOutputIds) {
|
||||
final spendingTx = transactionHistory.transactions.values
|
||||
.firstWhereOrNull((tx) => tx.inputAddresses?.contains(outputId) ?? false);
|
||||
if (spendingTx != null && !spendingTx.isPending) {
|
||||
target.add(outputId);
|
||||
}
|
||||
}
|
||||
if (outputId.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
final resp = await _stub.spent(SpentRequest(outputId: outputId));
|
||||
if (!setEquals(resp.outputId.toSet(), target)) return false;
|
||||
final status = await _stub.status(StatusRequest());
|
||||
if (!tx.isPending) return false;
|
||||
tx.height = status.mwebUtxosHeight;
|
||||
tx.confirmations = 1;
|
||||
tx.isPending = false;
|
||||
await transactionHistory.save();
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateUnspent() async {
|
||||
await super.updateUnspent();
|
||||
await checkMwebUtxosSpent();
|
||||
}
|
||||
|
||||
@override
|
||||
@action
|
||||
Future<void> updateAllUnspents() async {
|
||||
List<BitcoinUnspent> updatedUnspentCoins = [];
|
||||
|
||||
await Future.wait(walletAddresses.allAddresses.map((address) async {
|
||||
updatedUnspentCoins.addAll(await fetchUnspent(address));
|
||||
}));
|
||||
|
||||
if (mwebEnabled) {
|
||||
// update mweb unspents:
|
||||
final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs;
|
||||
mwebUtxosBox.keys.forEach((dynamic oId) {
|
||||
final String outputId = oId as String;
|
||||
final utxo = mwebUtxosBox.get(outputId);
|
||||
if (utxo == null) {
|
||||
return;
|
||||
}
|
||||
if (utxo.address.isEmpty) {
|
||||
// not sure if a bug or a special case but we definitely ignore these
|
||||
return;
|
||||
}
|
||||
final addressRecord = walletAddresses.allAddresses
|
||||
.firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address);
|
||||
|
||||
if (addressRecord == null) {
|
||||
print("utxo contains an address that is not in the wallet: ${utxo.address}");
|
||||
return;
|
||||
}
|
||||
final unspent = BitcoinUnspent(
|
||||
addressRecord,
|
||||
outputId,
|
||||
utxo.value.toInt(),
|
||||
mwebAddrs.indexOf(utxo.address),
|
||||
);
|
||||
if (unspent.vout == 0) {
|
||||
unspent.isChange = true;
|
||||
}
|
||||
updatedUnspentCoins.add(unspent);
|
||||
});
|
||||
}
|
||||
|
||||
unspentCoins = updatedUnspentCoins;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ElectrumBalance> fetchBalances() async {
|
||||
final balance = await super.fetchBalances();
|
||||
var confirmed = balance.confirmed;
|
||||
var unconfirmed = balance.unconfirmed;
|
||||
mwebUtxosBox.values.forEach((utxo) {
|
||||
if (utxo.height > 0) {
|
||||
confirmed += utxo.value.toInt();
|
||||
} else {
|
||||
unconfirmed += utxo.value.toInt();
|
||||
}
|
||||
});
|
||||
|
||||
// update unspent balances:
|
||||
|
||||
// reset coin balances and txCount to 0:
|
||||
// unspentCoins.forEach((coin) {
|
||||
// if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
|
||||
// coin.bitcoinAddressRecord.balance = 0;
|
||||
// coin.bitcoinAddressRecord.txCount = 0;
|
||||
// });
|
||||
for (var addressRecord in walletAddresses.allAddresses) {
|
||||
addressRecord.balance = 0;
|
||||
addressRecord.txCount = 0;
|
||||
}
|
||||
|
||||
unspentCoins.forEach((coin) {
|
||||
final coinInfoList = unspentCoinsInfo.values.where(
|
||||
(element) =>
|
||||
element.walletId.contains(id) &&
|
||||
element.hash.contains(coin.hash) &&
|
||||
element.vout == coin.vout,
|
||||
);
|
||||
|
||||
if (coinInfoList.isNotEmpty) {
|
||||
final coinInfo = coinInfoList.first;
|
||||
|
||||
coin.isFrozen = coinInfo.isFrozen;
|
||||
coin.isSending = coinInfo.isSending;
|
||||
coin.note = coinInfo.note;
|
||||
if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
|
||||
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||
} else {
|
||||
super.addCoinInfo(coin);
|
||||
}
|
||||
});
|
||||
|
||||
// update the txCount for each address using the tx history, since we can't rely on mwebd
|
||||
// to have an accurate count, we should just keep it in sync with what we know from the tx history:
|
||||
for (var tx in transactionHistory.transactions.values) {
|
||||
if (tx.isPending) continue;
|
||||
final txAddresses = tx.inputAddresses! + tx.outputAddresses!;
|
||||
for (var address in txAddresses) {
|
||||
final addressRecord = walletAddresses.allAddresses
|
||||
.firstWhereOrNull((addressRecord) => addressRecord.address == address);
|
||||
if (addressRecord == null) {
|
||||
continue;
|
||||
}
|
||||
addressRecord.txCount++;
|
||||
}
|
||||
}
|
||||
|
||||
await updateUnspent();
|
||||
|
||||
return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen);
|
||||
}
|
||||
|
||||
@override
|
||||
int feeRate(TransactionPriority priority) {
|
||||
if (priority is LitecoinTransactionPriority) {
|
||||
|
@ -133,4 +627,159 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> calcFee({
|
||||
required List<UtxoWithAddress> utxos,
|
||||
required List<BitcoinBaseOutput> outputs,
|
||||
required BasedUtxoNetwork network,
|
||||
String? memo,
|
||||
required int feeRate,
|
||||
List<ECPrivateInfo>? inputPrivKeyInfos,
|
||||
List<Outpoint>? vinOutpoints,
|
||||
}) async {
|
||||
final spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb);
|
||||
final paysToMweb = outputs
|
||||
.any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb);
|
||||
if (!spendsMweb && !paysToMweb) {
|
||||
return await super.calcFee(
|
||||
utxos: utxos,
|
||||
outputs: outputs,
|
||||
network: network,
|
||||
memo: memo,
|
||||
feeRate: feeRate,
|
||||
inputPrivKeyInfos: inputPrivKeyInfos,
|
||||
vinOutpoints: vinOutpoints,
|
||||
);
|
||||
}
|
||||
if (outputs.length == 1 && outputs[0].toOutput.amount == BigInt.zero) {
|
||||
outputs = [
|
||||
BitcoinScriptOutput(
|
||||
script: outputs[0].toOutput.scriptPubKey, value: utxos.sumOfUtxosValue())
|
||||
];
|
||||
}
|
||||
final preOutputSum =
|
||||
outputs.fold<BigInt>(BigInt.zero, (acc, output) => acc + output.toOutput.amount);
|
||||
final fee = utxos.sumOfUtxosValue() - preOutputSum;
|
||||
final txb =
|
||||
BitcoinTransactionBuilder(utxos: utxos, outputs: outputs, fee: fee, network: network);
|
||||
final resp = await _stub.create(CreateRequest(
|
||||
rawTx: txb.buildTransaction((a, b, c, d) => '').toBytes(),
|
||||
scanSecret: hex.decode(mwebHd.derive(0x80000000).privKey!),
|
||||
spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!),
|
||||
feeRatePerKb: Int64(feeRate * 1000),
|
||||
dryRun: true));
|
||||
final tx = BtcTransaction.fromRaw(hex.encode(resp.rawTx));
|
||||
final posUtxos = utxos
|
||||
.where((utxo) => tx.inputs
|
||||
.any((input) => input.txId == utxo.utxo.txHash && input.txIndex == utxo.utxo.vout))
|
||||
.toList();
|
||||
final posOutputSum = tx.outputs.fold<int>(0, (acc, output) => acc + output.amount.toInt());
|
||||
final mwebInputSum = utxos.sumOfUtxosValue() - posUtxos.sumOfUtxosValue();
|
||||
final expectedPegin = max(0, (preOutputSum - mwebInputSum).toInt());
|
||||
var feeIncrease = posOutputSum - expectedPegin;
|
||||
if (expectedPegin > 0 && fee == BigInt.zero) {
|
||||
feeIncrease += await super.calcFee(
|
||||
utxos: posUtxos,
|
||||
outputs: tx.outputs
|
||||
.map((output) =>
|
||||
BitcoinScriptOutput(script: output.scriptPubKey, value: output.amount))
|
||||
.toList(),
|
||||
network: network,
|
||||
memo: memo,
|
||||
feeRate: feeRate) +
|
||||
feeRate * 41;
|
||||
}
|
||||
return fee.toInt() + feeIncrease;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
try {
|
||||
var tx = await super.createTransaction(credentials) as PendingBitcoinTransaction;
|
||||
tx.isMweb = mwebEnabled;
|
||||
|
||||
if (!mwebEnabled) {
|
||||
return tx;
|
||||
}
|
||||
|
||||
final resp = await _stub.create(CreateRequest(
|
||||
rawTx: hex.decode(tx.hex),
|
||||
scanSecret: hex.decode(mwebHd.derive(0x80000000).privKey!),
|
||||
spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!),
|
||||
feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000,
|
||||
));
|
||||
final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx));
|
||||
tx.hexOverride = tx2
|
||||
.copyWith(
|
||||
witnesses: tx2.inputs.asMap().entries.map((e) {
|
||||
final utxo = unspentCoins
|
||||
.firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex);
|
||||
final key = generateECPrivate(
|
||||
hd: utxo.bitcoinAddressRecord.isHidden
|
||||
? walletAddresses.sideHd
|
||||
: walletAddresses.mainHd,
|
||||
index: utxo.bitcoinAddressRecord.index,
|
||||
network: network);
|
||||
final digest = tx2.getTransactionSegwitDigit(
|
||||
txInIndex: e.key,
|
||||
script: key.getPublic().toP2pkhAddress().toScriptPubKey(),
|
||||
amount: BigInt.from(utxo.value),
|
||||
);
|
||||
return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]);
|
||||
}).toList())
|
||||
.toHex();
|
||||
tx.outputs = resp.outputId;
|
||||
return tx
|
||||
..addListener((transaction) async {
|
||||
final addresses = <String>{};
|
||||
transaction.inputAddresses?.forEach((id) async {
|
||||
final utxo = mwebUtxosBox.get(id);
|
||||
await mwebUtxosBox.delete(id);
|
||||
if (utxo == null) return;
|
||||
final addressRecord = walletAddresses.allAddresses
|
||||
.firstWhere((addressRecord) => addressRecord.address == utxo.address);
|
||||
if (!addresses.contains(utxo.address)) {
|
||||
addresses.add(utxo.address);
|
||||
}
|
||||
addressRecord.balance -= utxo.value.toInt();
|
||||
});
|
||||
transaction.inputAddresses?.addAll(addresses);
|
||||
|
||||
transactionHistory.addOne(transaction);
|
||||
await updateUnspent();
|
||||
await updateBalance();
|
||||
});
|
||||
} catch (e, s) {
|
||||
print(e);
|
||||
print(s);
|
||||
if (e.toString().contains("commit failed")) {
|
||||
throw Exception("Transaction commit failed (no peers responded), please try again.");
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> save() async {
|
||||
await super.save();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await super.close();
|
||||
await mwebUtxosBox.close();
|
||||
_syncTimer?.cancel();
|
||||
_utxoStream?.cancel();
|
||||
}
|
||||
|
||||
void setMwebEnabled(bool enabled) {
|
||||
if (mwebEnabled == enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
mwebEnabled = enabled;
|
||||
stopSync();
|
||||
startSync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import 'package:convert/convert.dart';
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||
import 'package:cw_bitcoin/utils.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_mweb/cw_mweb.dart';
|
||||
import 'package:cw_mweb/mwebd.pb.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'litecoin_wallet_addresses.g.dart';
|
||||
|
@ -14,14 +17,62 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
WalletInfo walletInfo, {
|
||||
required super.mainHd,
|
||||
required super.sideHd,
|
||||
required this.mwebHd,
|
||||
required super.network,
|
||||
super.initialAddresses,
|
||||
super.initialRegularAddressIndex,
|
||||
super.initialChangeAddressIndex,
|
||||
}) : super(walletInfo);
|
||||
}) : super(walletInfo) {
|
||||
topUpMweb(0);
|
||||
}
|
||||
|
||||
final HDWallet mwebHd;
|
||||
List<String> mwebAddrs = [];
|
||||
|
||||
Future<void> topUpMweb(int index) async {
|
||||
while (mwebAddrs.length - index < 1000) {
|
||||
final length = mwebAddrs.length;
|
||||
final scanSecret = mwebHd.derive(0x80000000).privKey!;
|
||||
final spendPubkey = mwebHd.derive(0x80000001).pubKey!;
|
||||
final stub = await CwMweb.stub();
|
||||
final resp = await stub.addresses(AddressRequest(
|
||||
fromIndex: length,
|
||||
toIndex: index + 1000,
|
||||
scanSecret: hex.decode(scanSecret),
|
||||
spendPubkey: hex.decode(spendPubkey),
|
||||
));
|
||||
if (mwebAddrs.length == length) {
|
||||
mwebAddrs.addAll(resp.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String getAddress(
|
||||
{required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) =>
|
||||
generateP2WPKHAddress(hd: hd, index: index, network: network);
|
||||
String getAddress({required int index, required HDWallet hd, BitcoinAddressType? addressType}) {
|
||||
if (addressType == SegwitAddresType.mweb) {
|
||||
topUpMweb(index);
|
||||
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
|
||||
}
|
||||
return generateP2WPKHAddress(hd: hd, index: index, network: network);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getAddressAsync(
|
||||
{required int index, required HDWallet hd, BitcoinAddressType? addressType}) async {
|
||||
if (addressType == SegwitAddresType.mweb) {
|
||||
await topUpMweb(index);
|
||||
}
|
||||
return getAddress(index: index, hd: hd, addressType: addressType);
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<String> getChangeAddress() async {
|
||||
// super.getChangeAddress();
|
||||
// updateChangeAddresses();
|
||||
// print("getChangeAddress @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
// this means all change addresses used will be mweb addresses!:
|
||||
await topUpMweb(0);
|
||||
return mwebAddrs[0];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,15 +12,18 @@ import 'package:cw_core/wallet_info.dart';
|
|||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class LitecoinWalletService extends WalletService<
|
||||
BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinRestoreWalletFromWIFCredentials,BitcoinNewWalletCredentials> {
|
||||
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
BitcoinRestoreWalletFromWIFCredentials,
|
||||
BitcoinNewWalletCredentials> {
|
||||
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
|
||||
final bool alwaysScan;
|
||||
|
||||
@override
|
||||
WalletType getType() => WalletType.litecoin;
|
||||
|
@ -32,7 +35,8 @@ class LitecoinWalletService extends WalletService<
|
|||
password: credentials.password!,
|
||||
passphrase: credentials.passphrase,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
|
||||
|
@ -45,21 +49,29 @@ class LitecoinWalletService extends WalletService<
|
|||
|
||||
@override
|
||||
Future<LitecoinWallet> openWallet(String name, String password) async {
|
||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||
final walletInfo = walletInfoSource.values
|
||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||
|
||||
try {
|
||||
final wallet = await LitecoinWalletBase.open(
|
||||
password: password, name: name, walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
password: password,
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
alwaysScan: alwaysScan,
|
||||
);
|
||||
await wallet.init();
|
||||
saveBackup(name);
|
||||
return wallet;
|
||||
} catch (_) {
|
||||
await restoreWalletFilesFromBackup(name);
|
||||
final wallet = await LitecoinWalletBase.open(
|
||||
password: password, name: name, walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
password: password,
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
alwaysScan: alwaysScan,
|
||||
);
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
|
@ -67,22 +79,32 @@ class LitecoinWalletService extends WalletService<
|
|||
|
||||
@override
|
||||
Future<void> remove(String wallet) async {
|
||||
File(await pathForWalletDir(name: wallet, type: getType()))
|
||||
.delete(recursive: true);
|
||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
|
||||
final walletInfo = walletInfoSource.values
|
||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||
await walletInfoSource.delete(walletInfo.key);
|
||||
|
||||
// if there are no more litecoin wallets left, delete the neutrino db:
|
||||
if (walletInfoSource.values.where((info) => info.type == WalletType.litecoin).isEmpty) {
|
||||
final appDir = await getApplicationSupportDirectory();
|
||||
File neturinoDb = File('${appDir.path}/neutrino.db');
|
||||
if (neturinoDb.existsSync()) {
|
||||
neturinoDb.deleteSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> rename(String currentName, String password, String newName) async {
|
||||
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
||||
final currentWalletInfo = walletInfoSource.values
|
||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
||||
final currentWallet = await LitecoinWalletBase.open(
|
||||
password: password,
|
||||
name: currentName,
|
||||
walletInfo: currentWalletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
alwaysScan: alwaysScan,
|
||||
);
|
||||
|
||||
await currentWallet.renameWalletFiles(newName);
|
||||
await saveBackup(newName);
|
||||
|
@ -96,17 +118,18 @@ class LitecoinWalletService extends WalletService<
|
|||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromHardwareWallet(BitcoinNewWalletCredentials credentials) {
|
||||
throw UnimplementedError("Restoring a Litecoin wallet from a hardware wallet is not yet supported!");
|
||||
throw UnimplementedError(
|
||||
"Restoring a Litecoin wallet from a hardware wallet is not yet supported!");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromKeys(
|
||||
BitcoinRestoreWalletFromWIFCredentials credentials, {bool? isTestnet}) async =>
|
||||
Future<LitecoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
|
||||
{bool? isTestnet}) async =>
|
||||
throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromSeed(
|
||||
BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
||||
Future<LitecoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) {
|
||||
throw LitecoinMnemonicIsIncorrectException();
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import 'package:grpc/grpc.dart';
|
||||
import 'package:cw_bitcoin/exceptions.dart';
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_bitcoin/electrum.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_mweb/cw_mweb.dart';
|
||||
import 'package:cw_mweb/mwebd.pb.dart';
|
||||
|
||||
class PendingBitcoinTransaction with PendingTransaction {
|
||||
PendingBitcoinTransaction(
|
||||
|
@ -19,6 +23,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
required this.hasChange,
|
||||
this.isSendAll = false,
|
||||
this.hasTaprootInputs = false,
|
||||
this.isMweb = false,
|
||||
}) : _listeners = <void Function(ElectrumTransactionInfo transaction)>[];
|
||||
|
||||
final WalletType type;
|
||||
|
@ -28,15 +33,19 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
final int fee;
|
||||
final String feeRate;
|
||||
final BasedUtxoNetwork? network;
|
||||
final bool hasChange;
|
||||
final bool isSendAll;
|
||||
final bool hasChange;
|
||||
final bool hasTaprootInputs;
|
||||
bool isMweb;
|
||||
String? idOverride;
|
||||
String? hexOverride;
|
||||
List<String>? outputs;
|
||||
|
||||
@override
|
||||
String get id => _tx.txId();
|
||||
String get id => idOverride ?? _tx.txId();
|
||||
|
||||
@override
|
||||
String get hex => _tx.serialize();
|
||||
String get hex => hexOverride ?? _tx.serialize();
|
||||
|
||||
@override
|
||||
String get amountFormatted => bitcoinAmountToString(amount: amount);
|
||||
|
@ -49,8 +58,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
|
||||
final List<void Function(ElectrumTransactionInfo transaction)> _listeners;
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
Future<void> _commit() async {
|
||||
int? callId;
|
||||
|
||||
final result = await electrumClient.broadcastTransaction(
|
||||
|
@ -83,6 +91,25 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
|
||||
throw BitcoinTransactionCommitFailed();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _ltcCommit() async {
|
||||
try {
|
||||
final stub = await CwMweb.stub();
|
||||
final resp = await stub.broadcast(BroadcastRequest(rawTx: BytesUtils.fromHexString(hex)));
|
||||
idOverride = resp.txid;
|
||||
} on GrpcError catch (e) {
|
||||
throw BitcoinTransactionCommitFailed(errorMessage: e.message);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
if (isMweb) {
|
||||
await _ltcCommit();
|
||||
} else {
|
||||
await _commit();
|
||||
}
|
||||
|
||||
_listeners.forEach((listener) => listener(transactionInfo()));
|
||||
}
|
||||
|
@ -98,5 +125,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
date: DateTime.now(),
|
||||
isPending: true,
|
||||
confirmations: 0,
|
||||
inputAddresses: _tx.inputs.map((input) => input.txId).toList(),
|
||||
outputAddresses: outputs,
|
||||
fee: fee);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.7.0"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.10"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -261,6 +269,13 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
cw_mweb:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../cw_mweb"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -375,6 +390,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
googleapis_auth:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: googleapis_auth
|
||||
sha256: af7c3a3edf9d0de2e1e0a77e994fae0a581c525fa7012af4fa0d4a52ed9484da
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -383,6 +406,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
grpc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: grpc
|
||||
sha256: e93ee3bce45c134bf44e9728119102358c7cd69de7832d9a874e2e74eb8cab40
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.4"
|
||||
hex:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -32,7 +32,7 @@ dependencies:
|
|||
bitcoin_base:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_base
|
||||
ref: cake-update-v3
|
||||
ref: cake-mweb
|
||||
blockchain_utils:
|
||||
git:
|
||||
url: https://github.com/cake-tech/blockchain_utils
|
||||
|
@ -41,6 +41,9 @@ dependencies:
|
|||
ledger_bitcoin:
|
||||
git:
|
||||
url: https://github.com/cake-tech/ledger-bitcoin
|
||||
cw_mweb:
|
||||
path: ../cw_mweb
|
||||
grpc: ^3.2.4
|
||||
sp_scanner:
|
||||
git:
|
||||
url: https://github.com/cake-tech/sp_scanner
|
||||
|
@ -57,6 +60,11 @@ dev_dependencies:
|
|||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
protobuf: ^3.1.0
|
||||
bitcoin_base:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_base
|
||||
ref: cake-mweb
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
|
@ -32,7 +32,7 @@ dependencies:
|
|||
bitcoin_base:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_base
|
||||
ref: cake-update-v3
|
||||
ref: cake-mweb
|
||||
blockchain_utils:
|
||||
git:
|
||||
url: https://github.com/cake-tech/blockchain_utils
|
||||
|
@ -47,6 +47,10 @@ dev_dependencies:
|
|||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
bitcoin_base:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_base
|
||||
ref: cake-mweb
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
|
@ -18,3 +18,4 @@ const SPL_TOKEN_TYPE_ID = 16;
|
|||
const DERIVATION_INFO_TYPE_ID = 17;
|
||||
const TRON_TOKEN_TYPE_ID = 18;
|
||||
const HARDWARE_WALLET_TYPE_TYPE_ID = 19;
|
||||
const MWEB_UTXO_TYPE_ID = 20;
|
33
cw_core/lib/mweb_utxo.dart
Normal file
33
cw_core/lib/mweb_utxo.dart
Normal file
|
@ -0,0 +1,33 @@
|
|||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'mweb_utxo.g.dart';
|
||||
|
||||
@HiveType(typeId: MWEB_UTXO_TYPE_ID)
|
||||
class MwebUtxo extends HiveObject {
|
||||
MwebUtxo({
|
||||
required this.height,
|
||||
required this.value,
|
||||
required this.address,
|
||||
required this.outputId,
|
||||
required this.blockTime,
|
||||
});
|
||||
|
||||
static const typeId = MWEB_UTXO_TYPE_ID;
|
||||
static const boxName = 'MwebUtxo';
|
||||
|
||||
@HiveField(0)
|
||||
int height;
|
||||
|
||||
@HiveField(1)
|
||||
int value;
|
||||
|
||||
@HiveField(2)
|
||||
String address;
|
||||
|
||||
@HiveField(3)
|
||||
String outputId;
|
||||
|
||||
@HiveField(4)
|
||||
int blockTime;
|
||||
}
|
|
@ -65,11 +65,12 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
|||
|
||||
Future<void> startSync();
|
||||
|
||||
Future<void> stopSync() async {}
|
||||
|
||||
Future<PendingTransaction> createTransaction(Object credentials);
|
||||
|
||||
int calculateEstimatedFee(TransactionPriority priority, int? amount);
|
||||
|
||||
|
||||
// void fetchTransactionsAsync(
|
||||
// void Function(TransactionType transaction) onTransactionLoaded,
|
||||
// {void Function() onFinished});
|
||||
|
@ -90,7 +91,8 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
|||
|
||||
Future<void> renameWalletFiles(String newWalletName);
|
||||
|
||||
Future<String> signMessage(String message, {String? address = null}) => throw UnimplementedError();
|
||||
Future<String> signMessage(String message, {String? address = null}) =>
|
||||
throw UnimplementedError();
|
||||
|
||||
bool? isTestnet;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ android {
|
|||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
30
cw_mweb/.gitignore
vendored
Normal file
30
cw_mweb/.gitignore
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
36
cw_mweb/.metadata
Normal file
36
cw_mweb/.metadata
Normal file
|
@ -0,0 +1,36 @@
|
|||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
channel: stable
|
||||
|
||||
project_type: plugin
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
- platform: android
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
- platform: ios
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
- platform: macos
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
3
cw_mweb/CHANGELOG.md
Normal file
3
cw_mweb/CHANGELOG.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
1
cw_mweb/LICENSE
Normal file
1
cw_mweb/LICENSE
Normal file
|
@ -0,0 +1 @@
|
|||
TODO: Add your license here.
|
15
cw_mweb/README.md
Normal file
15
cw_mweb/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# cw_mweb
|
||||
|
||||
A new Flutter plugin project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter
|
||||
[plug-in package](https://flutter.dev/developing-packages/),
|
||||
a specialized package that includes platform-specific implementation code for
|
||||
Android and/or iOS.
|
||||
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
|
4
cw_mweb/analysis_options.yaml
Normal file
4
cw_mweb/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
10
cw_mweb/android/.gitignore
vendored
Normal file
10
cw_mweb/android/.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
/libs
|
||||
.cxx
|
76
cw_mweb/android/build.gradle
Normal file
76
cw_mweb/android/build.gradle
Normal file
|
@ -0,0 +1,76 @@
|
|||
group 'com.cakewallet.mweb'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.7.10'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.3.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
flatDir {
|
||||
dirs project(':cw_mweb').file('libs')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
test.java.srcDirs += 'src/test/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation 'org.jetbrains.kotlin:kotlin-test'
|
||||
testImplementation 'org.mockito:mockito-core:5.0.0'
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.all {
|
||||
useJUnitPlatform()
|
||||
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed", "standardOut", "standardError"
|
||||
outputs.upToDateWhen {false}
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation (name: 'mwebd', ext: 'aar')
|
||||
}
|
1
cw_mweb/android/settings.gradle
Normal file
1
cw_mweb/android/settings.gradle
Normal file
|
@ -0,0 +1 @@
|
|||
rootProject.name = 'cw_mweb'
|
3
cw_mweb/android/src/main/AndroidManifest.xml
Normal file
3
cw_mweb/android/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.cakewallet.mweb">
|
||||
</manifest>
|
|
@ -0,0 +1,45 @@
|
|||
package com.cakewallet.mweb
|
||||
|
||||
import androidx.annotation.NonNull
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import io.flutter.plugin.common.MethodChannel.Result
|
||||
|
||||
import mwebd.Mwebd
|
||||
import mwebd.Server
|
||||
|
||||
/** CwMwebPlugin */
|
||||
class CwMwebPlugin: FlutterPlugin, MethodCallHandler {
|
||||
/// The MethodChannel that will the communication between Flutter and native Android
|
||||
///
|
||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
/// when the Flutter Engine is detached from the Activity
|
||||
private lateinit var channel : MethodChannel
|
||||
private var server: Server? = null
|
||||
private var port: Long? = null
|
||||
|
||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "cw_mweb")
|
||||
channel.setMethodCallHandler(this)
|
||||
}
|
||||
|
||||
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
|
||||
if (call.method == "start") {
|
||||
val dataDir = call.argument("dataDir") ?: ""
|
||||
server = server ?: Mwebd.newServer("", dataDir, "")
|
||||
port = port ?: server?.start(0)
|
||||
result.success(port)
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel.setMethodCallHandler(null)
|
||||
server?.stop()
|
||||
server = null
|
||||
}
|
||||
}
|
38
cw_mweb/ios/.gitignore
vendored
Normal file
38
cw_mweb/ios/.gitignore
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
.idea/
|
||||
.vagrant/
|
||||
.sconsign.dblite
|
||||
.svn/
|
||||
|
||||
.DS_Store
|
||||
*.swp
|
||||
profile
|
||||
|
||||
DerivedData/
|
||||
build/
|
||||
GeneratedPluginRegistrant.h
|
||||
GeneratedPluginRegistrant.m
|
||||
|
||||
.generated/
|
||||
|
||||
*.pbxuser
|
||||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.perspectivev3
|
||||
|
||||
!default.pbxuser
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
!default.perspectivev3
|
||||
|
||||
xcuserdata
|
||||
|
||||
*.moved-aside
|
||||
|
||||
*.pyc
|
||||
*sync/
|
||||
Icon?
|
||||
.tags*
|
||||
|
||||
/Flutter/Generated.xcconfig
|
||||
/Flutter/ephemeral/
|
||||
/Flutter/flutter_export_environment.sh
|
0
cw_mweb/ios/Assets/.gitkeep
Normal file
0
cw_mweb/ios/Assets/.gitkeep
Normal file
71
cw_mweb/ios/Classes/CwMwebPlugin.swift
Normal file
71
cw_mweb/ios/Classes/CwMwebPlugin.swift
Normal file
|
@ -0,0 +1,71 @@
|
|||
import Flutter
|
||||
import UIKit
|
||||
import Mwebd
|
||||
|
||||
public class CwMwebPlugin: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "cw_mweb", binaryMessenger: registrar.messenger())
|
||||
let instance = CwMwebPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
|
||||
private static var server: MwebdServer?
|
||||
private static var port: Int = 0
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "getPlatformVersion":
|
||||
result("iOS " + UIDevice.current.systemVersion)
|
||||
case "start":
|
||||
let args = call.arguments as? [String: String]
|
||||
// print("args: \(args)")
|
||||
let dataDir = args?["dataDir"]
|
||||
var error: NSError?
|
||||
|
||||
if dataDir == "stop" && CwMwebPlugin.server != nil {
|
||||
print("Stopping server")
|
||||
CwMwebPlugin.server?.stop()
|
||||
CwMwebPlugin.server = nil
|
||||
result(0)
|
||||
return
|
||||
}
|
||||
|
||||
if CwMwebPlugin.server == nil {
|
||||
CwMwebPlugin.server = MwebdNewServer("", dataDir, "", &error)
|
||||
|
||||
if let server = CwMwebPlugin.server {
|
||||
do {
|
||||
print("starting server \(CwMwebPlugin.port)")
|
||||
try server.start(0, ret0_: &CwMwebPlugin.port)
|
||||
result(CwMwebPlugin.port)
|
||||
} catch let startError as NSError {
|
||||
print("Server Start Error: \(startError.localizedDescription)")
|
||||
result(FlutterError(code: "Server Start Error", message: startError.localizedDescription, details: nil))
|
||||
}
|
||||
} else if let error = error {
|
||||
print("Server Creation Error: \(error.localizedDescription)")
|
||||
result(FlutterError(code: "Server Creation Error", message: error.localizedDescription, details: nil))
|
||||
} else {
|
||||
print("Unknown Error: Failed to create server")
|
||||
result(FlutterError(code: "Unknown Error", message: "Failed to create server", details: nil))
|
||||
}
|
||||
} else {
|
||||
print("Server already running on port: \(CwMwebPlugin.port)")
|
||||
// result(FlutterError(code: "Server Already Running", message: "The server is already running", details: nil))
|
||||
result(CwMwebPlugin.port)
|
||||
}
|
||||
|
||||
|
||||
// result(0)
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
print("Stopping and cleaning up server")
|
||||
// Perform cleanup tasks
|
||||
CwMwebPlugin.server?.stop()
|
||||
CwMwebPlugin.server = nil
|
||||
}
|
||||
}
|
26
cw_mweb/ios/cw_mweb.podspec
Normal file
26
cw_mweb/ios/cw_mweb.podspec
Normal file
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint cw_mweb.podspec` to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'cw_mweb'
|
||||
s.version = '0.0.1'
|
||||
s.summary = 'A new Flutter plugin project.'
|
||||
s.description = <<-DESC
|
||||
A new Flutter plugin project.
|
||||
DESC
|
||||
s.homepage = 'http://example.com'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'Flutter'
|
||||
s.platform = :ios, '11.0'
|
||||
|
||||
# Flutter.framework does not contain a i386 slice.
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
||||
s.swift_version = '5.0'
|
||||
s.ios.vendored_frameworks = 'Mwebd.xcframework'
|
||||
s.preserve_paths = 'Mwebd.xcframework/**/*'
|
||||
|
||||
end
|
23
cw_mweb/lib/cw_mweb.dart
Normal file
23
cw_mweb/lib/cw_mweb.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
import 'package:grpc/grpc.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'cw_mweb_platform_interface.dart';
|
||||
import 'mwebd.pbgrpc.dart';
|
||||
|
||||
class CwMweb {
|
||||
static Future<RpcClient> stub() async {
|
||||
final appDir = await getApplicationSupportDirectory();
|
||||
int port = await CwMwebPlatform.instance.start(appDir.path) ?? 0;
|
||||
return RpcClient(
|
||||
ClientChannel('127.0.0.1',
|
||||
port: port,
|
||||
options: const ChannelOptions(
|
||||
credentials: ChannelCredentials.insecure(),
|
||||
keepAlive: ClientKeepAliveOptions(permitWithoutCalls: true),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> stop() async {
|
||||
await CwMwebPlatform.instance.start("stop");
|
||||
}
|
||||
}
|
17
cw_mweb/lib/cw_mweb_method_channel.dart
Normal file
17
cw_mweb/lib/cw_mweb_method_channel.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'cw_mweb_platform_interface.dart';
|
||||
|
||||
/// An implementation of [CwMwebPlatform] that uses method channels.
|
||||
class MethodChannelCwMweb extends CwMwebPlatform {
|
||||
/// The method channel used to interact with the native platform.
|
||||
@visibleForTesting
|
||||
final methodChannel = const MethodChannel('cw_mweb');
|
||||
|
||||
@override
|
||||
Future<int?> start(String dataDir) async {
|
||||
final result = await methodChannel.invokeMethod<int>('start', {'dataDir': dataDir});
|
||||
return result;
|
||||
}
|
||||
}
|
29
cw_mweb/lib/cw_mweb_platform_interface.dart
Normal file
29
cw_mweb/lib/cw_mweb_platform_interface.dart
Normal file
|
@ -0,0 +1,29 @@
|
|||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
import 'cw_mweb_method_channel.dart';
|
||||
|
||||
abstract class CwMwebPlatform extends PlatformInterface {
|
||||
/// Constructs a CwMwebPlatform.
|
||||
CwMwebPlatform() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static CwMwebPlatform _instance = MethodChannelCwMweb();
|
||||
|
||||
/// The default instance of [CwMwebPlatform] to use.
|
||||
///
|
||||
/// Defaults to [MethodChannelCwMweb].
|
||||
static CwMwebPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [CwMwebPlatform] when
|
||||
/// they register themselves.
|
||||
static set instance(CwMwebPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
Future<int?> start(String dataDir) {
|
||||
throw UnimplementedError('start() has not been implemented.');
|
||||
}
|
||||
}
|
801
cw_mweb/lib/mwebd.pb.dart
Normal file
801
cw_mweb/lib/mwebd.pb.dart
Normal file
|
@ -0,0 +1,801 @@
|
|||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: mwebd.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:fixnum/fixnum.dart' as $fixnum;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
class StatusRequest extends $pb.GeneratedMessage {
|
||||
factory StatusRequest() => create();
|
||||
StatusRequest._() : super();
|
||||
factory StatusRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory StatusRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StatusRequest', createEmptyInstance: create)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
StatusRequest clone() => StatusRequest()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
StatusRequest copyWith(void Function(StatusRequest) updates) => super.copyWith((message) => updates(message as StatusRequest)) as StatusRequest;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static StatusRequest create() => StatusRequest._();
|
||||
StatusRequest createEmptyInstance() => create();
|
||||
static $pb.PbList<StatusRequest> createRepeated() => $pb.PbList<StatusRequest>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static StatusRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<StatusRequest>(create);
|
||||
static StatusRequest? _defaultInstance;
|
||||
}
|
||||
|
||||
class StatusResponse extends $pb.GeneratedMessage {
|
||||
factory StatusResponse({
|
||||
$core.int? blockHeaderHeight,
|
||||
$core.int? mwebHeaderHeight,
|
||||
$core.int? mwebUtxosHeight,
|
||||
$core.int? blockTime,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (blockHeaderHeight != null) {
|
||||
$result.blockHeaderHeight = blockHeaderHeight;
|
||||
}
|
||||
if (mwebHeaderHeight != null) {
|
||||
$result.mwebHeaderHeight = mwebHeaderHeight;
|
||||
}
|
||||
if (mwebUtxosHeight != null) {
|
||||
$result.mwebUtxosHeight = mwebUtxosHeight;
|
||||
}
|
||||
if (blockTime != null) {
|
||||
$result.blockTime = blockTime;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
StatusResponse._() : super();
|
||||
factory StatusResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory StatusResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StatusResponse', createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'blockHeaderHeight', $pb.PbFieldType.O3)
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'mwebHeaderHeight', $pb.PbFieldType.O3)
|
||||
..a<$core.int>(3, _omitFieldNames ? '' : 'mwebUtxosHeight', $pb.PbFieldType.O3)
|
||||
..a<$core.int>(4, _omitFieldNames ? '' : 'blockTime', $pb.PbFieldType.OU3)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
StatusResponse clone() => StatusResponse()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
StatusResponse copyWith(void Function(StatusResponse) updates) => super.copyWith((message) => updates(message as StatusResponse)) as StatusResponse;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static StatusResponse create() => StatusResponse._();
|
||||
StatusResponse createEmptyInstance() => create();
|
||||
static $pb.PbList<StatusResponse> createRepeated() => $pb.PbList<StatusResponse>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static StatusResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<StatusResponse>(create);
|
||||
static StatusResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get blockHeaderHeight => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set blockHeaderHeight($core.int v) { $_setSignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasBlockHeaderHeight() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearBlockHeaderHeight() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get mwebHeaderHeight => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set mwebHeaderHeight($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasMwebHeaderHeight() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearMwebHeaderHeight() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.int get mwebUtxosHeight => $_getIZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set mwebUtxosHeight($core.int v) { $_setSignedInt32(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasMwebUtxosHeight() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearMwebUtxosHeight() => clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.int get blockTime => $_getIZ(3);
|
||||
@$pb.TagNumber(4)
|
||||
set blockTime($core.int v) { $_setUnsignedInt32(3, v); }
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasBlockTime() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearBlockTime() => clearField(4);
|
||||
}
|
||||
|
||||
class UtxosRequest extends $pb.GeneratedMessage {
|
||||
factory UtxosRequest({
|
||||
$core.int? fromHeight,
|
||||
$core.List<$core.int>? scanSecret,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (fromHeight != null) {
|
||||
$result.fromHeight = fromHeight;
|
||||
}
|
||||
if (scanSecret != null) {
|
||||
$result.scanSecret = scanSecret;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
UtxosRequest._() : super();
|
||||
factory UtxosRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory UtxosRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'UtxosRequest', createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'fromHeight', $pb.PbFieldType.O3)
|
||||
..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'scanSecret', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
UtxosRequest clone() => UtxosRequest()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
UtxosRequest copyWith(void Function(UtxosRequest) updates) => super.copyWith((message) => updates(message as UtxosRequest)) as UtxosRequest;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static UtxosRequest create() => UtxosRequest._();
|
||||
UtxosRequest createEmptyInstance() => create();
|
||||
static $pb.PbList<UtxosRequest> createRepeated() => $pb.PbList<UtxosRequest>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static UtxosRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UtxosRequest>(create);
|
||||
static UtxosRequest? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get fromHeight => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set fromHeight($core.int v) { $_setSignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasFromHeight() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearFromHeight() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<$core.int> get scanSecret => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set scanSecret($core.List<$core.int> v) { $_setBytes(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasScanSecret() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearScanSecret() => clearField(2);
|
||||
}
|
||||
|
||||
class Utxo extends $pb.GeneratedMessage {
|
||||
factory Utxo({
|
||||
$core.int? height,
|
||||
$fixnum.Int64? value,
|
||||
$core.String? address,
|
||||
$core.String? outputId,
|
||||
$core.int? blockTime,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (height != null) {
|
||||
$result.height = height;
|
||||
}
|
||||
if (value != null) {
|
||||
$result.value = value;
|
||||
}
|
||||
if (address != null) {
|
||||
$result.address = address;
|
||||
}
|
||||
if (outputId != null) {
|
||||
$result.outputId = outputId;
|
||||
}
|
||||
if (blockTime != null) {
|
||||
$result.blockTime = blockTime;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
Utxo._() : super();
|
||||
factory Utxo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory Utxo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Utxo', createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'height', $pb.PbFieldType.O3)
|
||||
..a<$fixnum.Int64>(2, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
|
||||
..aOS(3, _omitFieldNames ? '' : 'address')
|
||||
..aOS(4, _omitFieldNames ? '' : 'outputId')
|
||||
..a<$core.int>(5, _omitFieldNames ? '' : 'blockTime', $pb.PbFieldType.OU3)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
Utxo clone() => Utxo()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
Utxo copyWith(void Function(Utxo) updates) => super.copyWith((message) => updates(message as Utxo)) as Utxo;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Utxo create() => Utxo._();
|
||||
Utxo createEmptyInstance() => create();
|
||||
static $pb.PbList<Utxo> createRepeated() => $pb.PbList<Utxo>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Utxo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Utxo>(create);
|
||||
static Utxo? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get height => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set height($core.int v) { $_setSignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasHeight() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearHeight() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$fixnum.Int64 get value => $_getI64(1);
|
||||
@$pb.TagNumber(2)
|
||||
set value($fixnum.Int64 v) { $_setInt64(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasValue() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearValue() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.String get address => $_getSZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set address($core.String v) { $_setString(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasAddress() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearAddress() => clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.String get outputId => $_getSZ(3);
|
||||
@$pb.TagNumber(4)
|
||||
set outputId($core.String v) { $_setString(3, v); }
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasOutputId() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearOutputId() => clearField(4);
|
||||
|
||||
@$pb.TagNumber(5)
|
||||
$core.int get blockTime => $_getIZ(4);
|
||||
@$pb.TagNumber(5)
|
||||
set blockTime($core.int v) { $_setUnsignedInt32(4, v); }
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool hasBlockTime() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearBlockTime() => clearField(5);
|
||||
}
|
||||
|
||||
class AddressRequest extends $pb.GeneratedMessage {
|
||||
factory AddressRequest({
|
||||
$core.int? fromIndex,
|
||||
$core.int? toIndex,
|
||||
$core.List<$core.int>? scanSecret,
|
||||
$core.List<$core.int>? spendPubkey,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (fromIndex != null) {
|
||||
$result.fromIndex = fromIndex;
|
||||
}
|
||||
if (toIndex != null) {
|
||||
$result.toIndex = toIndex;
|
||||
}
|
||||
if (scanSecret != null) {
|
||||
$result.scanSecret = scanSecret;
|
||||
}
|
||||
if (spendPubkey != null) {
|
||||
$result.spendPubkey = spendPubkey;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
AddressRequest._() : super();
|
||||
factory AddressRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory AddressRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'AddressRequest', createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'fromIndex', $pb.PbFieldType.OU3)
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'toIndex', $pb.PbFieldType.OU3)
|
||||
..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'scanSecret', $pb.PbFieldType.OY)
|
||||
..a<$core.List<$core.int>>(4, _omitFieldNames ? '' : 'spendPubkey', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
AddressRequest clone() => AddressRequest()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
AddressRequest copyWith(void Function(AddressRequest) updates) => super.copyWith((message) => updates(message as AddressRequest)) as AddressRequest;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static AddressRequest create() => AddressRequest._();
|
||||
AddressRequest createEmptyInstance() => create();
|
||||
static $pb.PbList<AddressRequest> createRepeated() => $pb.PbList<AddressRequest>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static AddressRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<AddressRequest>(create);
|
||||
static AddressRequest? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get fromIndex => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set fromIndex($core.int v) { $_setUnsignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasFromIndex() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearFromIndex() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get toIndex => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set toIndex($core.int v) { $_setUnsignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasToIndex() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearToIndex() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<$core.int> get scanSecret => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set scanSecret($core.List<$core.int> v) { $_setBytes(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasScanSecret() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearScanSecret() => clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.List<$core.int> get spendPubkey => $_getN(3);
|
||||
@$pb.TagNumber(4)
|
||||
set spendPubkey($core.List<$core.int> v) { $_setBytes(3, v); }
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasSpendPubkey() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearSpendPubkey() => clearField(4);
|
||||
}
|
||||
|
||||
class AddressResponse extends $pb.GeneratedMessage {
|
||||
factory AddressResponse({
|
||||
$core.Iterable<$core.String>? address,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (address != null) {
|
||||
$result.address.addAll(address);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
AddressResponse._() : super();
|
||||
factory AddressResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory AddressResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'AddressResponse', createEmptyInstance: create)
|
||||
..pPS(1, _omitFieldNames ? '' : 'address')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
AddressResponse clone() => AddressResponse()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
AddressResponse copyWith(void Function(AddressResponse) updates) => super.copyWith((message) => updates(message as AddressResponse)) as AddressResponse;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static AddressResponse create() => AddressResponse._();
|
||||
AddressResponse createEmptyInstance() => create();
|
||||
static $pb.PbList<AddressResponse> createRepeated() => $pb.PbList<AddressResponse>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static AddressResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<AddressResponse>(create);
|
||||
static AddressResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.String> get address => $_getList(0);
|
||||
}
|
||||
|
||||
class SpentRequest extends $pb.GeneratedMessage {
|
||||
factory SpentRequest({
|
||||
$core.Iterable<$core.String>? outputId,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (outputId != null) {
|
||||
$result.outputId.addAll(outputId);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
SpentRequest._() : super();
|
||||
factory SpentRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory SpentRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SpentRequest', createEmptyInstance: create)
|
||||
..pPS(1, _omitFieldNames ? '' : 'outputId')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
SpentRequest clone() => SpentRequest()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
SpentRequest copyWith(void Function(SpentRequest) updates) => super.copyWith((message) => updates(message as SpentRequest)) as SpentRequest;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SpentRequest create() => SpentRequest._();
|
||||
SpentRequest createEmptyInstance() => create();
|
||||
static $pb.PbList<SpentRequest> createRepeated() => $pb.PbList<SpentRequest>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SpentRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SpentRequest>(create);
|
||||
static SpentRequest? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.String> get outputId => $_getList(0);
|
||||
}
|
||||
|
||||
class SpentResponse extends $pb.GeneratedMessage {
|
||||
factory SpentResponse({
|
||||
$core.Iterable<$core.String>? outputId,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (outputId != null) {
|
||||
$result.outputId.addAll(outputId);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
SpentResponse._() : super();
|
||||
factory SpentResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory SpentResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SpentResponse', createEmptyInstance: create)
|
||||
..pPS(1, _omitFieldNames ? '' : 'outputId')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
SpentResponse clone() => SpentResponse()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
SpentResponse copyWith(void Function(SpentResponse) updates) => super.copyWith((message) => updates(message as SpentResponse)) as SpentResponse;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SpentResponse create() => SpentResponse._();
|
||||
SpentResponse createEmptyInstance() => create();
|
||||
static $pb.PbList<SpentResponse> createRepeated() => $pb.PbList<SpentResponse>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SpentResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SpentResponse>(create);
|
||||
static SpentResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.String> get outputId => $_getList(0);
|
||||
}
|
||||
|
||||
class CreateRequest extends $pb.GeneratedMessage {
|
||||
factory CreateRequest({
|
||||
$core.List<$core.int>? rawTx,
|
||||
$core.List<$core.int>? scanSecret,
|
||||
$core.List<$core.int>? spendSecret,
|
||||
$fixnum.Int64? feeRatePerKb,
|
||||
$core.bool? dryRun,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (rawTx != null) {
|
||||
$result.rawTx = rawTx;
|
||||
}
|
||||
if (scanSecret != null) {
|
||||
$result.scanSecret = scanSecret;
|
||||
}
|
||||
if (spendSecret != null) {
|
||||
$result.spendSecret = spendSecret;
|
||||
}
|
||||
if (feeRatePerKb != null) {
|
||||
$result.feeRatePerKb = feeRatePerKb;
|
||||
}
|
||||
if (dryRun != null) {
|
||||
$result.dryRun = dryRun;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
CreateRequest._() : super();
|
||||
factory CreateRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory CreateRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CreateRequest', createEmptyInstance: create)
|
||||
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'rawTx', $pb.PbFieldType.OY)
|
||||
..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'scanSecret', $pb.PbFieldType.OY)
|
||||
..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'spendSecret', $pb.PbFieldType.OY)
|
||||
..a<$fixnum.Int64>(4, _omitFieldNames ? '' : 'feeRatePerKb', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
|
||||
..aOB(5, _omitFieldNames ? '' : 'dryRun')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
CreateRequest clone() => CreateRequest()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
CreateRequest copyWith(void Function(CreateRequest) updates) => super.copyWith((message) => updates(message as CreateRequest)) as CreateRequest;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static CreateRequest create() => CreateRequest._();
|
||||
CreateRequest createEmptyInstance() => create();
|
||||
static $pb.PbList<CreateRequest> createRepeated() => $pb.PbList<CreateRequest>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static CreateRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CreateRequest>(create);
|
||||
static CreateRequest? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.int> get rawTx => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set rawTx($core.List<$core.int> v) { $_setBytes(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasRawTx() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearRawTx() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<$core.int> get scanSecret => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set scanSecret($core.List<$core.int> v) { $_setBytes(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasScanSecret() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearScanSecret() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<$core.int> get spendSecret => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set spendSecret($core.List<$core.int> v) { $_setBytes(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasSpendSecret() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearSpendSecret() => clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$fixnum.Int64 get feeRatePerKb => $_getI64(3);
|
||||
@$pb.TagNumber(4)
|
||||
set feeRatePerKb($fixnum.Int64 v) { $_setInt64(3, v); }
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasFeeRatePerKb() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearFeeRatePerKb() => clearField(4);
|
||||
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool get dryRun => $_getBF(4);
|
||||
@$pb.TagNumber(5)
|
||||
set dryRun($core.bool v) { $_setBool(4, v); }
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool hasDryRun() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearDryRun() => clearField(5);
|
||||
}
|
||||
|
||||
class CreateResponse extends $pb.GeneratedMessage {
|
||||
factory CreateResponse({
|
||||
$core.List<$core.int>? rawTx,
|
||||
$core.Iterable<$core.String>? outputId,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (rawTx != null) {
|
||||
$result.rawTx = rawTx;
|
||||
}
|
||||
if (outputId != null) {
|
||||
$result.outputId.addAll(outputId);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
CreateResponse._() : super();
|
||||
factory CreateResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory CreateResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CreateResponse', createEmptyInstance: create)
|
||||
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'rawTx', $pb.PbFieldType.OY)
|
||||
..pPS(2, _omitFieldNames ? '' : 'outputId')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
CreateResponse clone() => CreateResponse()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
CreateResponse copyWith(void Function(CreateResponse) updates) => super.copyWith((message) => updates(message as CreateResponse)) as CreateResponse;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static CreateResponse create() => CreateResponse._();
|
||||
CreateResponse createEmptyInstance() => create();
|
||||
static $pb.PbList<CreateResponse> createRepeated() => $pb.PbList<CreateResponse>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static CreateResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CreateResponse>(create);
|
||||
static CreateResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.int> get rawTx => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set rawTx($core.List<$core.int> v) { $_setBytes(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasRawTx() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearRawTx() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<$core.String> get outputId => $_getList(1);
|
||||
}
|
||||
|
||||
class BroadcastRequest extends $pb.GeneratedMessage {
|
||||
factory BroadcastRequest({
|
||||
$core.List<$core.int>? rawTx,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (rawTx != null) {
|
||||
$result.rawTx = rawTx;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
BroadcastRequest._() : super();
|
||||
factory BroadcastRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory BroadcastRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BroadcastRequest', createEmptyInstance: create)
|
||||
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'rawTx', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
BroadcastRequest clone() => BroadcastRequest()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
BroadcastRequest copyWith(void Function(BroadcastRequest) updates) => super.copyWith((message) => updates(message as BroadcastRequest)) as BroadcastRequest;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static BroadcastRequest create() => BroadcastRequest._();
|
||||
BroadcastRequest createEmptyInstance() => create();
|
||||
static $pb.PbList<BroadcastRequest> createRepeated() => $pb.PbList<BroadcastRequest>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static BroadcastRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<BroadcastRequest>(create);
|
||||
static BroadcastRequest? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.int> get rawTx => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set rawTx($core.List<$core.int> v) { $_setBytes(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasRawTx() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearRawTx() => clearField(1);
|
||||
}
|
||||
|
||||
class BroadcastResponse extends $pb.GeneratedMessage {
|
||||
factory BroadcastResponse({
|
||||
$core.String? txid,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (txid != null) {
|
||||
$result.txid = txid;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
BroadcastResponse._() : super();
|
||||
factory BroadcastResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory BroadcastResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BroadcastResponse', createEmptyInstance: create)
|
||||
..aOS(1, _omitFieldNames ? '' : 'txid')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
BroadcastResponse clone() => BroadcastResponse()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
BroadcastResponse copyWith(void Function(BroadcastResponse) updates) => super.copyWith((message) => updates(message as BroadcastResponse)) as BroadcastResponse;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static BroadcastResponse create() => BroadcastResponse._();
|
||||
BroadcastResponse createEmptyInstance() => create();
|
||||
static $pb.PbList<BroadcastResponse> createRepeated() => $pb.PbList<BroadcastResponse>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static BroadcastResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<BroadcastResponse>(create);
|
||||
static BroadcastResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get txid => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set txid($core.String v) { $_setString(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasTxid() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearTxid() => clearField(1);
|
||||
}
|
||||
|
||||
|
||||
const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||
const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names');
|
159
cw_mweb/lib/mwebd.pbgrpc.dart
Normal file
159
cw_mweb/lib/mwebd.pbgrpc.dart
Normal file
|
@ -0,0 +1,159 @@
|
|||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: mwebd.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:grpc/service_api.dart' as $grpc;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'mwebd.pb.dart' as $0;
|
||||
|
||||
export 'mwebd.pb.dart';
|
||||
|
||||
@$pb.GrpcServiceName('Rpc')
|
||||
class RpcClient extends $grpc.Client {
|
||||
static final _$status = $grpc.ClientMethod<$0.StatusRequest, $0.StatusResponse>(
|
||||
'/Rpc/Status',
|
||||
($0.StatusRequest value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.StatusResponse.fromBuffer(value));
|
||||
static final _$utxos = $grpc.ClientMethod<$0.UtxosRequest, $0.Utxo>(
|
||||
'/Rpc/Utxos',
|
||||
($0.UtxosRequest value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.Utxo.fromBuffer(value));
|
||||
static final _$addresses = $grpc.ClientMethod<$0.AddressRequest, $0.AddressResponse>(
|
||||
'/Rpc/Addresses',
|
||||
($0.AddressRequest value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.AddressResponse.fromBuffer(value));
|
||||
static final _$spent = $grpc.ClientMethod<$0.SpentRequest, $0.SpentResponse>(
|
||||
'/Rpc/Spent',
|
||||
($0.SpentRequest value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.SpentResponse.fromBuffer(value));
|
||||
static final _$create = $grpc.ClientMethod<$0.CreateRequest, $0.CreateResponse>(
|
||||
'/Rpc/Create',
|
||||
($0.CreateRequest value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.CreateResponse.fromBuffer(value));
|
||||
static final _$broadcast = $grpc.ClientMethod<$0.BroadcastRequest, $0.BroadcastResponse>(
|
||||
'/Rpc/Broadcast',
|
||||
($0.BroadcastRequest value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.BroadcastResponse.fromBuffer(value));
|
||||
|
||||
RpcClient($grpc.ClientChannel channel,
|
||||
{$grpc.CallOptions? options,
|
||||
$core.Iterable<$grpc.ClientInterceptor>? interceptors})
|
||||
: super(channel, options: options,
|
||||
interceptors: interceptors);
|
||||
|
||||
$grpc.ResponseFuture<$0.StatusResponse> status($0.StatusRequest request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$status, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseStream<$0.Utxo> utxos($0.UtxosRequest request, {$grpc.CallOptions? options}) {
|
||||
return $createStreamingCall(_$utxos, $async.Stream.fromIterable([request]), options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.AddressResponse> addresses($0.AddressRequest request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$addresses, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.SpentResponse> spent($0.SpentRequest request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$spent, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.CreateResponse> create($0.CreateRequest request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$create, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.BroadcastResponse> broadcast($0.BroadcastRequest request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$broadcast, request, options: options);
|
||||
}
|
||||
}
|
||||
|
||||
@$pb.GrpcServiceName('Rpc')
|
||||
abstract class RpcServiceBase extends $grpc.Service {
|
||||
$core.String get $name => 'Rpc';
|
||||
|
||||
RpcServiceBase() {
|
||||
$addMethod($grpc.ServiceMethod<$0.StatusRequest, $0.StatusResponse>(
|
||||
'Status',
|
||||
status_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.StatusRequest.fromBuffer(value),
|
||||
($0.StatusResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.UtxosRequest, $0.Utxo>(
|
||||
'Utxos',
|
||||
utxos_Pre,
|
||||
false,
|
||||
true,
|
||||
($core.List<$core.int> value) => $0.UtxosRequest.fromBuffer(value),
|
||||
($0.Utxo value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.AddressRequest, $0.AddressResponse>(
|
||||
'Addresses',
|
||||
addresses_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.AddressRequest.fromBuffer(value),
|
||||
($0.AddressResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.SpentRequest, $0.SpentResponse>(
|
||||
'Spent',
|
||||
spent_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.SpentRequest.fromBuffer(value),
|
||||
($0.SpentResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.CreateRequest, $0.CreateResponse>(
|
||||
'Create',
|
||||
create_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.CreateRequest.fromBuffer(value),
|
||||
($0.CreateResponse value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.BroadcastRequest, $0.BroadcastResponse>(
|
||||
'Broadcast',
|
||||
broadcast_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.BroadcastRequest.fromBuffer(value),
|
||||
($0.BroadcastResponse value) => value.writeToBuffer()));
|
||||
}
|
||||
|
||||
$async.Future<$0.StatusResponse> status_Pre($grpc.ServiceCall call, $async.Future<$0.StatusRequest> request) async {
|
||||
return status(call, await request);
|
||||
}
|
||||
|
||||
$async.Stream<$0.Utxo> utxos_Pre($grpc.ServiceCall call, $async.Future<$0.UtxosRequest> request) async* {
|
||||
yield* utxos(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.AddressResponse> addresses_Pre($grpc.ServiceCall call, $async.Future<$0.AddressRequest> request) async {
|
||||
return addresses(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.SpentResponse> spent_Pre($grpc.ServiceCall call, $async.Future<$0.SpentRequest> request) async {
|
||||
return spent(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.CreateResponse> create_Pre($grpc.ServiceCall call, $async.Future<$0.CreateRequest> request) async {
|
||||
return create(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.BroadcastResponse> broadcast_Pre($grpc.ServiceCall call, $async.Future<$0.BroadcastRequest> request) async {
|
||||
return broadcast(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.StatusResponse> status($grpc.ServiceCall call, $0.StatusRequest request);
|
||||
$async.Stream<$0.Utxo> utxos($grpc.ServiceCall call, $0.UtxosRequest request);
|
||||
$async.Future<$0.AddressResponse> addresses($grpc.ServiceCall call, $0.AddressRequest request);
|
||||
$async.Future<$0.SpentResponse> spent($grpc.ServiceCall call, $0.SpentRequest request);
|
||||
$async.Future<$0.CreateResponse> create($grpc.ServiceCall call, $0.CreateRequest request);
|
||||
$async.Future<$0.BroadcastResponse> broadcast($grpc.ServiceCall call, $0.BroadcastRequest request);
|
||||
}
|
19
cw_mweb/macos/Classes/CwMwebPlugin.swift
Normal file
19
cw_mweb/macos/Classes/CwMwebPlugin.swift
Normal file
|
@ -0,0 +1,19 @@
|
|||
import Cocoa
|
||||
import FlutterMacOS
|
||||
|
||||
public class CwMwebPlugin: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "cw_mweb", binaryMessenger: registrar.messenger)
|
||||
let instance = CwMwebPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "getPlatformVersion":
|
||||
result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString)
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
23
cw_mweb/macos/cw_mweb.podspec
Normal file
23
cw_mweb/macos/cw_mweb.podspec
Normal file
|
@ -0,0 +1,23 @@
|
|||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint cw_mweb.podspec` to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'cw_mweb'
|
||||
s.version = '0.0.1'
|
||||
s.summary = 'A new Flutter plugin project.'
|
||||
s.description = <<-DESC
|
||||
A new Flutter plugin project.
|
||||
DESC
|
||||
s.homepage = 'http://example.com'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'FlutterMacOS'
|
||||
|
||||
s.platform = :osx, '10.11'
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
||||
s.swift_version = '5.0'
|
||||
end
|
76
cw_mweb/pubspec.yaml
Normal file
76
cw_mweb/pubspec.yaml
Normal file
|
@ -0,0 +1,76 @@
|
|||
name: cw_mweb
|
||||
description: A new Flutter plugin project.
|
||||
version: 0.0.1
|
||||
homepage:
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.6 <4.0.0'
|
||||
flutter: ">=3.3.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
grpc: ^3.2.4
|
||||
path_provider: ^2.1.2
|
||||
plugin_platform_interface: ^2.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
# This section identifies this Flutter project as a plugin project.
|
||||
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
|
||||
# which should be registered in the plugin registry. This is required for
|
||||
# using method channels.
|
||||
# The Android 'package' specifies package in which the registered class is.
|
||||
# This is required for using method channels on Android.
|
||||
# The 'ffiPlugin' specifies that native code should be built and bundled.
|
||||
# This is required for using `dart:ffi`.
|
||||
# All these are used by the tooling to maintain consistency when
|
||||
# adding or updating assets for this project.
|
||||
plugin:
|
||||
platforms:
|
||||
android:
|
||||
package: com.cakewallet.mweb
|
||||
pluginClass: CwMwebPlugin
|
||||
ios:
|
||||
pluginClass: CwMwebPlugin
|
||||
macos:
|
||||
pluginClass: CwMwebPlugin
|
||||
|
||||
# To add assets to your plugin package, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
#
|
||||
# For details regarding assets in packages, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
#
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||
|
||||
# To add custom fonts to your plugin package, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts in packages, see
|
||||
# https://flutter.dev/custom-fonts/#from-packages
|
|
@ -83,6 +83,7 @@
|
|||
9F46EE5D2BC11178009318F5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
ABD6FCBB0F4244B090459128 /* BreezSDK.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BreezSDK.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/BreezSDK.swift"; sourceTree = "<group>"; };
|
||||
AD0937B0140D5A4C24E73BEA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
C58D93382C00FAC6004BCF69 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
|
||||
B3D5E78267F5F18D882FDC3B /* ServiceConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServiceConfig.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/ServiceConfig.swift"; sourceTree = "<group>"; };
|
||||
CE291CFD2C15DB9A00B9F709 /* WowneroWallet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = WowneroWallet.framework; sourceTree = "<group>"; };
|
||||
DCEA540E3586164FB47AD13E /* LnurlPayInvoice.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LnurlPayInvoice.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Task/LnurlPayInvoice.swift"; sourceTree = "<group>"; };
|
||||
|
@ -104,6 +105,7 @@
|
|||
06957875428D0F5AAE053765 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C58D93382C00FAC6004BCF69 /* libresolv.tbd */,
|
||||
0C9986A3251A932F00D566FD /* CryptoSwift.framework */,
|
||||
3C663361C56EBB242598F609 /* Pods_Runner.framework */,
|
||||
);
|
||||
|
|
|
@ -208,8 +208,8 @@ class CWBitcoin extends Bitcoin {
|
|||
}
|
||||
|
||||
WalletService createLitecoinWalletService(
|
||||
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
|
||||
return LitecoinWalletService(walletInfoSource, unspentCoinSource);
|
||||
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan) {
|
||||
return LitecoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -259,6 +259,8 @@ class CWBitcoin extends Bitcoin {
|
|||
return SegwitAddresType.p2tr;
|
||||
case BitcoinReceivePageOption.p2wsh:
|
||||
return SegwitAddresType.p2wsh;
|
||||
case BitcoinReceivePageOption.mweb:
|
||||
return SegwitAddresType.mweb;
|
||||
case BitcoinReceivePageOption.p2wpkh:
|
||||
default:
|
||||
return SegwitAddresType.p2wpkh;
|
||||
|
@ -580,4 +582,16 @@ class CWBitcoin extends Bitcoin {
|
|||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
await bitcoinWallet.updateFeeRates();
|
||||
}
|
||||
|
||||
@override
|
||||
void setMwebEnabled(Object wallet, bool enabled) {
|
||||
final litecoinWallet = wallet as LitecoinWallet;
|
||||
litecoinWallet.setMwebEnabled(enabled);
|
||||
}
|
||||
|
||||
@override
|
||||
bool getMwebEnabled(Object wallet) {
|
||||
final litecoinWallet = wallet as LitecoinWallet;
|
||||
return litecoinWallet.mwebEnabled;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@ class AddressValidator extends TextValidator {
|
|||
AddressValidator({required CryptoCurrency type})
|
||||
: super(
|
||||
errorMessage: S.current.error_text_address,
|
||||
useAdditionalValidation: type == CryptoCurrency.btc
|
||||
? (String txt) => validateAddress(address: txt, network: BitcoinNetwork.mainnet)
|
||||
useAdditionalValidation: type == CryptoCurrency.btc || type == CryptoCurrency.ltc
|
||||
? (String txt) => validateAddress(address: txt, network:
|
||||
type == CryptoCurrency.btc ? BitcoinNetwork.mainnet : LitecoinNetwork.mainnet)
|
||||
: null,
|
||||
pattern: getPattern(type),
|
||||
length: getLength(type));
|
||||
|
@ -27,6 +28,8 @@ class AddressValidator extends TextValidator {
|
|||
'|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$';
|
||||
case CryptoCurrency.btc:
|
||||
return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${SilentPaymentAddress.regex.pattern}\$';
|
||||
case CryptoCurrency.ltc:
|
||||
return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${MwebAddress.regex.pattern}\$';
|
||||
case CryptoCurrency.nano:
|
||||
return '[0-9a-zA-Z_]';
|
||||
case CryptoCurrency.banano:
|
||||
|
@ -97,8 +100,6 @@ class AddressValidator extends TextValidator {
|
|||
return '^(?!bitcoincash:)[0-9a-zA-Z]*\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{41}\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{42}\$|^bitcoincash:q|p[0-9a-zA-Z]{41}\$|^bitcoincash:q|p[0-9a-zA-Z]{42}\$';
|
||||
case CryptoCurrency.bnb:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.ltc:
|
||||
return '^(?!(ltc|LTC)1)[0-9a-zA-Z]*\$|(^LTC1[A-Z0-9]*\$)|(^ltc1[a-z0-9]*\$)';
|
||||
case CryptoCurrency.hbar:
|
||||
return '[0-9a-zA-Z.]';
|
||||
case CryptoCurrency.zaddr:
|
||||
|
@ -146,6 +147,8 @@ class AddressValidator extends TextValidator {
|
|||
return null;
|
||||
case CryptoCurrency.btc:
|
||||
return null;
|
||||
case CryptoCurrency.ltc:
|
||||
return null;
|
||||
case CryptoCurrency.dash:
|
||||
return [34];
|
||||
case CryptoCurrency.eos:
|
||||
|
@ -192,8 +195,6 @@ class AddressValidator extends TextValidator {
|
|||
return [42, 43, 44, 54, 55];
|
||||
case CryptoCurrency.bnb:
|
||||
return [42];
|
||||
case CryptoCurrency.ltc:
|
||||
return [34, 43, 63];
|
||||
case CryptoCurrency.nano:
|
||||
return [64, 65];
|
||||
case CryptoCurrency.banano:
|
||||
|
@ -284,7 +285,8 @@ class AddressValidator extends TextValidator {
|
|||
case CryptoCurrency.ltc:
|
||||
return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)ltc[a-zA-Z0-9]{26,45}([^0-9a-zA-Z]|\$)';
|
||||
'|([^0-9a-zA-Z]|^)ltc[a-zA-Z0-9]{26,45}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)((ltc|t)mweb1q[ac-hj-np-z02-9]{90,120})([^0-9a-zA-Z]|\$)';
|
||||
case CryptoCurrency.eth:
|
||||
return '0x[0-9a-zA-Z]{42}';
|
||||
case CryptoCurrency.maticpoly:
|
||||
|
|
17
lib/di.dart
17
lib/di.dart
|
@ -106,6 +106,7 @@ import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settin
|
|||
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/mweb_settings.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
|
||||
|
@ -154,6 +155,7 @@ import 'package:cake_wallet/view_model/send/output.dart';
|
|||
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
|
||||
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/mweb_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
|
||||
|
@ -690,7 +692,9 @@ Future<void> setup({
|
|||
|
||||
getIt.registerFactory<MoneroAccountListViewModel>(() {
|
||||
final wallet = getIt.get<AppStore>().wallet!;
|
||||
if (wallet.type == WalletType.monero || wallet.type == WalletType.wownero || wallet.type == WalletType.haven) {
|
||||
if (wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.wownero ||
|
||||
wallet.type == WalletType.haven) {
|
||||
return MoneroAccountListViewModel(wallet);
|
||||
}
|
||||
throw Exception(
|
||||
|
@ -750,6 +754,9 @@ Future<void> setup({
|
|||
getIt.registerFactory(() =>
|
||||
SilentPaymentsSettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => MwebSettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!));
|
||||
|
||||
getIt.registerFactory(() {
|
||||
return PrivacySettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!);
|
||||
});
|
||||
|
@ -814,6 +821,8 @@ Future<void> setup({
|
|||
getIt.registerFactory(
|
||||
() => SilentPaymentsSettingsPage(getIt.get<SilentPaymentsSettingsViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => MwebSettingsPage(getIt.get<MwebSettingsViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => OtherSettingsPage(getIt.get<OtherSettingsViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => NanoChangeRepPage(
|
||||
|
@ -907,7 +916,11 @@ Future<void> setup({
|
|||
getIt.get<SettingsStore>().silentPaymentsAlwaysScan,
|
||||
);
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||
return bitcoin!.createLitecoinWalletService(
|
||||
_walletInfoSource,
|
||||
_unspentCoinsInfoSource,
|
||||
getIt.get<SettingsStore>().mwebAlwaysScan,
|
||||
);
|
||||
case WalletType.ethereum:
|
||||
return ethereum!.createEthereumWalletService(_walletInfoSource);
|
||||
case WalletType.bitcoinCash:
|
||||
|
|
|
@ -49,6 +49,10 @@ class PreferencesKey {
|
|||
static const customBitcoinFeeRate = 'custom_electrum_fee_rate';
|
||||
static const silentPaymentsCardDisplay = 'silentPaymentsCardDisplay';
|
||||
static const silentPaymentsAlwaysScan = 'silentPaymentsAlwaysScan';
|
||||
static const mwebCardDisplay = 'mwebCardDisplay';
|
||||
static const mwebEnabled = 'mwebEnabled';
|
||||
static const hasEnabledMwebBefore = 'hasEnabledMwebBefore';
|
||||
static const mwebAlwaysScan = 'mwebAlwaysScan';
|
||||
static const shouldShowReceiveWarning = 'should_show_receive_warning';
|
||||
static const shouldShowYatPopup = 'should_show_yat_popup';
|
||||
static const shouldShowRepWarning = 'should_show_rep_warning';
|
||||
|
|
|
@ -29,6 +29,7 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
|||
import 'package:cw_core/address_info.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:cw_core/mweb_utxo.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
@ -167,6 +168,10 @@ Future<void> initializeAppConfigs() async {
|
|||
CakeHive.registerAdapter(AnonpayInvoiceInfoAdapter());
|
||||
}
|
||||
|
||||
if (!CakeHive.isAdapterRegistered(MwebUtxo.typeId)) {
|
||||
CakeHive.registerAdapter(MwebUtxoAdapter());
|
||||
}
|
||||
|
||||
final secureStorage = secureStorageShared;
|
||||
|
||||
final transactionDescriptionsBoxKey =
|
||||
|
@ -249,7 +254,6 @@ Future<void> initialSetup(
|
|||
navigatorKey: navigatorKey,
|
||||
);
|
||||
await bootstrap(navigatorKey);
|
||||
monero?.onStartup();
|
||||
}
|
||||
|
||||
class App extends StatefulWidget {
|
||||
|
|
|
@ -69,6 +69,7 @@ import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settin
|
|||
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/mweb_settings.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
|
||||
|
@ -369,6 +370,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
return CupertinoPageRoute<void>(
|
||||
fullscreenDialog: true, builder: (_) => getIt.get<SilentPaymentsSettingsPage>());
|
||||
|
||||
case Routes.mwebSettings:
|
||||
return CupertinoPageRoute<void>(
|
||||
fullscreenDialog: true, builder: (_) => getIt.get<MwebSettingsPage>());
|
||||
|
||||
case Routes.connectionSync:
|
||||
return CupertinoPageRoute<void>(
|
||||
fullscreenDialog: true, builder: (_) => getIt.get<ConnectionSyncPage>());
|
||||
|
|
|
@ -73,6 +73,7 @@ class Routes {
|
|||
static const cakePayAccountPage = '/cake_pay_account_page';
|
||||
static const webViewPage = '/web_view_page';
|
||||
static const silentPaymentsSettings = '/silent_payments_settings';
|
||||
static const mwebSettings = '/mweb_settings';
|
||||
static const connectionSync = '/connection_sync_page';
|
||||
static const securityBackupPage = '/security_and_backup_page';
|
||||
static const privacyPage = '/privacy_page';
|
||||
|
|
|
@ -225,7 +225,8 @@ class AddressPage extends BasePage {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
if (addressListViewModel.type == WalletType.bitcoin) {
|
||||
if (addressListViewModel.type == WalletType.bitcoin ||
|
||||
addressListViewModel.type == WalletType.litecoin) {
|
||||
addressListViewModel.setAddressType(bitcoin!.getBitcoinAddressType(option));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart';
|
|||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.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/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
|
||||
|
@ -24,6 +25,7 @@ import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class BalancePage extends StatelessWidget {
|
||||
|
@ -329,7 +331,73 @@ class CryptoBalanceWidget extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
if (dashboardViewModel.showMwebCard) ...[
|
||||
SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||
child: DashBoardRoundedCardWidget(
|
||||
customBorder: 30,
|
||||
title: S.current.litecoin_mweb,
|
||||
subTitle: S.current.litecoin_enable_mweb_sync,
|
||||
hint: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => launchUrl(
|
||||
Uri.parse(
|
||||
"https://guides.cakewallet.com/docs/cryptos/litecoin/#mweb"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
S.current.litecoin_what_is_mweb,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'Lato',
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context)
|
||||
.extension<BalancePageTheme>()!
|
||||
.labelTextColor,
|
||||
height: 1,
|
||||
),
|
||||
softWrap: true,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Icon(Icons.help_outline,
|
||||
size: 16,
|
||||
color: Theme.of(context)
|
||||
.extension<BalancePageTheme>()!
|
||||
.labelTextColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => StandardSwitch(
|
||||
value: dashboardViewModel.mwebScanningActive,
|
||||
onTaped: () => _toggleMweb(context),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () => _toggleMweb(context),
|
||||
icon: ImageIcon(
|
||||
AssetImage('assets/images/mweb_logo.png'),
|
||||
color:
|
||||
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
|
||||
size: 50,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}),
|
||||
|
@ -369,6 +437,22 @@ class CryptoBalanceWidget extends StatelessWidget {
|
|||
|
||||
return dashboardViewModel.setSilentPaymentsScanning(newValue);
|
||||
}
|
||||
|
||||
Future<void> _toggleMweb(BuildContext context) async {
|
||||
if (!dashboardViewModel.hasEnabledMwebBefore) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).warning,
|
||||
alertContent: S.current.litecoin_mweb_warning,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
));
|
||||
}
|
||||
dashboardViewModel.setMwebScanningActive(!dashboardViewModel.mwebScanningActive);
|
||||
}
|
||||
}
|
||||
|
||||
class BalanceRowWidget extends StatelessWidget {
|
||||
|
|
|
@ -83,10 +83,6 @@ class TransactionsPage extends StatelessWidget {
|
|||
}
|
||||
|
||||
final transaction = item.transaction;
|
||||
final transactionType = dashboardViewModel.type == WalletType.ethereum &&
|
||||
transaction.evmSignatureName == 'approval'
|
||||
? ' (${transaction.evmSignatureName})'
|
||||
: '';
|
||||
|
||||
return Observer(
|
||||
builder: (_) => TransactionRow(
|
||||
|
@ -101,7 +97,9 @@ class TransactionsPage extends StatelessWidget {
|
|||
: item.formattedFiatAmount,
|
||||
isPending: transaction.isPending,
|
||||
title: item.formattedTitle +
|
||||
item.formattedStatus + ' $transactionType',
|
||||
item.formattedStatus +
|
||||
' ${item.formattedType}',
|
||||
tag: item.tag,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -191,6 +191,11 @@ class MenuWidgetState extends State<MenuWidget> {
|
|||
return Container();
|
||||
}
|
||||
|
||||
if (!widget.dashboardViewModel.hasMweb &&
|
||||
item.name(context) == S.current.litecoin_mweb_settings) {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
final isLastTile = index == itemCount - 1;
|
||||
|
||||
return SettingActionButton(
|
||||
|
|
|
@ -5,14 +5,16 @@ import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
|
|||
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
|
||||
|
||||
class TransactionRow extends StatelessWidget {
|
||||
TransactionRow(
|
||||
{required this.direction,
|
||||
TransactionRow({
|
||||
required this.direction,
|
||||
required this.formattedDate,
|
||||
required this.formattedAmount,
|
||||
required this.formattedFiatAmount,
|
||||
required this.isPending,
|
||||
required this.title,
|
||||
required this.onTap});
|
||||
required this.onTap,
|
||||
required this.tag,
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
final TransactionDirection direction;
|
||||
|
@ -21,6 +23,7 @@ class TransactionRow extends StatelessWidget {
|
|||
final String formattedFiatAmount;
|
||||
final bool isPending;
|
||||
final String title;
|
||||
final String tag;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -38,10 +41,8 @@ class TransactionRow extends StatelessWidget {
|
|||
width: 36,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).extension<TransactionTradeTheme>()!.rowsColor
|
||||
),
|
||||
child: Image.asset(
|
||||
direction == TransactionDirection.incoming
|
||||
color: Theme.of(context).extension<TransactionTradeTheme>()!.rowsColor),
|
||||
child: Image.asset(direction == TransactionDirection.incoming
|
||||
? 'assets/images/down_arrow.png'
|
||||
: 'assets/images/up_arrow.png'),
|
||||
),
|
||||
|
@ -50,36 +51,59 @@ class TransactionRow extends StatelessWidget {
|
|||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(title,
|
||||
children: [
|
||||
if (tag.isNotEmpty)
|
||||
Container(
|
||||
height: 17,
|
||||
padding: EdgeInsets.only(left: 6, right: 6),
|
||||
margin: EdgeInsets.only(right: 6),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.5)),
|
||||
color: Colors.white),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
tag,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 7,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
formattedAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor)),
|
||||
Text(formattedAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor))
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
]),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
|
||||
Text(formattedDate,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.dateSectionRowColor)),
|
||||
color:
|
||||
Theme.of(context).extension<CakeTextTheme>()!.dateSectionRowColor)),
|
||||
Text(formattedFiatAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.dateSectionRowColor))
|
||||
color:
|
||||
Theme.of(context).extension<CakeTextTheme>()!.dateSectionRowColor))
|
||||
])
|
||||
],
|
||||
)
|
||||
)
|
||||
))
|
||||
],
|
||||
),
|
||||
));
|
||||
|
|
|
@ -33,6 +33,7 @@ class RescanPage extends BasePage {
|
|||
key: _blockchainHeightWidgetKey,
|
||||
onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value,
|
||||
isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan,
|
||||
isMwebScan: _rescanViewModel.isMwebScan,
|
||||
doSingleScan: _rescanViewModel.doSingleScan,
|
||||
toggleSingleScan: () =>
|
||||
_rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan,
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:cake_wallet/core/totp_request_details.dart';
|
|||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
|
@ -133,6 +134,10 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
setState(() => _setInactive(true));
|
||||
}
|
||||
|
||||
// if (widget.appStore.wallet?.type == WalletType.litecoin) {
|
||||
// widget.appStore.wallet?.stopSync();
|
||||
// }
|
||||
|
||||
break;
|
||||
case AppLifecycleState.resumed:
|
||||
widget.authService.requireAuth().then((value) {
|
||||
|
@ -142,6 +147,9 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
});
|
||||
}
|
||||
});
|
||||
if (widget.appStore.wallet?.type == WalletType.litecoin) {
|
||||
widget.appStore.wallet?.startSync();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -60,6 +60,11 @@ class _DesktopSettingsPageState extends State<DesktopSettingsPage> {
|
|||
return Container();
|
||||
}
|
||||
|
||||
if (!widget.dashboardViewModel.hasMweb &&
|
||||
item.name(context) == S.of(context).litecoin_mweb_settings) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
final isLastTile = index == itemCount - 1;
|
||||
return SettingActionButton(
|
||||
isLastTile: isLastTile,
|
||||
|
|
51
lib/src/screens/settings/mweb_settings.dart
Normal file
51
lib/src/screens/settings/mweb_settings.dart
Normal file
|
@ -0,0 +1,51 @@
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/view_model/settings/mweb_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/silent_payments_settings_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class MwebSettingsPage extends BasePage {
|
||||
MwebSettingsPage(this._mwebSettingsViewModel);
|
||||
|
||||
@override
|
||||
String get title => S.current.litecoin_mweb_settings;
|
||||
|
||||
final MwebSettingsViewModel _mwebSettingsViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Observer(builder: (_) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.litecoin_mweb_display_card,
|
||||
value: _mwebSettingsViewModel.mwebCardDisplay,
|
||||
onValueChange: (_, bool value) {
|
||||
_mwebSettingsViewModel.setMwebCardDisplay(value);
|
||||
},
|
||||
),
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.litecoin_mweb_always_scan,
|
||||
value: _mwebSettingsViewModel.mwebAlwaysScan,
|
||||
onValueChange: (_, bool value) {
|
||||
_mwebSettingsViewModel.setMwebAlwaysScan(value);
|
||||
},
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.litecoin_mweb_scanning,
|
||||
handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.rescan),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -103,6 +103,9 @@ class UnspentCoinsListItem extends StatelessWidget {
|
|||
),
|
||||
maxLines: 1,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
if (isChange)
|
||||
Container(
|
||||
height: 17,
|
||||
|
@ -120,6 +123,24 @@ class UnspentCoinsListItem extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (address.toLowerCase().contains("mweb"))
|
||||
Container(
|
||||
height: 17,
|
||||
padding: EdgeInsets.only(left: 6, right: 6),
|
||||
margin: EdgeInsets.only(left: 6),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.5)),
|
||||
color: Colors.white),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
"MWEB",
|
||||
style: TextStyle(
|
||||
color: itemColor,
|
||||
fontSize: 7,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isSilentPayment)
|
||||
Container(
|
||||
height: 17,
|
||||
|
@ -139,6 +160,8 @@ class UnspentCoinsListItem extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
])),
|
||||
],
|
||||
|
|
|
@ -18,6 +18,7 @@ class BlockchainHeightWidget extends StatefulWidget {
|
|||
this.onHeightOrDateEntered,
|
||||
this.hasDatePicker = true,
|
||||
this.isSilentPaymentsScan = false,
|
||||
this.isMwebScan = false,
|
||||
this.toggleSingleScan,
|
||||
this.doSingleScan = false,
|
||||
required this.walletType,
|
||||
|
@ -28,6 +29,7 @@ class BlockchainHeightWidget extends StatefulWidget {
|
|||
final FocusNode? focusNode;
|
||||
final bool hasDatePicker;
|
||||
final bool isSilentPaymentsScan;
|
||||
final bool isMwebScan;
|
||||
final bool doSingleScan;
|
||||
final Function()? toggleSingleScan;
|
||||
final WalletType walletType;
|
||||
|
@ -165,7 +167,10 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
|||
|
||||
if (date != null) {
|
||||
int height;
|
||||
if (widget.isSilentPaymentsScan) {
|
||||
if (widget.isMwebScan) {
|
||||
throw UnimplementedError();
|
||||
// height = bitcoin!.getMwebHeightByDate(date: date);
|
||||
} else if (widget.isSilentPaymentsScan) {
|
||||
height = bitcoin!.getHeightByDate(date: date);
|
||||
} else {
|
||||
if (widget.walletType == WalletType.monero) {
|
||||
|
|
|
@ -22,7 +22,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget {
|
|||
final String subTitle;
|
||||
final Widget? hint;
|
||||
final SvgPicture? svgPicture;
|
||||
final Icon? icon;
|
||||
final Widget? icon;
|
||||
final double? customBorder;
|
||||
|
||||
@override
|
||||
|
|
|
@ -18,6 +18,7 @@ class SettingActions {
|
|||
walletSettingAction,
|
||||
addressBookSettingAction,
|
||||
silentPaymentsSettingAction,
|
||||
litecoinMwebSettingAction,
|
||||
securityBackupSettingAction,
|
||||
privacySettingAction,
|
||||
displaySettingAction,
|
||||
|
@ -30,6 +31,7 @@ class SettingActions {
|
|||
walletSettingAction,
|
||||
addressBookSettingAction,
|
||||
silentPaymentsSettingAction,
|
||||
litecoinMwebSettingAction,
|
||||
securityBackupSettingAction,
|
||||
privacySettingAction,
|
||||
displaySettingAction,
|
||||
|
@ -46,6 +48,15 @@ class SettingActions {
|
|||
},
|
||||
);
|
||||
|
||||
static SettingActions litecoinMwebSettingAction = SettingActions._(
|
||||
name: (context) => S.current.litecoin_mweb_settings,
|
||||
image: 'assets/images/bitcoin_menu.png',
|
||||
onTap: (BuildContext context) {
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pushNamed(Routes.mwebSettings);
|
||||
},
|
||||
);
|
||||
|
||||
static SettingActions connectionSettingAction = SettingActions._(
|
||||
name: (context) => S.of(context).connection_sync,
|
||||
image: 'assets/images/nodes_menu.png',
|
||||
|
|
|
@ -111,6 +111,10 @@ abstract class SettingsStoreBase with Store {
|
|||
required this.customBitcoinFeeRate,
|
||||
required this.silentPaymentsCardDisplay,
|
||||
required this.silentPaymentsAlwaysScan,
|
||||
required this.mwebAlwaysScan,
|
||||
required this.mwebCardDisplay,
|
||||
required this.mwebEnabled,
|
||||
required this.hasEnabledMwebBefore,
|
||||
TransactionPriority? initialBitcoinTransactionPriority,
|
||||
TransactionPriority? initialMoneroTransactionPriority,
|
||||
TransactionPriority? initialWowneroTransactionPriority,
|
||||
|
@ -542,6 +546,24 @@ abstract class SettingsStoreBase with Store {
|
|||
(bool silentPaymentsAlwaysScan) => _sharedPreferences.setBool(
|
||||
PreferencesKey.silentPaymentsAlwaysScan, silentPaymentsAlwaysScan));
|
||||
|
||||
reaction(
|
||||
(_) => mwebAlwaysScan,
|
||||
(bool mwebAlwaysScan) =>
|
||||
_sharedPreferences.setBool(PreferencesKey.mwebAlwaysScan, mwebAlwaysScan));
|
||||
|
||||
reaction(
|
||||
(_) => mwebCardDisplay,
|
||||
(bool mwebCardDisplay) =>
|
||||
_sharedPreferences.setBool(PreferencesKey.mwebCardDisplay, mwebCardDisplay));
|
||||
|
||||
reaction((_) => mwebEnabled,
|
||||
(bool mwebEnabled) => _sharedPreferences.setBool(PreferencesKey.mwebEnabled, mwebEnabled));
|
||||
|
||||
reaction(
|
||||
(_) => hasEnabledMwebBefore,
|
||||
(bool hasEnabledMwebBefore) =>
|
||||
_sharedPreferences.setBool(PreferencesKey.hasEnabledMwebBefore, hasEnabledMwebBefore));
|
||||
|
||||
this.nodes.observe((change) {
|
||||
if (change.newValue != null && change.key != null) {
|
||||
_saveCurrentNode(change.newValue!, change.key!);
|
||||
|
@ -743,6 +765,18 @@ abstract class SettingsStoreBase with Store {
|
|||
@observable
|
||||
bool silentPaymentsAlwaysScan;
|
||||
|
||||
@observable
|
||||
bool mwebAlwaysScan;
|
||||
|
||||
@observable
|
||||
bool mwebCardDisplay;
|
||||
|
||||
@observable
|
||||
bool mwebEnabled;
|
||||
|
||||
@observable
|
||||
bool hasEnabledMwebBefore;
|
||||
|
||||
final SecureStorage _secureStorage;
|
||||
final SharedPreferences _sharedPreferences;
|
||||
final BackgroundTasks _backgroundTasks;
|
||||
|
@ -905,6 +939,11 @@ abstract class SettingsStoreBase with Store {
|
|||
sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true;
|
||||
final silentPaymentsAlwaysScan =
|
||||
sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false;
|
||||
final mwebAlwaysScan = sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false;
|
||||
final mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true;
|
||||
final mwebEnabled = sharedPreferences.getBool(PreferencesKey.hasEnabledMwebBefore) ?? false;
|
||||
final hasEnabledMwebBefore =
|
||||
sharedPreferences.getBool(PreferencesKey.hasEnabledMwebBefore) ?? false;
|
||||
|
||||
// If no value
|
||||
if (pinLength == null || pinLength == 0) {
|
||||
|
@ -1158,6 +1197,10 @@ abstract class SettingsStoreBase with Store {
|
|||
customBitcoinFeeRate: customBitcoinFeeRate,
|
||||
silentPaymentsCardDisplay: silentPaymentsCardDisplay,
|
||||
silentPaymentsAlwaysScan: silentPaymentsAlwaysScan,
|
||||
mwebAlwaysScan: mwebAlwaysScan,
|
||||
mwebCardDisplay: mwebCardDisplay,
|
||||
mwebEnabled: mwebEnabled,
|
||||
hasEnabledMwebBefore: hasEnabledMwebBefore,
|
||||
initialMoneroTransactionPriority: moneroTransactionPriority,
|
||||
initialWowneroTransactionPriority: wowneroTransactionPriority,
|
||||
initialLightningTransactionPriority: lightningTransactionPriority,
|
||||
|
@ -1315,6 +1358,10 @@ abstract class SettingsStoreBase with Store {
|
|||
sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true;
|
||||
silentPaymentsAlwaysScan =
|
||||
sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false;
|
||||
mwebAlwaysScan = sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false;
|
||||
mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true;
|
||||
mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false;
|
||||
hasEnabledMwebBefore = sharedPreferences.getBool(PreferencesKey.hasEnabledMwebBefore) ?? false;
|
||||
final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
||||
final bitcoinElectrumServerId =
|
||||
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
|
||||
|
|
|
@ -81,7 +81,7 @@ class ExceptionHandler {
|
|||
}
|
||||
|
||||
static void onError(FlutterErrorDetails errorDetails) async {
|
||||
if (kDebugMode) {
|
||||
if (kDebugMode || kProfileMode) {
|
||||
FlutterError.presentError(errorDetails);
|
||||
debugPrint(errorDetails.toString());
|
||||
return;
|
||||
|
|
|
@ -187,7 +187,8 @@ abstract class DashboardViewModelBase with Store {
|
|||
|
||||
final _accountTransactions = _wallet.transactionHistory.transactions.values
|
||||
.where((tx) =>
|
||||
wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id)
|
||||
wow.wownero!.getTransactionInfoAccountId(tx) ==
|
||||
wow.wownero!.getCurrentAccount(wallet).id)
|
||||
.toList();
|
||||
|
||||
final sortedTransactions = [..._accountTransactions];
|
||||
|
@ -271,6 +272,10 @@ abstract class DashboardViewModelBase with Store {
|
|||
silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet);
|
||||
});
|
||||
}
|
||||
|
||||
if (hasMweb) {
|
||||
mwebScanningActive = bitcoin!.getMwebEnabled(wallet);
|
||||
}
|
||||
}
|
||||
|
||||
@observable
|
||||
|
@ -364,6 +369,7 @@ abstract class DashboardViewModelBase with Store {
|
|||
bool get hasRescan =>
|
||||
wallet.type == WalletType.bitcoin ||
|
||||
wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.litecoin ||
|
||||
wallet.type == WalletType.wownero ||
|
||||
wallet.type == WalletType.haven;
|
||||
|
||||
|
@ -388,6 +394,32 @@ abstract class DashboardViewModelBase with Store {
|
|||
}
|
||||
}
|
||||
|
||||
@computed
|
||||
bool get hasMweb => wallet.type == WalletType.litecoin;
|
||||
|
||||
@computed
|
||||
bool get showMwebCard => hasMweb && settingsStore.mwebCardDisplay;
|
||||
|
||||
@observable
|
||||
bool mwebScanningActive = false;
|
||||
|
||||
@computed
|
||||
bool get hasEnabledMwebBefore => settingsStore.hasEnabledMwebBefore;
|
||||
|
||||
@action
|
||||
void setMwebScanningActive(bool active) {
|
||||
if (!hasMweb) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
settingsStore.hasEnabledMwebBefore = true;
|
||||
}
|
||||
|
||||
mwebScanningActive = active;
|
||||
bitcoin!.setMwebEnabled(wallet, active);
|
||||
}
|
||||
|
||||
BalanceViewModel balanceViewModel;
|
||||
|
||||
AppStore appStore;
|
||||
|
@ -591,7 +623,8 @@ abstract class DashboardViewModelBase with Store {
|
|||
}
|
||||
|
||||
if (wallet.type == WalletType.wownero) {
|
||||
return wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id;
|
||||
return wow.wownero!.getTransactionInfoAccountId(tx) ==
|
||||
wow.wownero!.getCurrentAccount(wallet).id;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -616,8 +649,8 @@ abstract class DashboardViewModelBase with Store {
|
|||
.getTransactionHistory(wallet)
|
||||
.transactions
|
||||
.values
|
||||
.where(
|
||||
(tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
|
||||
.where((tx) =>
|
||||
monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
|
||||
.toList();
|
||||
|
||||
transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem(
|
||||
|
@ -629,8 +662,9 @@ abstract class DashboardViewModelBase with Store {
|
|||
.getTransactionHistory(wallet)
|
||||
.transactions
|
||||
.values
|
||||
.where(
|
||||
(tx) => wow.wownero!.getTransactionInfoAccountId(tx) == wow.wownero!.getCurrentAccount(wallet).id)
|
||||
.where((tx) =>
|
||||
wow.wownero!.getTransactionInfoAccountId(tx) ==
|
||||
wow.wownero!.getCurrentAccount(wallet).id)
|
||||
.toList();
|
||||
|
||||
transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/lightning/lightning.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_receive_page_option.dart';
|
||||
import 'package:cw_core/receive_page_option.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
@ -12,25 +13,31 @@ class ReceiveOptionViewModel = ReceiveOptionViewModelBase with _$ReceiveOptionVi
|
|||
abstract class ReceiveOptionViewModelBase with Store {
|
||||
ReceiveOptionViewModelBase(this._wallet, this.initialPageOption)
|
||||
: selectedReceiveOption = initialPageOption ??
|
||||
(_wallet.type == WalletType.bitcoin
|
||||
(_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin
|
||||
? bitcoin!.getSelectedAddressType(_wallet)
|
||||
: ReceivePageOption.mainnet),
|
||||
_options = [] {
|
||||
final walletType = _wallet.type;
|
||||
|
||||
switch (walletType) {
|
||||
case WalletType.haven:
|
||||
_options = [ReceivePageOption.mainnet];
|
||||
break;
|
||||
case WalletType.lightning:
|
||||
_options = [...lightning!.getLightningReceivePageOptions()];
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
_options = [
|
||||
...bitcoin!.getBitcoinReceivePageOptions(),
|
||||
...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet)
|
||||
];
|
||||
break;
|
||||
case WalletType.lightning:
|
||||
_options = [...lightning!.getLightningReceivePageOptions()];
|
||||
break;
|
||||
|
||||
case WalletType.litecoin:
|
||||
_options = [
|
||||
...BitcoinReceivePageOption.allLitecoin,
|
||||
...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet)
|
||||
];
|
||||
break;
|
||||
case WalletType.haven:
|
||||
_options = [ReceivePageOption.mainnet];
|
||||
break;
|
||||
default:
|
||||
_options = [
|
||||
ReceivePageOption.mainnet,
|
||||
|
|
|
@ -57,7 +57,8 @@ class TransactionListItem extends ActionListItem with Keyable {
|
|||
}
|
||||
|
||||
String get formattedPendingStatus {
|
||||
if (balanceViewModel.wallet.type == WalletType.monero || balanceViewModel.wallet.type == WalletType.haven) {
|
||||
if (balanceViewModel.wallet.type == WalletType.monero ||
|
||||
balanceViewModel.wallet.type == WalletType.haven) {
|
||||
if (transaction.confirmations >= 0 && transaction.confirmations < 10) {
|
||||
return ' (${transaction.confirmations}/10)';
|
||||
}
|
||||
|
@ -80,6 +81,25 @@ class TransactionListItem extends ActionListItem with Keyable {
|
|||
return transaction.isPending ? S.current.pending : '';
|
||||
}
|
||||
|
||||
String get formattedType {
|
||||
if (transaction.evmSignatureName == 'approval') {
|
||||
return ' (${transaction.evmSignatureName})';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
String get tag {
|
||||
List<String> addresses =
|
||||
(transaction.inputAddresses ?? []) + (transaction.outputAddresses ?? []);
|
||||
for (var address in addresses) {
|
||||
if (address.toLowerCase().contains('mweb')) {
|
||||
return 'MWEB';
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
CryptoCurrency? get assetOfTransaction {
|
||||
try {
|
||||
if (balanceViewModel.wallet.type == WalletType.ethereum) {
|
||||
|
|
|
@ -29,6 +29,9 @@ abstract class RescanViewModelBase with Store {
|
|||
@computed
|
||||
bool get isSilentPaymentsScan => wallet.type == WalletType.bitcoin;
|
||||
|
||||
@computed
|
||||
bool get isMwebScan => wallet.type == WalletType.litecoin;
|
||||
|
||||
@action
|
||||
Future<void> rescanCurrentWallet({required int restoreHeight}) async {
|
||||
state = RescanWalletState.rescaning;
|
||||
|
|
32
lib/view_model/settings/mweb_settings_view_model.dart
Normal file
32
lib/view_model/settings/mweb_settings_view_model.dart
Normal file
|
@ -0,0 +1,32 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'mweb_settings_view_model.g.dart';
|
||||
|
||||
class MwebSettingsViewModel = MwebSettingsViewModelBase with _$MwebSettingsViewModel;
|
||||
|
||||
abstract class MwebSettingsViewModelBase with Store {
|
||||
MwebSettingsViewModelBase(this._settingsStore, this._wallet);
|
||||
|
||||
final SettingsStore _settingsStore;
|
||||
final WalletBase _wallet;
|
||||
|
||||
@computed
|
||||
bool get mwebCardDisplay => _settingsStore.mwebCardDisplay;
|
||||
|
||||
@computed
|
||||
bool get mwebAlwaysScan => _settingsStore.mwebAlwaysScan;
|
||||
|
||||
@action
|
||||
void setMwebCardDisplay(bool value) {
|
||||
_settingsStore.mwebCardDisplay = value;
|
||||
}
|
||||
|
||||
@action
|
||||
void setMwebAlwaysScan(bool value) {
|
||||
_settingsStore.mwebAlwaysScan = value;
|
||||
bitcoin!.setMwebEnabled(_wallet, value);
|
||||
}
|
||||
}
|
|
@ -85,19 +85,26 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
|||
}
|
||||
|
||||
List<Unspent> _getUnspents() {
|
||||
if (wallet.type == WalletType.monero) return monero!.getUnspents(wallet);
|
||||
if (wallet.type == WalletType.wownero) return wownero!.getUnspents(wallet);
|
||||
if ([WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type))
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
return monero!.getUnspents(wallet);
|
||||
case WalletType.wownero:
|
||||
return wownero!.getUnspents(wallet);
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
return bitcoin!.getUnspents(wallet);
|
||||
default:
|
||||
return List.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void _updateUnspentCoinsInfo() {
|
||||
_items.clear();
|
||||
|
||||
List<UnspentCoinsItem> unspents = [];
|
||||
_getUnspents().forEach((elem) {
|
||||
_getUnspents().forEach((Unspent elem) {
|
||||
try {
|
||||
final info =
|
||||
getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage);
|
||||
|
|
|
@ -524,7 +524,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
|
||||
@action
|
||||
Future<void> setAddressType(dynamic option) async {
|
||||
if (wallet.type == WalletType.bitcoin) {
|
||||
if (wallet.type == WalletType.bitcoin ||
|
||||
wallet.type == WalletType.litecoin) {
|
||||
await bitcoin!.setAddressType(wallet, option);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import FlutterMacOS
|
|||
import Foundation
|
||||
|
||||
import connectivity_plus
|
||||
import cw_mweb
|
||||
import device_info_plus
|
||||
import devicelocale
|
||||
import flutter_inappwebview_macos
|
||||
|
@ -22,6 +23,7 @@ import wakelock_plus
|
|||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||
CwMwebPlugin.register(with: registry.registrar(forPlugin: "CwMwebPlugin"))
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
|
||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||
|
|
|
@ -11,4 +11,5 @@ cd cw_tron; flutter pub get; flutter packages pub run build_runner build --delet
|
|||
cd cw_wownero; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
|
||||
cd cw_polygon; flutter pub get; cd ..
|
||||
cd cw_ethereum; flutter pub get; cd ..
|
||||
cd cw_mweb && flutter pub get && cd ..
|
||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||
|
|
|
@ -104,7 +104,7 @@ dependencies:
|
|||
bitcoin_base:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_base
|
||||
ref: cake-update-v3
|
||||
ref: cake-mweb
|
||||
ledger_flutter: ^1.0.1
|
||||
hashlib: 1.12.0
|
||||
|
||||
|
@ -146,6 +146,11 @@ dependency_overrides:
|
|||
url: https://github.com/cake-tech/web3dart.git
|
||||
ref: cake
|
||||
flutter_secure_storage_platform_interface: 1.0.2
|
||||
protobuf: ^3.1.0
|
||||
bitcoin_base:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_base
|
||||
ref: cake-mweb
|
||||
|
||||
flutter_icons:
|
||||
image_path: "assets/images/app_logo.png"
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "أرسل أكثر من ${min} SATs وحتى ${max} SATs إلى هذا العنوان. سيتم تطبيق رسوم إعداد ${feePercent} ٪ مع ما لا يقل عن ${fee} SATs عند تلقي هذه الفاتورة. سيؤدي ذلك إلى تحويل أي Bitcoin المستلم إلى Lightning. سيتم تطبيق رسوم على السلسلة. لا يمكن استخدام هذا العنوان إلا مرة واحدة.",
|
||||
"lightning_received_sats": "تلقيت ${num} sats",
|
||||
"lightning_swap_out_error": "عكس المبادلة كحد أقصى <دقيقة ، مقايضة عكسية (مبادلة) غير ممكن",
|
||||
"litecoin_enable_mweb_sync": "تمكين MWEB المسح الضوئي",
|
||||
"litecoin_mweb": "mweb",
|
||||
"litecoin_mweb_always_scan": "اضبط MWEB دائمًا على المسح الضوئي",
|
||||
"litecoin_mweb_display_card": "عرض بطاقة mweb",
|
||||
"litecoin_mweb_scanning": "MWEB المسح الضوئي",
|
||||
"litecoin_mweb_settings": "إعدادات MWEB",
|
||||
"litecoin_mweb_warning": "سيقوم استخدام MWEB في البداية بتنزيل ~ 600 ميجابايت من البيانات ، وقد يستغرق ما يصل إلى 30 دقيقة حسب سرعة الشبكة. سيتم تنزيل هذه البيانات الأولية مرة واحدة فقط وستكون متاحة لجميع محافظ Litecoin",
|
||||
"litecoin_what_is_mweb": "ما هو MWEB؟",
|
||||
"load_more": "تحميل المزيد",
|
||||
"loading_your_wallet": "يتم تحميل محفظتك",
|
||||
"login": "تسجيل الدخول",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Изпратете повече от ${min} SATs и до ${max} SATS на този адрес. Такса за настройка от ${feePercent}% с минимум ${fee} SATS ще бъде приложена при получаване на тази фактура. Това ще преобразува всеки получен биткойн в мълния. Ще бъде приложена такса на веригата. Този адрес може да се използва само веднъж.",
|
||||
"lightning_received_sats": "Получи ${num} sats",
|
||||
"lightning_swap_out_error": "Обратна суап макс <min, обратен суап (размяна) не е възможен",
|
||||
"litecoin_enable_mweb_sync": "Активирайте сканирането на MWeb",
|
||||
"litecoin_mweb": "Mweb",
|
||||
"litecoin_mweb_always_scan": "Задайте MWeb винаги сканиране",
|
||||
"litecoin_mweb_display_card": "Показване на MWEB карта",
|
||||
"litecoin_mweb_scanning": "Сканиране на MWEB",
|
||||
"litecoin_mweb_settings": "Настройки на MWEB",
|
||||
"litecoin_mweb_warning": "Използването на MWEB първоначално ще изтегли ~ 600MB данни и може да отнеме до 30 минути в зависимост от скоростта на мрежата. Тези първоначални данни ще изтеглят само веднъж и ще бъдат достъпни за всички портфейли Litecoin",
|
||||
"litecoin_what_is_mweb": "Какво е MWEB?",
|
||||
"load_more": "Зареди още",
|
||||
"loading_your_wallet": "Зареждане на портфейл",
|
||||
"login": "Влизане",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Pošlete více než ${min} sats a až do ${max} sats na tuto adresu. Po obdržení této faktury se použije poplatek za nastavení ${feePercent}% s minimem ${fee} SATS. Tím se převedou jakýkoli přijatý bitcoin na blesk. Bude použito poplatek za řetěz. Tuto adresu lze použít pouze jednou.",
|
||||
"lightning_received_sats": "Obdržel ${num} sats",
|
||||
"lightning_swap_out_error": "Reverzní swap max <min, reverzní swap (swap ven) není možný",
|
||||
"litecoin_enable_mweb_sync": "Povolit skenování MWeb",
|
||||
"litecoin_mweb": "MWeb",
|
||||
"litecoin_mweb_always_scan": "Nastavit MWeb vždy skenování",
|
||||
"litecoin_mweb_display_card": "Zobrazit kartu MWeb",
|
||||
"litecoin_mweb_scanning": "Skenování mWeb",
|
||||
"litecoin_mweb_settings": "Nastavení mWeb",
|
||||
"litecoin_mweb_warning": "Pomocí MWeb zpočátku stahuje ~ 600 MB dat a může trvat až 30 minut v závislosti na rychlosti sítě. Tato počáteční data si stáhnou pouze jednou a budou k dispozici pro všechny litecoinové peněženky",
|
||||
"litecoin_what_is_mweb": "Co je Mweb?",
|
||||
"load_more": "Načíst další",
|
||||
"loading_your_wallet": "Načítám peněženku",
|
||||
"login": "Login",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Senden Sie mehr als ${min} SATs und bis zu ${max} SATs an diese Adresse. Eine Einrichtungsgebühr von ${feePercent}% mit mindestens ${fee} SATs wird nach Erhalt dieser Rechnung angewendet. Dadurch werden alle empfangenen Bitcoin in Blitz umgewandelt. Es wird eine Gebühr für Ketten angewendet. Diese Adresse kann nur einmal verwendet werden.",
|
||||
"lightning_received_sats": "Erhalten ${num} sats",
|
||||
"lightning_swap_out_error": "Reverse -Swap Max <min, ein Rückwärtsausschub (Ausstausch) ist nicht möglich",
|
||||
"litecoin_enable_mweb_sync": "Aktivieren Sie das MWEB -Scannen",
|
||||
"litecoin_mweb": "MWeb",
|
||||
"litecoin_mweb_always_scan": "Setzen Sie MWeb immer scannen",
|
||||
"litecoin_mweb_display_card": "MWEB -Karte anzeigen",
|
||||
"litecoin_mweb_scanning": "MWEB Scanning",
|
||||
"litecoin_mweb_settings": "MWEB -Einstellungen",
|
||||
"litecoin_mweb_warning": "Durch die Verwendung von MWEB wird zunächst ~ 600 MB Daten heruntergeladen und kann je nach Netzwerkgeschwindigkeit bis zu 30 Minuten dauern. Diese ersten Daten werden nur einmal heruntergeladen und für alle Litecoin -Brieftaschen verfügbar",
|
||||
"litecoin_what_is_mweb": "Was ist MWeb?",
|
||||
"load_more": "Mehr laden",
|
||||
"loading_your_wallet": "Wallet wird geladen",
|
||||
"login": "Einloggen",
|
||||
|
@ -461,8 +469,8 @@
|
|||
"placeholder_transactions": "Ihre Transaktionen werden hier angezeigt",
|
||||
"please_fill_totp": "Bitte geben Sie den 8-stelligen Code ein, der auf Ihrem anderen Gerät vorhanden ist",
|
||||
"please_make_selection": "Bitte treffen Sie unten eine Auswahl zum Erstellen oder Wiederherstellen Ihrer Wallet.",
|
||||
"Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.",
|
||||
"please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.",
|
||||
"Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.",
|
||||
"please_select": "Bitte auswählen:",
|
||||
"please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.",
|
||||
"please_try_to_connect_to_another_node": "Bitte versuchen Sie, sich mit einem anderen Knoten zu verbinden",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Send more than ${min} sats and up to ${max} sats to this address. A setup fee of ${feePercent}% with a minimum of ${fee} sats will be applied upon receiving this invoice. This will convert any received Bitcoin into Lightning. An on-chain fee will be applied. This address can only be used once.",
|
||||
"lightning_received_sats": "Received ${num} sats",
|
||||
"lightning_swap_out_error": "Reverse swap max < min, a reverse swap (swap out) is not possible",
|
||||
"litecoin_enable_mweb_sync": "Enable MWEB scanning",
|
||||
"litecoin_mweb": "MWEB",
|
||||
"litecoin_mweb_always_scan": "Set MWEB always scanning",
|
||||
"litecoin_mweb_display_card": "Show MWEB card",
|
||||
"litecoin_mweb_scanning": "MWEB Scanning",
|
||||
"litecoin_mweb_settings": "MWEB settings",
|
||||
"litecoin_mweb_warning": "Using MWEB will initially download ~600MB of data, and may take up to 30 minutes depending on network speed. This initial data will only download once and be available for all Litecoin wallets",
|
||||
"litecoin_what_is_mweb": "What is MWEB?",
|
||||
"load_more": "Load more",
|
||||
"loading_your_wallet": "Loading your wallet",
|
||||
"login": "Login",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Envíe más de ${min} SATS y hasta ${max} SATS a esta dirección. Se aplicará una tarifa de configuración de ${feePercent}% con un mínimo de ${fee} SATS al recibir esta factura. Esto convertirá cualquier bitcoin recibido en rayos. Se aplicará una tarifa en la cadena. Esta dirección solo se puede usar una vez.",
|
||||
"lightning_received_sats": "Recibió ${num} sats",
|
||||
"lightning_swap_out_error": "Inverso de intercambio max <min, un intercambio inverso (intercambio) no es posible",
|
||||
"litecoin_enable_mweb_sync": "Habilitar el escaneo mweb",
|
||||
"litecoin_mweb": "Mweb",
|
||||
"litecoin_mweb_always_scan": "Establecer mweb siempre escaneo",
|
||||
"litecoin_mweb_display_card": "Mostrar tarjeta MWEB",
|
||||
"litecoin_mweb_scanning": "Escaneo mweb",
|
||||
"litecoin_mweb_settings": "Configuración de MWEB",
|
||||
"litecoin_mweb_warning": "El uso de MWEB inicialmente descargará ~ 600 MB de datos, y puede tomar hasta 30 minutos según la velocidad de la red. Estos datos iniciales solo se descargarán una vez y estarán disponibles para todas las billeteras de Litecoin",
|
||||
"litecoin_what_is_mweb": "¿Qué es mweb?",
|
||||
"load_more": "Carga más",
|
||||
"loading_your_wallet": "Cargando tu billetera",
|
||||
"login": "Iniciar sesión",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Envoyez plus que ${min} SATS et jusqu'à ${max} SATS à cette adresse. Des frais de configuration de ${feePercent}% avec un minimum de ${fee} SATS seront appliqués sur la réception de cette facture. Cela convertira tout bitcoin reçu en foudre. Des frais de chaîne seront appliqués. Cette adresse ne peut être utilisée qu'une seule fois.",
|
||||
"lightning_received_sats": "Reçu ${num} SATS",
|
||||
"lightning_swap_out_error": "Échange inversé max <min, un échange inversé (échange) n'est pas possible",
|
||||
"litecoin_enable_mweb_sync": "Activer la numérisation MWEB",
|
||||
"litecoin_mweb": "Mweb",
|
||||
"litecoin_mweb_always_scan": "Définir MWEB Score Scanning",
|
||||
"litecoin_mweb_display_card": "Afficher la carte MWeb",
|
||||
"litecoin_mweb_scanning": "Scann mweb",
|
||||
"litecoin_mweb_settings": "Paramètres MWEB",
|
||||
"litecoin_mweb_warning": "L'utilisation de MWEB téléchargera initialement ~ 600 Mo de données et peut prendre jusqu'à 30 minutes en fonction de la vitesse du réseau. Ces données initiales ne téléchargeront qu'une seule fois et seront disponibles pour tous les portefeuilles litecoin",
|
||||
"litecoin_what_is_mweb": "Qu'est-ce que MWEB?",
|
||||
"load_more": "Charger plus",
|
||||
"loading_your_wallet": "Chargement de votre portefeuille (wallet)",
|
||||
"login": "Utilisateur",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Aika sama da ${min} Haske kuma har zuwa ${max} yana cikin wannan adireshin. Kudin saiti na ${feePercent}% tare da mafi karancin ${fee} Ana amfani da shi a kan karbar wannan daftari. Wannan zai sauya wani karuwa da aka samu cikin walƙiya. Za a yi amfani da kudin kan sarkar. Wannan adireshin za a iya amfani da wannan adireshin sau ɗaya kawai.",
|
||||
"lightning_received_sats": "Samu ${num}",
|
||||
"lightning_swap_out_error": "Juyawa swap fi <min, juyawa swap (canzawa) ba zai yiwu ba",
|
||||
"litecoin_enable_mweb_sync": "Kunna binciken Mweb",
|
||||
"litecoin_mweb": "Mweb",
|
||||
"litecoin_mweb_always_scan": "Saita Mweb koyaushe",
|
||||
"litecoin_mweb_display_card": "Nuna katin Mweb",
|
||||
"litecoin_mweb_scanning": "Mweb scanning",
|
||||
"litecoin_mweb_settings": "Saitunan Mweb",
|
||||
"litecoin_mweb_warning": "Amfani da Mweb zai fara saukewa ~ 600MB na bayanai, kuma yana iya ɗaukar minti 30 dangane da saurin cibiyar sadarwa. Wannan bayanan farko zai saika saukarwa sau ɗaya kawai kuma a samu don duk wuraren shakatawa",
|
||||
"litecoin_what_is_mweb": "Menene Mweb?",
|
||||
"load_more": "Like more",
|
||||
"loading_your_wallet": "Ana loda walat ɗin ku",
|
||||
"login": "Shiga",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "इस पते पर ${min} से अधिक और ${max} से अधिक सीटें भेजें। यह चालान प्राप्त होने पर न्यूनतम ${fee} सैट के साथ ${feePercent}% का सेटअप शुल्क लागू किया जाएगा। यह किसी भी प्राप्त बिटकॉइन को लाइटनिंग में बदल देगा। ऑन-चेन शुल्क लागू किया जाएगा. इस पते का उपयोग केवल एक बार किया जा सकता है.",
|
||||
"lightning_received_sats": "${num} sats प्राप्त किया",
|
||||
"lightning_swap_out_error": "रिवर्स स्वैप मैक्स <मिनट, एक रिवर्स स्वैप (स्वैप आउट) संभव नहीं है",
|
||||
"litecoin_enable_mweb_sync": "MWEB स्कैनिंग सक्षम करें",
|
||||
"litecoin_mweb": "मावली",
|
||||
"litecoin_mweb_always_scan": "MWEB हमेशा स्कैनिंग सेट करें",
|
||||
"litecoin_mweb_display_card": "MWEB कार्ड दिखाएं",
|
||||
"litecoin_mweb_scanning": "MWEB स्कैनिंग",
|
||||
"litecoin_mweb_settings": "MWEB सेटिंग्स",
|
||||
"litecoin_mweb_warning": "MWEB का उपयोग शुरू में ~ 600MB डेटा डाउनलोड करेगा, और नेटवर्क की गति के आधार पर 30 मिनट तक का समय लग सकता है। यह प्रारंभिक डेटा केवल एक बार डाउनलोड करेगा और सभी लिटकोइन वॉलेट के लिए उपलब्ध होगा",
|
||||
"litecoin_what_is_mweb": "MWEB क्या है?",
|
||||
"load_more": "और लोड करें",
|
||||
"loading_your_wallet": "अपना बटुआ लोड कर रहा है",
|
||||
"login": "लॉग इन करें",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Na ovu adresu pošaljite više od ${min} SAT -a i do ${max} SAT -a. Naknada za postavljanje ${feePercent}% s najmanje ${fee} SAT -om primijenit će se nakon primanja ove fakture. To će pretvoriti bilo koji primljeni bitcoin u munje. Primjenjivat će se naknada na lancu. Ova se adresa može koristiti samo jednom.",
|
||||
"lightning_received_sats": "Primljeni ${num} sats",
|
||||
"lightning_swap_out_error": "Obrnuto zamijenite max <min, obrnuta zamjena (zamjena) nije moguća",
|
||||
"litecoin_enable_mweb_sync": "Omogućite MWEB skeniranje",
|
||||
"litecoin_mweb": "MWeb",
|
||||
"litecoin_mweb_always_scan": "Postavite MWeb uvijek skeniranje",
|
||||
"litecoin_mweb_display_card": "Prikaži MWeb karticu",
|
||||
"litecoin_mweb_scanning": "MWEB skeniranje",
|
||||
"litecoin_mweb_settings": "Postavke MWEB -a",
|
||||
"litecoin_mweb_warning": "Korištenje MWEB -a u početku će preuzeti ~ 600MB podataka, a može potrajati do 30 minuta, ovisno o brzini mreže. Ovi početni podaci preuzet će samo jednom i biti dostupni za sve Litecoin novčanike",
|
||||
"litecoin_what_is_mweb": "Što je MWEB?",
|
||||
"load_more": "Učitaj više",
|
||||
"loading_your_wallet": "Novčanik se učitava",
|
||||
"login": "Prijava",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Kirim lebih dari ${min} sat dan hingga ${max} SAT ke alamat ini. Biaya pengaturan ${feePercent}% dengan minimum ${fee} SAT akan diterapkan setelah menerima faktur ini. Ini akan mengubah bitcoin yang diterima menjadi kilat. Biaya rantai akan diterapkan. Alamat ini hanya dapat digunakan sekali.",
|
||||
"lightning_received_sats": "Menerima ${num} sats",
|
||||
"lightning_swap_out_error": "Reverse Swap Max <min, swap terbalik (swap out) tidak mungkin",
|
||||
"litecoin_enable_mweb_sync": "Aktifkan pemindaian MWEB",
|
||||
"litecoin_mweb": "Mweb",
|
||||
"litecoin_mweb_always_scan": "Atur mWeb selalu memindai",
|
||||
"litecoin_mweb_display_card": "Tunjukkan kartu mWeb",
|
||||
"litecoin_mweb_scanning": "Pemindaian MWEB",
|
||||
"litecoin_mweb_settings": "Pengaturan MWEB",
|
||||
"litecoin_mweb_warning": "Menggunakan MWEB pada awalnya akan mengunduh ~ 600MB data, dan dapat memakan waktu hingga 30 menit tergantung pada kecepatan jaringan. Data awal ini hanya akan mengunduh sekali dan tersedia untuk semua dompet litecoin",
|
||||
"litecoin_what_is_mweb": "Apa itu MWEB?",
|
||||
"load_more": "Muat lebih banyak",
|
||||
"loading_your_wallet": "Memuat dompet Anda",
|
||||
"login": "Masuk",
|
||||
|
|
|
@ -360,6 +360,14 @@
|
|||
"lightning_receive_limits": "Invia più di ${min} SAT e fino a ${max} SAT a questo indirizzo. Una commissione di configurazione di ${feePercent}% con un minimo di ${fee} SAT sarà applicata al momento della ricezione di questa fattura. Ciò convertirà qualsiasi bitcoin ricevuto in fulmini. Verrà applicata una tassa sulla catena. Questo indirizzo può essere usato solo una volta.",
|
||||
"lightning_received_sats": "Ricevuto ${num} sats",
|
||||
"lightning_swap_out_error": "Swap inverso Max <min, uno swap inverso (swap out) non è possibile",
|
||||
"litecoin_enable_mweb_sync": "Abilita la scansione MWeb",
|
||||
"litecoin_mweb": "MWeb",
|
||||
"litecoin_mweb_always_scan": "Imposta MWeb per scansionare sempre",
|
||||
"litecoin_mweb_display_card": "Mostra la scheda MWeb",
|
||||
"litecoin_mweb_scanning": "Scansione MWeb",
|
||||
"litecoin_mweb_settings": "Impostazioni MWeb",
|
||||
"litecoin_mweb_warning": "L'uso di MWeb inizialmente scaricherà ~ 600 MB di dati e potrebbe richiedere fino a 30 minuti a seconda della velocità di rete. Questi dati iniziali scaricheranno solo una volta e saranno disponibili per tutti i portafogli Litecoin",
|
||||
"litecoin_what_is_mweb": "Cos'è MWeb?",
|
||||
"load_more": "Carica di più",
|
||||
"loading_your_wallet": "Caricamento portafoglio",
|
||||
"login": "Accedi",
|
||||
|
|
|
@ -360,6 +360,14 @@
|
|||
"lightning_receive_limits": "このアドレスに ${min} 個以上、最大 ${max} 個のSat を送信します。 この請求書の受け取り時に、${feePercent}% のセットアップ料金 (最低 ${fee} サット) が適用されます。 これにより、受け取ったビットコインがライトニングに変換されます。 オンチェーン料金が適用されます。 このアドレスは 1 回のみ使用できます。",
|
||||
"lightning_received_sats": "${num} satを受信しました",
|
||||
"lightning_swap_out_error": "逆スワップmax <min、リバーススワップ(スワップアウト)は不可能です",
|
||||
"litecoin_enable_mweb_sync": "MWEBスキャンを有効にします",
|
||||
"litecoin_mweb": "mweb",
|
||||
"litecoin_mweb_always_scan": "MWEBを常にスキャンします",
|
||||
"litecoin_mweb_display_card": "MWEBカードを表示します",
|
||||
"litecoin_mweb_scanning": "MWEBスキャン",
|
||||
"litecoin_mweb_settings": "MWEB設定",
|
||||
"litecoin_mweb_warning": "MWEBを使用すると、最初は〜600MBのデータをダウンロードし、ネットワーク速度に応じて最大30分かかる場合があります。この最初のデータは一度だけダウンロードされ、すべてのLitecoinウォレットで利用可能になります",
|
||||
"litecoin_what_is_mweb": "MWEBとは何ですか?",
|
||||
"load_more": "もっと読み込む",
|
||||
"loading_your_wallet": "ウォレットをロードしています",
|
||||
"login": "ログイン",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "${min} sats 이상을 보내고이 주소로 ${max} SATS를 보내십시오. 이 송장을 받으면 최소 ${fee} SAT의 ${feePercent}%의 설정 수수료가 적용됩니다. 이것은 수신 된 비트 코인을 번개로 변환합니다. 온쇄 수수료가 적용됩니다. 이 주소는 한 번만 사용할 수 있습니다.",
|
||||
"lightning_received_sats": "${num} sat을 받았습니다",
|
||||
"lightning_swap_out_error": "리버스 스왑 max <min, 리버스 스왑 (스왑 아웃)이 불가능합니다.",
|
||||
"litecoin_enable_mweb_sync": "mweb 스캔을 활성화합니다",
|
||||
"litecoin_mweb": "mweb",
|
||||
"litecoin_mweb_always_scan": "mweb는 항상 스캔을 설정합니다",
|
||||
"litecoin_mweb_display_card": "mweb 카드를 보여주십시오",
|
||||
"litecoin_mweb_scanning": "mweb 스캔",
|
||||
"litecoin_mweb_settings": "mweb 설정",
|
||||
"litecoin_mweb_warning": "MWEB를 사용하면 처음에는 ~ 600MB의 데이터를 다운로드하며 네트워크 속도에 따라 최대 30 분이 소요될 수 있습니다. 이 초기 데이터는 한 번만 다운로드하여 모든 조명 지갑에 사용할 수 있습니다.",
|
||||
"litecoin_what_is_mweb": "MWEB 란 무엇입니까?",
|
||||
"load_more": "더로드하십시오",
|
||||
"loading_your_wallet": "지갑 넣기",
|
||||
"login": "로그인",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "${min} sats နှင့် ${max} sats အထိ ဤလိပ်စာသို့ ပို့ပါ။ ဤငွေတောင်းခံလွှာကို လက်ခံရရှိသည့်အခါ အနည်းဆုံး ${fee} ကြိမ်ဖြင့် ${feePercent}% ဖြင့် တပ်ဆင်ခကို ကောက်ခံပါမည်။ ၎င်းသည် လက်ခံရရှိထားသော Bitcoin ကို Lightning အဖြစ်သို့ ပြောင်းလဲပေးမည်ဖြစ်သည်။ ကွင်းဆက်ကြေးကောက်ခံပါမည်။ ဤလိပ်စာကို တစ်ကြိမ်သာ အသုံးပြုနိုင်သည်။",
|
||||
"lightning_received_sats": "${num} sats ကိုလက်ခံရရှိခဲ့သည်",
|
||||
"lightning_swap_out_error": "ပြောင်းပြန် Swap Max <Min, ပြောင်းပြန် swap (swap out) ကိုပြောင်းပြန်",
|
||||
"litecoin_enable_mweb_sync": "mweb scanning ဖွင့်ပါ",
|
||||
"litecoin_mweb": "မင်္ဂလာပါ",
|
||||
"litecoin_mweb_always_scan": "Mweb အမြဲစကင်ဖတ်စစ်ဆေးပါ",
|
||||
"litecoin_mweb_display_card": "MweB ကဒ်ကိုပြပါ",
|
||||
"litecoin_mweb_scanning": "mweb scanning",
|
||||
"litecoin_mweb_settings": "Mweb ဆက်တင်များ",
|
||||
"litecoin_mweb_warning": "MweB ကိုအသုံးပြုခြင်းသည်အစပိုင်းတွင် ~ 600MB ဒေတာများကို download လုပ်ပြီးကွန်ယက်အမြန်နှုန်းပေါ် မူတည်. မိနစ် 30 အထိကြာနိုင်သည်။ ဤကန ဦး ဒေတာကိုတစ်ကြိမ်သာ download လုပ်ပြီး litecoin Walkets အားလုံးအတွက်ရနိုင်သည်",
|
||||
"litecoin_what_is_mweb": "MweB ဆိုတာဘာလဲ။",
|
||||
"load_more": "ပိုပြီး load",
|
||||
"loading_your_wallet": "သင့်ပိုက်ဆံအိတ်ကို ဖွင့်နေသည်။",
|
||||
"login": "လော့ဂ်အင်",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Stuur meer dan ${min} sats en tot ${max} sats naar dit adres. Een installatiekosten van ${feePercent}% met een minimum van ${fee} SAT's wordt toegepast bij het ontvangen van deze factuur. Dit zal elke ontvangen bitcoin omzetten in bliksem. Een on-chain-vergoeding wordt toegepast. Dit adres kan slechts eenmaal worden gebruikt.",
|
||||
"lightning_received_sats": "Ontving ${num} sats",
|
||||
"lightning_swap_out_error": "Reverse Swap Max <min, een omgekeerde swap (ruilen) is niet mogelijk",
|
||||
"litecoin_enable_mweb_sync": "MWEB -scanning inschakelen",
|
||||
"litecoin_mweb": "Mweb",
|
||||
"litecoin_mweb_always_scan": "Stel mweb altijd op scannen",
|
||||
"litecoin_mweb_display_card": "Toon MWEB -kaart",
|
||||
"litecoin_mweb_scanning": "MWEB -scanning",
|
||||
"litecoin_mweb_settings": "MWEB -instellingen",
|
||||
"litecoin_mweb_warning": "Het gebruik van MWeb downloadt in eerste instantie ~ 600 MB aan gegevens en kan tot 30 minuten duren, afhankelijk van de netwerksnelheid. Deze eerste gegevens worden slechts eenmaal gedownload en zijn beschikbaar voor alle Litecoin -portefeuilles",
|
||||
"litecoin_what_is_mweb": "Wat is Mweb?",
|
||||
"load_more": "Meer laden",
|
||||
"loading_your_wallet": "Uw portemonnee laden",
|
||||
"login": "Log in",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Wyślij więcej niż ${min} SAT i do ${max} SATS na ten adres. Opłata konfiguracyjna w wysokości ${feePercent}% przy minimum ${fee} SAT zostanie zastosowana po otrzymaniu tej faktury. To przekonwertuje każdy otrzymany bitcoin w błyskawicę. Zostanie zastosowana opłata w łańcuchu. Ten adres można użyć tylko raz.",
|
||||
"lightning_received_sats": "Otrzymałem ${num} sats",
|
||||
"lightning_swap_out_error": "Odwrotna wymiana maks. <Min, odwrotna wymiana (wymiana) nie jest możliwa",
|
||||
"litecoin_enable_mweb_sync": "Włącz skanowanie MWEB",
|
||||
"litecoin_mweb": "MWEB",
|
||||
"litecoin_mweb_always_scan": "Ustaw MWEB zawsze skanowanie",
|
||||
"litecoin_mweb_display_card": "Pokaż kartę MWEB",
|
||||
"litecoin_mweb_scanning": "Skanowanie MWEB",
|
||||
"litecoin_mweb_settings": "Ustawienia MWEB",
|
||||
"litecoin_mweb_warning": "Korzystanie z MWEB początkowo pobiera ~ 600 MB danych i może potrwać do 30 minut w zależności od prędkości sieci. Te początkowe dane pobierają tylko raz i będą dostępne dla wszystkich portfeli Litecoin",
|
||||
"litecoin_what_is_mweb": "Co to jest MWEB?",
|
||||
"load_more": "Załaduj więcej",
|
||||
"loading_your_wallet": "Ładowanie portfela",
|
||||
"login": "Login",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Envie mais do que ${min} SATs e até ${max} SATS para este endereço. Uma taxa de configuração ${feePercent}% com um mínimo de ${fee} SATS será aplicada ao receber esta fatura. Isso converterá qualquer bitcoin recebido em raios. Uma taxa na cadeia será aplicada. Este endereço só pode ser usado uma vez.",
|
||||
"lightning_received_sats": "Recebeu ${num} SATS",
|
||||
"lightning_swap_out_error": "Troca reversa max <min, uma troca reversa (troca) não é possível",
|
||||
"litecoin_enable_mweb_sync": "Ativar digitalização do MWEB",
|
||||
"litecoin_mweb": "Mweb",
|
||||
"litecoin_mweb_always_scan": "Definir mweb sempre digitalizando",
|
||||
"litecoin_mweb_display_card": "Mostre o cartão MWEB",
|
||||
"litecoin_mweb_scanning": "MWEB Scanning",
|
||||
"litecoin_mweb_settings": "Configurações do MWEB",
|
||||
"litecoin_mweb_warning": "O uso do MWEB baixará inicialmente ~ 600 MB de dados e pode levar até 30 minutos, dependendo da velocidade da rede. Esses dados iniciais serão baixados apenas uma vez e estarão disponíveis para todas as carteiras Litecoin",
|
||||
"litecoin_what_is_mweb": "O que é MWeb?",
|
||||
"load_more": "Carregue mais",
|
||||
"loading_your_wallet": "Abrindo sua carteira",
|
||||
"login": "Login",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Отправьте больше, чем ${min} SAT и до ${max} SAT на этот адрес. Плата за настройку ${feePercent}% с минимумом ${fee} SAT будет применяться при получении этого счета. Это преобразует любой полученный биткойн в молнию. Будет применяться плата за цепь. Этот адрес можно использовать только один раз.",
|
||||
"lightning_received_sats": "Получил ${num} sats",
|
||||
"lightning_swap_out_error": "Обратный обмен макс <мин, обратный обмен (обмен) невозможно.",
|
||||
"litecoin_enable_mweb_sync": "Включить MWEB сканирование",
|
||||
"litecoin_mweb": "Мвеб",
|
||||
"litecoin_mweb_always_scan": "Установить MWEB всегда сканирование",
|
||||
"litecoin_mweb_display_card": "Показать карту MWEB",
|
||||
"litecoin_mweb_scanning": "MWEB сканирование",
|
||||
"litecoin_mweb_settings": "Настройки MWEB",
|
||||
"litecoin_mweb_warning": "Использование MWEB изначально загрузит ~ 600 МБ данных и может занять до 30 минут в зависимости от скорости сети. Эти начальные данные будут загружаться только один раз и будут доступны для всех кошельков Litecoin",
|
||||
"litecoin_what_is_mweb": "Что такое MWEB?",
|
||||
"load_more": "Загрузи больше",
|
||||
"loading_your_wallet": "Загрузка кошелька",
|
||||
"login": "Логин",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "ส่งมากกว่า ${min} sats และสูงสุด ${max} sats ไปยังที่อยู่นี้ ค่าธรรมเนียมการตั้งค่า ${feePercent}% ที่มีค่าน้อยที่สุด ${fee} SAT จะถูกนำไปใช้เมื่อได้รับใบแจ้งหนี้นี้ สิ่งนี้จะแปลง bitcoin ที่ได้รับเป็นฟ้าผ่า จะมีการใช้ค่าธรรมเนียมในห่วงโซ่ ที่อยู่นี้สามารถใช้ได้เพียงครั้งเดียว",
|
||||
"lightning_received_sats": "ได้รับ ${num} sats",
|
||||
"lightning_swap_out_error": "ย้อนกลับ Swap Max <min, การแลกเปลี่ยนย้อนกลับ (swap out) เป็นไปไม่ได้",
|
||||
"litecoin_enable_mweb_sync": "เปิดใช้งานการสแกน MWEB",
|
||||
"litecoin_mweb": "mweb",
|
||||
"litecoin_mweb_always_scan": "ตั้งค่าการสแกน MWEB เสมอ",
|
||||
"litecoin_mweb_display_card": "แสดงการ์ด mweb",
|
||||
"litecoin_mweb_scanning": "การสแกน MWEB",
|
||||
"litecoin_mweb_settings": "การตั้งค่า MWEB",
|
||||
"litecoin_mweb_warning": "การใช้ MWEB จะดาวน์โหลดข้อมูล ~ 600MB ในขั้นต้นและอาจใช้เวลาสูงสุด 30 นาทีขึ้นอยู่กับความเร็วเครือข่าย ข้อมูลเริ่มต้นนี้จะดาวน์โหลดได้เพียงครั้งเดียวและพร้อมใช้งานสำหรับกระเป๋าเงินทั้งหมดของ Litecoin",
|
||||
"litecoin_what_is_mweb": "MWEB คืออะไร?",
|
||||
"load_more": "โหลดมากขึ้น",
|
||||
"loading_your_wallet": "กำลังโหลดกระเป๋าของคุณ",
|
||||
"login": "เข้าสู่ระบบ",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Magpadala ng higit sa ${min} sats at hanggang sa ${max} sats sa address na ito. Ang isang setup fee ng ${feePercent}% na may isang minimum na ${fee} sats ay ilalapat sa pagtanggap ng invoice na ito. Ito ay i -convert ang anumang natanggap na Bitcoin sa kidlat. Ang isang on-chain fee ay ilalapat. Ang address na ito ay maaari lamang magamit nang isang beses.",
|
||||
"lightning_received_sats": "Nakatanggap ng ${num} sats",
|
||||
"lightning_swap_out_error": "Ang reverse swap max <min, isang reverse swap (swap out) ay hindi posible",
|
||||
"litecoin_enable_mweb_sync": "Paganahin ang pag -scan ng MWeb",
|
||||
"litecoin_mweb": "Mweb",
|
||||
"litecoin_mweb_always_scan": "Itakda ang MWeb na laging nag -scan",
|
||||
"litecoin_mweb_display_card": "Ipakita ang MWEB Card",
|
||||
"litecoin_mweb_scanning": "Pag -scan ng Mweb",
|
||||
"litecoin_mweb_settings": "Mga Setting ng Mweb",
|
||||
"litecoin_mweb_warning": "Ang paggamit ng MWEB ay unang i -download ang ~ 600MB ng data, at maaaring tumagal ng hanggang sa 30 minuto depende sa bilis ng network. Ang paunang data na ito ay mag -download lamang ng isang beses at magagamit para sa lahat ng mga wallets ng Litecoin",
|
||||
"litecoin_what_is_mweb": "Ano ang MWEB?",
|
||||
"load_more": "Mag -load pa",
|
||||
"loading_your_wallet": "Naglo -load ng iyong pitaka",
|
||||
"login": "Mag log in",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Bu adrese ${min} SATS'den fazla ve ${max} SAT'ları gönderin. Bu faturayı aldıktan sonra en az ${fee} SAT ile% ${feePercent} kurulum ücreti uygulanacaktır. Bu, alınan bitcoin'i yıldırım haline getirecektir. Zincir üstü bir ücret uygulanacaktır. Bu adres yalnızca bir kez kullanılabilir.",
|
||||
"lightning_received_sats": "${num} sats aldı",
|
||||
"lightning_swap_out_error": "Ters takas maksimum <min, ters takas (takas) mümkün değil",
|
||||
"litecoin_enable_mweb_sync": "MWEB taramasını etkinleştir",
|
||||
"litecoin_mweb": "Mweb",
|
||||
"litecoin_mweb_always_scan": "MWEB'i her zaman taramayı ayarlayın",
|
||||
"litecoin_mweb_display_card": "MWEB kartını göster",
|
||||
"litecoin_mweb_scanning": "MWEB taraması",
|
||||
"litecoin_mweb_settings": "MWEB ayarları",
|
||||
"litecoin_mweb_warning": "MWEB kullanmak başlangıçta ~ 600MB veri indirir ve ağ hızına bağlı olarak 30 dakikaya kadar sürebilir. Bu ilk veriler yalnızca bir kez indirilecek ve tüm Litecoin cüzdanları için kullanılabilir olacak",
|
||||
"litecoin_what_is_mweb": "MWEB nedir?",
|
||||
"load_more": "Daha fazla yükle",
|
||||
"loading_your_wallet": "Cüzdanın yükleniyor",
|
||||
"login": "Login",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "Надішліть більше ${min} SAT та до ${max} SATS на цю адресу. Плата за налаштування в розмірі ${feePercent}% з мінімум ${fee} САТ буде застосовуватися після отримання цього рахунку -фактури. Це перетворить будь -який отриманий біткойн у блискавку. Буде застосовуватися плата за ланцюг. Цю адресу можна використовувати лише один раз.",
|
||||
"lightning_received_sats": "Отримали ${num} sats",
|
||||
"lightning_swap_out_error": "Зворотний обмін максимум <хв, зворотна своп (своп) неможливий",
|
||||
"litecoin_enable_mweb_sync": "Увімкнути сканування MWEB",
|
||||
"litecoin_mweb": "Мвеб",
|
||||
"litecoin_mweb_always_scan": "Встановити mweb завжди сканувати",
|
||||
"litecoin_mweb_display_card": "Показати карту MWeb",
|
||||
"litecoin_mweb_scanning": "Сканування Mweb",
|
||||
"litecoin_mweb_settings": "Налаштування MWEB",
|
||||
"litecoin_mweb_warning": "Використання MWEB спочатку завантажить ~ 600 Мб даних і може зайняти до 30 хвилин залежно від швидкості мережі. Ці початкові дані завантажуються лише один раз і будуть доступні для всіх гаманців Litecoin",
|
||||
"litecoin_what_is_mweb": "Що таке mweb?",
|
||||
"load_more": "Завантажити ще",
|
||||
"loading_your_wallet": "Завантаження гаманця",
|
||||
"login": "Логін",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "اس پتے پر ${min} SATS اور ${max} SATS سے زیادہ بھیجیں۔ اس انوائس کو حاصل کرنے پر کم از کم ${fee} ایس اے ٹی کے ساتھ ${feePercent} ٪ کی سیٹ اپ فیس کا اطلاق ہوگا۔ یہ کسی بھی موصولہ بٹ کوائن کو بجلی میں تبدیل کردے گا۔ آن چین فیس کا اطلاق ہوگا۔ یہ پتہ صرف ایک بار استعمال کیا جاسکتا ہے۔",
|
||||
"lightning_received_sats": "موصول ${num} sats",
|
||||
"lightning_swap_out_error": "ریورس سویپ میکس <منٹ ، ایک ریورس سویپ (تبادلہ) ممکن نہیں ہے",
|
||||
"litecoin_enable_mweb_sync": "MWEB اسکیننگ کو فعال کریں",
|
||||
"litecoin_mweb": "MWEB",
|
||||
"litecoin_mweb_always_scan": "MWEB ہمیشہ اسکیننگ سیٹ کریں",
|
||||
"litecoin_mweb_display_card": "MWEB کارڈ دکھائیں",
|
||||
"litecoin_mweb_scanning": "MWEB اسکیننگ",
|
||||
"litecoin_mweb_settings": "MWEB کی ترتیبات",
|
||||
"litecoin_mweb_warning": "MWEB کا استعمال ابتدائی طور پر m 600mb ڈیٹا ڈاؤن لوڈ کرے گا ، اور نیٹ ورک کی رفتار کے لحاظ سے 30 منٹ تک کا وقت لگ سکتا ہے۔ یہ ابتدائی اعداد و شمار صرف ایک بار ڈاؤن لوڈ کریں گے اور تمام لیٹیکوئن بٹوے کے لئے دستیاب ہوں گے",
|
||||
"litecoin_what_is_mweb": "MWEB کیا ہے؟",
|
||||
"load_more": "مزید لوڈ کریں",
|
||||
"loading_your_wallet": "آپ کا بٹوہ لوڈ ہو رہا ہے۔",
|
||||
"login": "لاگ ان کریں",
|
||||
|
|
|
@ -360,6 +360,14 @@
|
|||
"lightning_receive_limits": "Firanṣẹ diẹ sii ju ${min} osù ati si to ${max} O si só si adirẹsi yii. Owo oso kan ti ${feePercent}% pẹlu o kere ju ${fee} Sahs yoo wa ni lilo lori gbigba ikowe yii. Eyi yoo ṣe iyipada eyikeyi ti o gba bitcoin sinu monomono. Owo lori-pq yoo lo. Adirẹsi yii le ṣee lo lẹẹkan.",
|
||||
"lightning_received_sats": "Gba ${num} Satani",
|
||||
"lightning_swap_out_error": "Yiyipada spap max <min, snap yiyipada (swap jade) ko ṣeeṣe",
|
||||
"litecoin_enable_mweb_sync": "Mu mweb ọlọjẹ",
|
||||
"litecoin_mweb": "Mweb",
|
||||
"litecoin_mweb_always_scan": "Ṣeto mweb nigbagbogbo n ṣayẹwo",
|
||||
"litecoin_mweb_display_card": "Fihan kaadi Mweb",
|
||||
"litecoin_mweb_scanning": "Mweb scanning",
|
||||
"litecoin_mweb_settings": "Awọn eto Mweb",
|
||||
"litecoin_mweb_warning": "Lilo Mweb yoo wa lakoko igbasilẹ ~ 600MB ti data, o le gba to iṣẹju 30 da lori iyara nẹtiwọọki. Awọn data akọkọ yii yoo ṣe igbasilẹ lẹẹkan si ki o wa fun gbogbo awọn Wolinkun LiveCooin",
|
||||
"litecoin_what_is_mweb": "Kini mweb?",
|
||||
"load_more": "Ẹru diẹ sii",
|
||||
"loading_your_wallet": "A ń ṣí àpamọ́wọ́ yín",
|
||||
"login": "Orúkọ",
|
||||
|
|
|
@ -359,6 +359,14 @@
|
|||
"lightning_receive_limits": "将超过 ${min} 个 sat 且最多 ${max} 个 sat 发送到此地址。 收到此发票后,将收取 ${feePercent}% 的安装费,至少 ${fee} sat。 这会将任何收到的比特币转换为闪电网络。 将收取链上费用。 该地址只能使用一次。",
|
||||
"lightning_received_sats": "收到 ${num} SATS",
|
||||
"lightning_swap_out_error": "反向交换最大<min,不可能进行反向交换(交换)",
|
||||
"litecoin_enable_mweb_sync": "启用MWEB扫描",
|
||||
"litecoin_mweb": "MWEB",
|
||||
"litecoin_mweb_always_scan": "设置MWEB总是扫描",
|
||||
"litecoin_mweb_display_card": "显示MWEB卡",
|
||||
"litecoin_mweb_scanning": "MWEB扫描",
|
||||
"litecoin_mweb_settings": "MWEB设置",
|
||||
"litecoin_mweb_warning": "使用MWEB最初将下载约600MB的数据,并且最多可能需要30分钟的时间,具体取决于网络速度。此初始数据只能下载一次,并适用于所有莱特币钱包",
|
||||
"litecoin_what_is_mweb": "什么是MWEB?",
|
||||
"load_more": "装载更多",
|
||||
"loading_your_wallet": "加载您的钱包",
|
||||
"login": "登录",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue