diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 841ea570d..7c8329328 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -163,6 +163,16 @@ jobs: run: | echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties + - name: build mweb + run: | + cd /opt/android/cake_wallet + git clone https://github.com/ltcmweb/mwebd + cd /opt/android/cake_wallet/mwebd + go install github.com/ltcmweb/mwebd/cmd/mwebd@latest + gomobile bind -target=android -androidapi 21 github.com/ltcmweb/mwebd + mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/ + mv ./mwebd.aar $_ + - name: Build run: | cd /opt/android/cake_wallet diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index fd19edfc7..f9079b749 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -712,26 +712,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( - utxos: utxoDetails.utxos, - outputs: outputs, - network: network, - memo: memo, - inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos, - vinOutpoints: utxoDetails.vinOutpoints, - ); - } - - int fee = feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize); + int fee = await calcFee( + utxos: utxoDetails.utxos, + outputs: outputs, + network: network, + memo: memo, + feeRate: feeRate, + ); if (fee == 0) { throw BitcoinTransactionNoFeeException(); @@ -741,6 +728,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] = @@ -1786,18 +1775,6 @@ abstract class ElectrumWalletBase 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; diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 5a58196e1..6a78af0f2 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -184,7 +184,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final confirmations = mwebUtxosHeight - transaction.height + 1; if (transaction.confirmations == confirmations) continue; transaction.confirmations = confirmations; - print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@4"); transactionHistory.addOne(transaction); } await transactionHistory.save(); @@ -200,6 +199,22 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { mwebUtxosBox = await CakeHive.openBox(boxName); } + @action + @override + Future rescan({ + required int height, + int? chainTip, + ScanData? scanData, + bool? doSingleScan, + bool? usingElectrs, + }) async { + await mwebUtxosBox.clear(); + mwebUtxosHeight = height; + walletInfo.restoreHeight = height; + await walletInfo.save(); + processMwebUtxos(); + } + @override Future init() async { await initMwebUtxosBox(); @@ -208,9 +223,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future processMwebUtxos() async { final stub = await CwMweb.stub(); final scanSecret = mwebHd.derive(0x80000000).privKey!; - print("SCANNING FROM HEIGHT: ${walletInfo.restoreHeight}"); - final req = - UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: walletInfo.restoreHeight); + int restoreHeight = walletInfo.restoreHeight; + print("SCANNING FROM HEIGHT: $restoreHeight"); + final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: restoreHeight); bool initDone = false; for (final utxo in mwebUtxosBox.values) { @@ -257,7 +272,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (isNew) { final addressRecord = walletAddresses.allAddresses - .firstWhere((addressRecord) => addressRecord.address == utxo.address); + .firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address); + if (addressRecord == null) { + print("addressRecord is null! TODO: handle this case 1"); + continue; + } if (!(tx.inputAddresses?.contains(utxo.address) ?? false)) addressRecord.txCount++; addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); @@ -401,7 +420,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // print(a.address); // } if (addressRecord == null) { - print("addressRecord is null! TODO: handle this case"); + print("addressRecord is null! TODO: handle this case2"); return; } final unspent = BitcoinUnspent( @@ -423,7 +442,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unconfirmed += utxo.value.toInt(); } }); - print("confirmed: $confirmed, unconfirmed: $unconfirmed"); + // print("confirmed: $confirmed, unconfirmed: $unconfirmed"); return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen); } @@ -513,6 +532,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { Future createTransaction(Object credentials) async { try { final tx = await super.createTransaction(credentials) as PendingBitcoinTransaction; + final stub = await CwMweb.stub(); final resp = await stub.create(CreateRequest( rawTx: hex.decode(tx.hex), diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index d405ad58f..a1089f473 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -68,6 +68,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with @action @override Future getChangeAddress() async { + // super.getChangeAddress(); updateChangeAddresses(); // this means all change addresses used will be mweb addresses!: await topUpMweb(0); diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index dfc032c24..4217d35b4 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -32,8 +32,8 @@ 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; String? idOverride; String? hexOverride; @@ -91,19 +91,24 @@ class PendingBitcoinTransaction with PendingTransaction { } } - @override - Future commit() async { - if (network is LitecoinNetwork) try { + Future _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 commit() async { + if (network is LitecoinNetwork) { + await _ltcCommit(); } else { await _commit(); } - _listeners.forEach((listener) => listener(transactionInfo())); } diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index c59ae4ad0..9f32c9067 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -30,6 +30,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, diff --git a/lib/src/widgets/blockchain_height_widget.dart b/lib/src/widgets/blockchain_height_widget.dart index d85680cc8..1bfc7c0cd 100644 --- a/lib/src/widgets/blockchain_height_widget.dart +++ b/lib/src/widgets/blockchain_height_widget.dart @@ -16,6 +16,7 @@ class BlockchainHeightWidget extends StatefulWidget { this.onHeightOrDateEntered, this.hasDatePicker = true, this.isSilentPaymentsScan = false, + this.isMwebScan = false, this.toggleSingleScan, this.doSingleScan = false, }) : super(key: key); @@ -25,6 +26,7 @@ class BlockchainHeightWidget extends StatefulWidget { final FocusNode? focusNode; final bool hasDatePicker; final bool isSilentPaymentsScan; + final bool isMwebScan; final bool doSingleScan; final Function()? toggleSingleScan; @@ -157,7 +159,10 @@ class BlockchainHeightState extends State { 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 { height = monero!.getHeightByDate(date: date); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index b59dd1592..fd4f1ede2 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -303,6 +303,7 @@ abstract class DashboardViewModelBase with Store { bool get hasRescan => wallet.type == WalletType.bitcoin || wallet.type == WalletType.monero || + wallet.type == WalletType.litecoin || wallet.type == WalletType.haven; @computed diff --git a/lib/view_model/rescan_view_model.dart b/lib/view_model/rescan_view_model.dart index dcc81c0a0..3d8cf39f3 100644 --- a/lib/view_model/rescan_view_model.dart +++ b/lib/view_model/rescan_view_model.dart @@ -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 rescanCurrentWallet({required int restoreHeight}) async { state = RescanWalletState.rescaning;